summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: c81789f)
raw | patch | inline | side by side (parent: c81789f)
author | oetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa> | |
Wed, 23 May 2007 16:08:14 +0000 (16:08 +0000) | ||
committer | oetiker <oetiker@a5681a0c-68f1-0310-ab6d-d61299d08faa> | |
Wed, 23 May 2007 16:08:14 +0000 (16:08 +0000) |
203 files changed:
diff --git a/program/00README b/program/00README
--- /dev/null
+++ b/program/00README
@@ -0,0 +1,6 @@
+Title: RRDtool
+Date: 2005-04-04
+Owner: Tobias Oetiker <tobi@oetiker.ch>
+Group: Software
+
+Round Robin Database Tool
diff --git a/program/CONTRIBUTORS b/program/CONTRIBUTORS
--- /dev/null
+++ b/program/CONTRIBUTORS
@@ -0,0 +1,74 @@
+I would like to thank to following people for helping to
+bring RRDtool into existence.
+
+Alan Lichty <alan_lichty with eli.net>
+Alan Milligan <alan.milligan@last-bastion.net> Python bindings
+Alex van den Bogaerdt <alex with ergens.op.het.net> (rrd_resize.c and more)
+Amos Shapira <amos with gezernet.co.il>
+Andreas Kroomaa <andre with ml.ee>
+Andrew Turner <turner with mint.net> (LAST consolidator)
+Bernard Fischer <bfischer with syslog.ch> 64bit stuff and --alt-autoscale-max
+Bill Fenner <fenner with research.att.com>
+Blair Zajac <bzajac with geostaff.com>
+Bruce Campbell <bruce.campbell with apnic.net>
+Chin-A-Young <china with thewrittenword.com>
+Christophe VG <Christophe.VanGinneken with ubizen.com>
+Christophe Van Ginneken <Christophe.VanGinneken with ubizen.com> (--no-legend)
+Dan Dunn <dandunn with computer.org>
+Dave Bodenstab <dave@bodenstab.org> AT style time in update, tclfixes
+David Grimes <dgrimes with navisite.com> SQRT/SORT/REV/SHIFT/TREND
+David L. Barker <dave with ncomtech.com> xport function bug fixes
+Frank Strauss <strauss with escape.de> TCL bindings
+Henrik Storner <henrik with hswn.dk> functions for min/max values of data in graph
+Hermann Hueni <hueni with glue.ch> (SunOS porting)
+Jakob Ilves <jilves with se.oracle.com> HPUX 11
+Jeff R. Allen <jeff.allen with acm.org> (autoconfigure, portability)
+Jeremy Fischer <jeremy with pobox.com> (Makefile changes & RPM builds)
+Jesús Couto Fandiño
+Joel Becker <jlbec with raleigh.ibm.com> AIX
+Joey Miller <joeym with inficad.com>php3 and php4 bindings
+Jost.Krieger <Jost.Krieger with ruhr-uni-bochum.de>
+Kai Siering <kai.siering with mediaways.net>
+Larry Leszczynski <larryl with furph.com>
+McCreary mccreary with xoanon.colorado.edu
+Mike Mitchell <mcm with unx.sas.com>
+Mike Slifcak <slif with bellsouth.net> many rrdtool-1.1.x fixes
+Oleg Cherevko <olwi with icyb.kiev.ua>
+Otmar Lendl <O.Lendl with Austria.EU.net> (lots of bugfixes)
+Paul Joslin <Paul.Joslin with sdrc.com>
+Peter Speck <speck with vitality.dk> eps/svg/pdf file format code in rrdtool-1.x
+Peter Stamfest <peter with stamfest.at> initial multi-thread support
+Peter Breitenlohner <peb with mppmu.mpg.de> many patches for rrdtool 1.2.x
+Philippe.Simonet <Philippe.Simonet with swisscom.com> (NT porting)
+Poul-Henning Kamp <phk with freebsd.org> CDEF enhancements
+REIBENSCHUH Alfred <alfred.reibenschuh with it-austria.com> AIX
+Radoslaw Karas <rkaras with tyndall.ie>
+Rainer Bawidamann <Rainer.Bawidamann with informatik.uni-ulm.de>
+Roman Hoogant <rhoogant with ee.ethz.ch>
+Ronan Mullally <ronan in 4L.ie>
+Roger J. Meier <roger.meier in terreactive.ch> (arbitrary linelength in rrdtool)
+Russ Wright <rwwright with home.com>
+Sean Summers <sean with Fenstermaker.com> (RPM .spec)
+Selena M Brewington <smbrewin with ichips.intel.com> add_ds
+Shane O'Donnell <shaneo with opennms.org>
+Simon Leinen <simon with switch.ch>
+Steen Linden <Steen.Linden with ebone.net>
+Stefan Mueller <s.mueller with computer.org> HPUX 11
+Steve Harris <steveh with wesley.com.au> AIX portability
+Steve Rader <rader with teak.wiscnet.net> (rrd_cgi debugging and LAST)
+Terminator rAT <karl_schilke with eli.net>
+Tobias Weingartner <weingart with cs.ualberta.ca>
+Tom Crawley <Tom.Crawley with hi.riotinto.com.au> (GCC&HP configuration)
+Travis Brown <tebrown with csh.rit.edu>
+Tuc <ttsg with ttsg.com>
+Ulf Lilleengen <lulf with pvv.ntnu.no> Python binding for 'rrdtool first'
+Ulrich Schilling <schilling with netz.uni-essen.de> AIX
+Wim Heirman <wim.heirman elis.ugent.be> --units=si option
+Wolfgang Schrimm <wschrimm with uni-hd.de> xport function
+Wrolf Courtney <wrolf with wrolf.net> (HP-UX)
+hendrik visage <hvisage with is.co.za>
+Philippe Simonet <philippe.simonet with swisscom.ch> (Windows Binaries)
+Alexander Lucke (lucke with dns-net.de)
+ of DNS:NET Internet Services (www.dns-net.de) http://rrdtool.org
+Hedley Simons <heds@metahusky.net>
+Nicola Worthington <nicolaw@cpan.org>
diff --git a/program/COPYING b/program/COPYING
--- /dev/null
+++ b/program/COPYING
@@ -0,0 +1,280 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for non-commercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
diff --git a/program/COPYRIGHT b/program/COPYRIGHT
--- /dev/null
+++ b/program/COPYRIGHT
@@ -0,0 +1,90 @@
+ RRDTOOL - Round Robin Database Tool
+ A tool for fast logging of numerical data graphical display
+ of this data.
+
+ Copyright (c) 1998-2006 Tobias Oetiker
+ All rights reserved.
+
+ GNU GPL License
+ ===============
+
+ 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
+
+ FLOSS License Exception
+ =======================
+ (Adapted from http://www.mysql.com/company/legal/licensing/foss-exception.html)
+
+ I want specified Free/Libre and Open Source Software ("FLOSS")
+ applications to be able to use specified GPL-licensed RRDtool
+ libraries (the "Program") despite the fact that not all FLOSS licenses are
+ compatible with version 2 of the GNU General Public License (the "GPL").
+
+ As a special exception to the terms and conditions of version 2.0 of the GPL:
+
+ You are free to distribute a Derivative Work that is formed entirely from
+ the Program and one or more works (each, a "FLOSS Work") licensed under one
+ or more of the licenses listed below, as long as:
+
+ 1. You obey the GPL in all respects for the Program and the Derivative
+ Work, except for identifiable sections of the Derivative Work which are
+ not derived from the Program, and which can reasonably be considered
+ independent and separate works in themselves,
+
+ 2. all identifiable sections of the Derivative Work which are not derived
+ from the Program, and which can reasonably be considered independent and
+ separate works in themselves,
+
+ 1. are distributed subject to one of the FLOSS licenses listed
+ below, and
+
+ 2. the object code or executable form of those sections are
+ accompanied by the complete corresponding machine-readable source
+ code for those sections on the same medium and under the same FLOSS
+ license as the corresponding object code or executable forms of
+ those sections, and
+
+ 3. any works which are aggregated with the Program or with a Derivative
+ Work on a volume of a storage or distribution medium in accordance with
+ the GPL, can reasonably be considered independent and separate works in
+ themselves which are not derivatives of either the Program, a Derivative
+ Work or a FLOSS Work.
+
+ If the above conditions are not met, then the Program may only be copied,
+ modified, distributed or used under the terms and conditions of the GPL.
+
+ FLOSS License List
+ ==================
+ License name Version(s)/Copyright Date
+ Academic Free License 2.0
+ Apache Software License 1.0/1.1/2.0
+ Apple Public Source License 2.0
+ Artistic license From Perl 5.8.0
+ BSD license "July 22 1999"
+ Common Public License 1.0
+ GNU Library or "Lesser" General Public License (LGPL) 2.0/2.1
+ IBM Public License, Version 1.0
+ Jabber Open Source License 1.0
+ MIT License (As listed in file MIT-License.txt) -
+ Mozilla Public License (MPL) 1.0/1.1
+ Open Software License 2.0
+ OpenSSL license (with original SSLeay license) "2003" ("1998")
+ PHP License 3.0
+ Python license (CNRI Python License) -
+ Python Software Foundation License 2.1.1
+ Sleepycat License "1999"
+ W3C License "2001"
+ X11 License "2001"
+ Zlib/libpng License -
+ Zope Public License 2.0
diff --git a/program/MakeMakefile b/program/MakeMakefile
--- /dev/null
+++ b/program/MakeMakefile
@@ -0,0 +1,70 @@
+#!/bin/sh
+#
+# Run this script after the first cvs checkout to build
+# makefiles and friends
+
+PATH="/usr/sepp/bin:$PATH"
+export PATH
+
+vcheck (){
+ perl <<PERL
+@t = split /\./, "$1";
+@v = map { int \$_ } split /\./, (split /\s+/, \`$2\`)[3];
+print "$2 = ", (join ".",@v), " (expected $1)\n";
+\$v = \$t[0]*1000000+\$t[1]*1000+\$t[2] <= \$v[0]*1000000+\$v[1]*1000+\$v[2];
+exit \$v
+PERL
+}
+
+ERROR=0
+
+if vcheck 1.5.14 "libtool --version"
+then
+ echo "get a copy of GNU libtool >= 1.5.14"
+ ERROR=1
+fi
+
+if vcheck 1.9.5 "automake --version"
+then
+ if vcheck 1.9.5 "automake-1.9 --version"
+ then
+ echo "get a copy of GNU automake >= 1.9.5"
+ ERROR=1
+ else
+ automake=automake-1.9
+ aclocal="aclocal-1.9"
+ for d in /usr/pack/automake-1.9.5-to/share/aclocal-1.9 /usr/share/aclocal-1.9; do
+ [ -d $d ] && aclocal="$aclocal -I $d"
+ done
+ fi
+else
+ automake="automake"
+ aclocal="aclocal"
+# aclocal="aclocal -I /usr/pack/libtool-1.5.14-to/share/aclocal"
+fi
+
+if vcheck 2.59 "autoconf --version"
+then
+ echo "get a copy of GNU autoconf >= 2.59"
+ ERROR=1
+fi
+
+if [ $ERROR -ne 0 ]
+then
+ exit 1
+fi
+
+# cleanup
+set -x
+find . -name Makefile | grep -v win32 | grep -v netware | xargs rm -f _UNKNONW_
+find . -name "*.la" | xargs rm -f _UNKNOWN_
+find . -name Makefile.in | xargs rm -f _UNKNONW_
+find . -name .libs | xargs rm -r
+find . -name .debs | xargs rm -r
+
+$aclocal
+libtoolize --copy --force
+autoheader --force
+$aclocal
+$automake --foreign --add-missing --force-missing --copy
+autoconf --force
diff --git a/program/Makefile.am b/program/Makefile.am
--- /dev/null
+++ b/program/Makefile.am
@@ -0,0 +1,50 @@
+## Process this file with automake to produce Makefile.in
+RSYNC = rsync --rsh=ssh
+
+# build the following subdirectories
+SUBDIRS = src doc examples bindings
+
+ # the following files are not mentioned in any other Makefile
+EXTRA_DIST = COPYRIGHT CHANGES WIN32-BUILD-TIPS.txt TODO CONTRIBUTORS THREADS \
+ rrdtool.spec favicon.ico win32/config.h win32/rrd.dsp win32/rrd.vcproj \
+ win32/rrdtool.dsp win32/rrdtool.dsw win32/rrdtool.vcproj win32/Makefile \
+ win32/rrd_config.h.msvc netware/Makefile
+
+
+
+CLEANFILES = config.cache
+
+# use relaxed rules when building dists
+AUTOMAKE_OPTIONS= foreign
+
+# where we keep local rules for automake
+
+ACLOCAL_M4= $(top_srcdir)/aclocal.m4
+#AUTOHEADER = @AUTOHEADER@ --localdir=$(top_srcdir)/config
+#AUTOCONF = @AUTOCONF@ --localdir=$(top_srcdir)/config
+
+to-docs: to-versync
+ (cd doc && $(MAKE) clean && $(MAKE) && $(MAKE) pdf)
+
+to-dist: to-docs dist
+ mv $(PACKAGE)-$(VERSION).tar.gz archive
+
+to-scp: to-dist
+ cp CHANGES archive/$(PACKAGE)-$(VERSION).tar.gz /home/oetiker/public_html/webtools/rrdtool/pub/
+ (cd /home/oetiker/public_html/webtools/rrdtool/pub; rm $(PACKAGE).tar.gz; ln -s $(PACKAGE)-$(VERSION).tar.gz $(PACKAGE).tar.gz)
+
+# $(RSYNC) CHANGES archive/$(PACKAGE)-$(VERSION).tar.gz tobi@ipn.caida.org:/ipn/web/Tools/RRDtool/pub/
+
+site-perl-inst: site-perl-install
+
+site-perl-install: all bindings/perl-piped/Makefile bindings/perl-shared/Makefile
+ cd bindings/perl-piped && $(MAKE) install
+ cd bindings/perl-shared && $(MAKE) install
+
+site-tcl-install: all
+ cd bindings/tcl && $(MAKE) tcl-install
+
+site-python-install: all
+ cd bindings/python && $(PYTHON) setup.py install
+
+##END##
diff --git a/program/NEWS b/program/NEWS
--- /dev/null
+++ b/program/NEWS
@@ -0,0 +1,62 @@
+RRDTOOL NEWS
+============
+Major Changes between 1.0.x and 1.2.x
+
+Graphing
+--------
+
+* rewritten graphics generation based on libart.
+ - anti-aliased output
+ - alpha transparency support
+ - truetype fonts
+
+* additional graphics formats: EPS, PDF, SVG
+
+* extended multi-part documentation
+
+* VDEF support; define and use variables. Find, and use, the
+ maximum rate seen by rrdtool; compute and show the average
+
+* Sliding window (trend) analysis
+ Compute a smoother average, for instance over the last 6 CDPs
+
+* percentile (95th or other)
+ Remove peaks, 95 percent of all rates are at or below the
+ returned value
+
+Logging
+-------
+* a second logging interface: rrdtool updatev
+ Verbose updating of the database; show CPDs being created
+
+* Aberrant Behavior Detection with Holt-Winters Forecasting
+ Compare current data with expected data, detect and log when
+ the rates are outside expected levels
+
+* COMPUTE data type for artificial data-sources calculating their
+ input using RPN math and data from the other data-sources.
+
+Incompatibilities
+-----------------
+* Colons in COMMENT arguments to rrdtool graph must be escaped with a backslash
+
+* the --alt-y-mrtg option is gone or rather since 1.2.7 it is back but
+ without functionality.
+
+* In pipe mode, rrdtool answers with OK only if it was successful with the
+ command. Otherwhise the answer will be ERROR...
+
+
+Behind the Scenes
+-----------------
+* In order to support Holt-Winters and Calculated Datasources,
+ the rrdtool data format has changed. While the new version of rrdtool can
+ read files created with rrdtool 1.0.x. It is not possible to read files
+ created by rrdtool-1.2.x with rrdtool-1.0.x
+
+* External libraries are not included with rrdtool anymore. This is in line
+ with todays trend of using shared libraries everywhere. With the exception
+ of the cgi library most things required by rrdtool will be found on every recent
+ system.
+
+* Memory Mapped IO support for faster logging.
diff --git a/program/PROJECTS b/program/PROJECTS
--- /dev/null
+++ b/program/PROJECTS
@@ -0,0 +1,43 @@
+NEW RRD DATAFORMAT with Accessor Functions
+==========================================
+
+Interested:
+
+Tobias Oetiker <tobi@oetiker.ch>
+Jake Brutlag <jakeb@microsoft.com>
+- updating rrd_update to use accessor fks
+
+Plan:
+
+Encapsulating access to the RRD files through
+special accessor functions which provide
+access to the datastructures within the
+RRDfiles.
+
+pseudo code by Jake
+
+For example, here is a current code block from rrd_update.c:
+
+ for (i=0;i<rrd.stat_head->ds_cnt;i++) {
+ if(isnan(pdp_new[i]))
+ rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt += interval;
+ else
+ rrd.pdp_prep[i].scratch[PDP_val].u_val+= pdp_new[i];
+ }
+
+This could read:
+
+ for (i=0 ; i < getDSCount(&rrd) ;i++) {
+ if(isnan(pdp_new[i])) {
+ temp = getPDPParam(&rrd, i, PDP_unkn_sec_cnt);
+ temp.u_cnt += interval;
+ setPDPParam(&rrd, i, PDP_unkn_sec_cnt, temp);
+ } else {
+ temp = getPDPParam(&rrd, i, PDP_val);
+ temp.u_val += pdp_new[i];
+ setPDPParam(&rrd, i, PDP_val, temp);
+ }
+ }
+
+
+
diff --git a/program/README b/program/README
--- /dev/null
+++ b/program/README
@@ -0,0 +1,75 @@
+Round Robin Database Tools
+==========================
+
+It is pretty easy to gather status information from all sorts of things,
+ranging from the temperature in your office to the number of octets which
+have passed through the FDDI interface of your router. But it is not so
+trivial to store this data in a efficient and systematic manner. This is
+where RRDtool kicks in. It lets you log and analyze the data you gather from
+all kinds of data-sources (DS). The data analysis part of RRDtool is based
+on the ability to quickly generate graphical representations of the data
+values collected over a definable time period.
+
+
+To compile:
+-----------
+
+check out the instructions in doc/rrdbuild.txt
+
+Getting Started:
+----------------
+
+Either after compiling or after installing you can try the example
+RRDtool applications in the examples directory.
+
+To learn:
+---------
+
+Read the documentation in the doc directory. Start of with
+RRDtool. All documents are available as html and as ASCII text.
+
+If you are looking for a more slow paced introduction, make sure to read
+Alex van den Bogaerdt's rrdtutorial which is also available from the doc
+directory. Also read his cdeftutorial and Steve Rader's rpntutorial.
+
+If you want to know about the format of the log files check
+src/rrd_format.h there are a lot of comments in there ...
+
+How to make Tobi happy:
+-----------------------
+
+If you want to show your appreciation for RRDtool you could make me happy
+by going to http://tobi.oetiker.ch/wish and ordering a CD from
+my CD wish list ...
+
+
+How to keep in touch:
+---------------------
+
+There are 3 Mailing lists for RRDtool:
+
+rrd-announce LOW volume RRDtool Announcements List (Only Stable Releases)
+rrd-users For discussion amongst people who use RRDtool in their applications
+rrd-developers For people who actually HACK RRDtool code
+
+To subscribe to <MAILGLIST> send a message with the subject 'subscribe'
+to <MAILGLIST>-request@lists.oetiker.ch
+
+Note, that postings to rrd-announce will always be cross-posted
+to rrd-users and rrd-developers as well.
+
+To Contribute:
+--------------
+
+Contributed feature and bug patches are most welcome. But please
+send complete patches. A complete patch patches the CODE as well
+as the CHANGES, CONTRIBUTORS and the POD files.
+
+Use GNU diff --unified --recursive olddir newdir to build your patches.
+
+The latest Version:
+-------------------
+Is available from http://oss.oetiker.ch/rrdtool/
+
+
+Tobias Oetiker <tobi@oetiker.ch>
diff --git a/program/THREADS b/program/THREADS
--- /dev/null
+++ b/program/THREADS
@@ -0,0 +1,60 @@
+In order to use the librrd in multi-threaded programs you must:
+
+ * Link with librrd_th instead of with librrd
+ * Use the *_r function instead or the *-functions
+ * Never use non *_r functions unless it is explicitly documented that the
+ function is tread-safe
+
+Every thread SHOULD call rrd_get_context() before the first call to
+any librrd function in order to set up thread specific data. This is
+not strictly required, but it is the only way to test if memory
+allocation can be done by this function. Otherwise the program may die
+with a SIGSEGV in a low-memory situation.
+
+
+IMPORTANT NOTE FOR RRD CONTRIBUTORS:
+
+Some precautions must be followed when developing rrd from now on:
+
+* Only use thread-safe functions in library code. Many often used libc
+ functions aren't thread-safe. Take care if you want to use any of
+ the following functions:
+
+ + direct strerror calls must be avoided: use rrd_strerror instead,
+ it provides a per-thread error message
+ + the getpw*, getgr*, gethost* function families (and some more get*
+ functions): use the *_r variants
+ + Time functions: asctime, ctime, gmtime, localtime: use *_r variants
+ + strtok: use strtok_r
+ + tmpnam: use tmpnam_r
+ + many other (lookup documentation)
+
+As an aide(?) a header file named "rrd_is_thread_safe.h" is provided
+that works with the GNU C-preprocessor to "poison" some of the most
+common non-thread-safe functions using the "#pragma GCC poison"
+directive. Just include this header in source files you want to keep
+thread-safe.
+
+* Do not introduce global variables!
+
+ If you really, really have to use a global variable you may add a
+ new field to the rrd_context structure and modify rrd_error.c,
+ rrd_thread_safe.c and rrd_non_thread_safe.c
+
+* Do not use "getopt" or "getopt_long" in *_r (directly or indirectly)
+
+ getopt uses global variables and behaves badly in a multithreaded
+ application when called concurrently. Instead provide a *_r function
+ taking all options as function parameters. You may provide argc and
+ **argv arguments for variable length argument lists. See
+ rrd_update_r as an example.
+
+* Do not use the parsetime function!
+
+ It uses lots of global vars. You may use it in functions not
+ designed to be thread-safe like functions wrapping the _r version of some
+ operation (eg. rrd_create, but not in rrd_create_r)
+
+WIN32 Platform Note (added 04/01/03):
+
+Both rrdtool.vcproj (MSVC++ 7.0) and rrd.dsw (MSVC++ 6.0) are configured to compile with rrd_thread_safe_nt.c.
diff --git a/program/TODO b/program/TODO
--- /dev/null
+++ b/program/TODO
@@ -0,0 +1,68 @@
+What has to be done before the 1.2.0 Release
+--------------------------------------------
+* Sync Docs with reality (there are things described which have not been
+ implemented)
+
+* Write Changes Document with all new features
+
+* Update Tutorials for new Release.
+
+Future Ideas
+------------
+reverse order of stacked graph entries prior to plotting ... this is to make
+plotting order more naturally fit with the ordering of the legend ...
+
+make it possible to define order of legend items independant of their order
+on the commandline ...
+
+use XDR to write architecture independant date
+
+have drawing methode modifier for the LINE, AREA, STACK functions which allows
+to select wether the data points should be presented as stepes (todays state)
+or connected with lines.
+
+LINEx:ds[#color][/method]:description
+
+be more clever in the fetch function when no complete data coverage for the
+desired area is possible.
+
+Let the user define the base resolution of the graph independently of
+the pixel resolution. If it is smaller than one pixel it will simply
+be ignored
+
+allow sub second precision for the step definition
+
+modularise consolidation and acquisition functions
+
+Allow the user to define vertical scaling of graphs in a more flexible way.
+some thing like --inner-range 0-1000 and --outer-range 0-10000 which means
+that the grapher can choose its vertical scaling in the given range ...
+this would replace upper and lower limit
+
+make the inclusion of G?PRINT like output in to the lables of other graph
+elements possible
+
+add axis on the right and on top of the graph ...
+
+add configurable counter wrap
+
+build derivatives while plotting
+
+show trend by building moving average and represent it as an arrow
+
+offer side by side tiny box plots for each succesive hour, or some other
+means of seeing the variation rather than just the mean level
+
+circular periodic plots ... (polar)
+
+analyse data in non time domain ...
+
+add smothing functions to grapher, using rolling average ...
+
+make VRULE and HRULE with 123 versions ...
+
+have configurable role-over limits for counters
+
+align data points not to GMT but some free offset
+
+allow starting through symlinks called rrdcreate rrdtune and the like
diff --git a/program/WIN32-BUILD-TIPS.txt b/program/WIN32-BUILD-TIPS.txt
--- /dev/null
@@ -0,0 +1,335 @@
+Compiling RRDtool 1.1.x on Win32 with Microsoft Visual C++:
+---------------------------------------------------------------
+5/1/05 Tobi
+to help windows deal with the reentrant versions of many unix
+calls link with win32comp.c
+
+4/10/05 Tobi
+The windows implementation of strftime does not seem to support
+the ISO 8601 week number (%V) I have therfore included the file
+strftime.[ch] which provides strftime_ ... if you compile rrdtool
+with -Dstrftime=_strftime and link strftime.o then you will
+get propper support for %V.
+
+7/29/04 Jake Brutlag
+
+As of Jan 2004, code for libraries utilized by rrdtool
+(png, libart, freetype, and zlib) is no longer distributed with
+rrdtool. This requires some changes to the compile process on
+Win32. The solution described here is to compile rrdtool to
+link against these libraries dynamically. There is an advantage
+to this approach: namely the rrdtool distribution doesn't have to
+worry about how to compile these libraries on Win32. In theory,
+since others already provide and maintain Win32 binaries for these
+libraries the users don't have to worry about how to compile them
+either. The disadvantage of this approach is that the DLLs for
+these libraries must be available on the hosts where rrdtool will run.
+
+Here are step by step instructions for compiling rrdtool.exe and
+the perl shared library (RRDS.dll) with Microsoft Visual C++ 6.0.
+(1) Download libraries rrdtool depends on from GnuWin32:
+http://gnuwin32.sourceforge.net/
+For freetype, libpng, and zlib download the "Complete Package"; each of
+these will be a self-extracting self-installing executable.
+For libart, download both the "Binaries" and "Developer Files" packages.
+Unfortunately at this time GnuWin32 doesn't provide the "Complete Package"
+installer for libart. Perhaps by the time you are following these
+instructions GnuWin32 will have a "Complete Package" for libart.
+(2) Install the GnuWin32 libraries by running the executables for freetype,
+libpng, and zlib. These instructions and the Visual C++ project files
+distributed with rrdtool assume that you will use the default install
+location: C:\Program Files\GnuWin32. Extract the two zip files for libart,
+libart-2.3.3-bin.zip and libart-2.3.3-1-lib.zip into the GnuWin32 directory;
+the appropriate libart files will be added to the include, lib, and bin
+subdirectories.
+(3) Add C:\Program Files\GnuWin32\bin to the PATH (Control Panel ->
+System -> Advanced -> Environment Variables).
+(4) Start Microsoft Visual C++ 6.0. Load the workspace file, rrdtool.dsw,
+from the src subdirectory of your rrdtool code directory.
+(5) Compile the Release build of the rrdtool project (since rrdtool depends
+on the rrd project, the rrd library will also be compiled). At this
+time, the compile will fail in zconf.h, a zlib header file. The problem
+is a preprocessor directive that loads unistd.h. Open zconf.h in VC++
+(this file is in C:\Program Files\GnuWin32\include) and find the following
+code block:
+
+#if 1 /* HAVE_UNISTD_H -- this line is updated by ./configure */
+# include <sys/types.h> /* for off_t */
+# include <unistd.h> /* for SEEK_* and off_t */
+# ifdef VMS
+# include <unixio.h> /* for off_t */
+# endif
+# define z_off_t off_t
+#endif
+
+Change it to reads as follows (this is code from zlib-1.1.4):
+
+#if HAVE_UNISTD_H
+# include <sys/types.h> /* for off_t */
+# include <unistd.h> /* for SEEK_* and off_t */
+# ifdef VMS
+# include <unixio.h> /* for off_t */
+# endif
+# define z_off_t off_t
+#endif
+
+Note that it is actually just a one line change. Save the file and
+recompile rrdtool. By the time you are following these instructions
+this issue with zconf.h may be resolved.
+(6) At this point, you can run the executable rrdtool.exe in the
+src\toolrelease subdirectory. Note that if you wish to run rrdtool
+on other machines, you will need the following DLLs installed (on the
+path) on those machines:
+zlib1.dll
+libpng12.dll
+libart_lgpl.dll
+freetype6.dll
+msvcrt.dll
+The names of the first four DLLs might vary from what is listed here
+depending on the versions of the packages you downloaded from GnuWin32.
+The fifth DLL, msvcrt.dll, is a system DLL for most versions of Windows.
+If you are running on old version of Windows, you can install/upgrade to
+IE4.0 to get this DLL.
+(7) To compile the perl-shared library, open a Command Prompt (DOS box)
+and cd to the bindings\perl-shared subdirectory.
+(8) Run vcvars32.bat; this batch file, in your vc98\bin directory will
+set necessary environment options for command line compiling.
+(9) In bindings\perl-shared, run
+perl ntmake.pl
+nmake
+nmake test
+If nmake test succeeds, you are good to go. RRDs.dll is in
+blib\arch\auto\RRDs. If you plan to install via the Active State ppm
+tool, tar and gzip the blib directory. You can use the RRDs.ppd file
+in bindings\perl-shared directory. Remember that as in the case of
+rrdtool.exe you will need the DLLs listed in (6) on the machine where
+you are going to use RRDs.dll.
+
+Microsoft Visual C++ 7.1 (.NET 2003):
+
+Unfortunately, this is more difficult than with VC++ 6.0. The problem
+is that by default the C runtime dll for VC++ 7.1 is msvcr71.dll rather
+than msvcrt.dll. The GnuWin32 library binaries are all compiled
+to use msvcrt.dll and you can't mix msvcr71.dll and msvcrt.dll in the
+same process. One option is to download the source code for the libraries
+(available from http://gnuwin32.sourceforge.net) recompile them with
+VC++ 7.l. Then all the components will use msvcr71.dll. Once you are
+going to go this route, you can also use static multi-threaded libraries
+and use static linking between rrdtool (or RRDs.dll) and its dependencies.
+
+To use the GnuWin32 library binaries, you need to trick VC++ 7.1 into
+compiling rrdtool to use the older msvcrt.dll. Follow steps (1) - (3)
+as above, then:
+(4) Obtain a different version of the msvcrt.lib import library that
+is compatible with vc7 and points to msvcrt.dll:
+msvcrtlib_for_vc7.zip from http://xchat.org/win32/testing
+Backup msvcrt.lib in your vc7\lib directory
+(\Program Files\Microsoft Visual Studio .NET 2003\vc7\lib)
+Then extract the msvcrt.lib from the zip file into the vc7\lib directory.
+WARNING: Use this msvcrt.lib at your own risk! This is not a Microsoft
+supplied file nor a file supported by anyone associated with rrdtool.
+(5) Start Microsoft Visual C++ 7.1. Load the solution file, rrdtool.sln,
+from the src subdirectory of your rrdtool code directory. Edit zconf.h,
+as needed, as described under (5) above. Compile the release build of
+the rrdtool project.
+Proceed with steps (6) - (9) as above, if you are using/picking up
+the wrong msvcrt.lib import library then nmake test for perl-shared
+will fail.
+
+Note: it is possible in the future that GnuWin32 will provide Win32
+binaries that utilize msvcr71.dll rather than msvcrt.dll.
+
+5/14/02 Jake Brutlag
+
+These notes share some insight I gained compiling 1.1.x with
+MS Visual C++ 6.0 (using project files). This information may or
+may not be accurate at the time you are reading this.
+
+(1) freetype and rrdtool cannot use precompiled headers (which are
+enabled by default for MSVC++ projects). MSVC++ 6.0 does not
+support precompiled headers if #include directives contain MACROS.
+(2) Compile Release build with Default optimization, not the
+Maximize Speed optimization. I encountered some strange errors
+(related to argument processing for complex commands like graph--
+perhaps the getopt stuff is too blame) with Maximize Speed.
+(3) libart relies upon config.h (ostensibly generated by the
+configure script-- but of course not on Win32 platforms). ..\..\confignt
+(which contains a static Win32 version of config.h) should be on
+the include path.
+(4) Fonts are located in the %windir%\fonts, so the default font
+is c:\winnt\fonts\cour.ttf. (6/19/02) At Kerry Calvert's suggestion
+this setting was moved to confignt\config.h.
+(5) libart requires a custom build step to generate art_config.h; this
+is done manually via the commands:
+cl -I..\..\confignt gen_art_config.c
+gen_art_config.exe > art_config.h
+
+Currently, to compile rrd.lib and rrdtool.exe using
+the MSVC++ project files, first start MSVC++ 6.0. Open the rrdtool
+workspace (rrdtool.dsw in the src directory). The active project/
+configuration should be rrdtool-Win32 Release. Select Rebuild All
+from the Build menu. The static link library (rrd.lib) will
+be generated in src\release directory and executable will be generated
+in the src\toolrelease directory.
+
+Compiling RRDtool on NT ... work in progress
+---------------------------------------------------------------
+ by Tamas Kovacshazy (khazy@mit.bme.hu)
+
+Persisting Problems with the current NT port:
+
+Unfortunately, the RRD perl modules does not work with Perl
+(ActivePerl) using the current distribution.
+
+The RRD shared perl module can be compiled after some
+modification...
+
+Follow these steps:
+
+0. Install perl if you do not have it!
+ Visit http://www.ActiveState.com/pw32/ for a complete distribution.
+
+1. Copy ..\gd1.2\release\gd.lib to ..\gd1.2\
+2. Copy ..\src\release\rrd.lib to ..\src
+3. perl Makefile.pl
+
+In this step the system complains about something I do not
+understand. The error message is the following:
+
+Note (probably harmless): No library found for '-lm'
+
+Is a library missing? But it does not stop with an error...
+
+4. nmake test (You must have Visual C++ on the machine!)
+
+After these steps it generates the test files (svgs and rrds),
+and they seem to be good.
+
+The real problem in the shared perl modul is the following:
+
+I do not know how this installation stuff works. The problem is
+that the installation stuff looks for the gd.lib and the
+rrd.lib in the ..\gd1.2 and ..\src directory. The UNIX compile
+puts the files into these directories, but the NT compile does
+not.
+
+It is all for today,
+
+khazy
+
+Tamas Kovacshazy E-mail: khazy@mit.bme.hu
+WWW: http://www.mit.bme.hu/~khazy
+Technical University of Budapest
+Department of Measurement and Information Systems
+
+
+Compiling RRDtool 1.2.x on Win32 with MingW32 gcc:
+---------------------------------------------------------------
+
+1. Obtain and install the current version of the MingW package.
+
+ http://www.mingw.org/download.shtml
+
+ In the MinGW set you will need the gcc and binutils as a minimum.
+
+2. Obtain either of the following awk versions and install in a directory
+ on your System Path:
+
+ - awk.exe
+
+ http://cm.bell-labs.com/cm/cs/awkbook/index.html
+
+ Note: This version has no dependencies to other libs.
+
+ - gawk.exe (GnuWin32 version)
+
+ http://gnuwin32.sourceforge.net/packages/gawk.htm
+
+ Note: Also fetch the dependant libraries for it from the same page.
+
+3. If you plan to create a 'distribution' release of the RRD Tools, the
+ Makefile.Win32 will copy all the needed files to an output directory and
+ then zip the entire directory. A suitable zip utility can be obtained here:
+
+ http://www.info-zip.org/
+
+ Install in a directory on your System Path.
+
+4. Obtain the following libraries, ideally install them all under a common
+ directory:
+
+ = zlib
+
+ http://oss.oetiker.ch/rrdtool/pub/libs/zlib-1.2.3.tar.gz
+ http://www.zlib.net/
+
+ = libpng
+
+ http://oss.oetiker.ch/rrdtool/pub/libs/libpng-1.2.12.tar.gz
+ http://libpng.sourceforge.net/
+
+ = freetype
+
+ http://oss.oetiker.ch/rrdtool/pub/libs/freetype-2.2.1.tar.gz
+ http://freetype.sourceforge.net/index2.html
+
+ = libart_lgpl
+
+ http://oss.oetiker.ch/rrdtool/pub/libs/libart_lgpl-2.3.17.tar.gz
+ http://www.levien.com/libart/
+
+ Note: libart_lgpl needs a special tweak because the archive contains
+ only the base directory, but the libart headers are usually included with
+ a directory prefix; therefore create a subfolder 'libart_lgpl' and move
+ all files into this subfolder.
+
+5. Set up for DOS environment.
+
+ Add MingW\bin and MSYS\bin directories to your System path.
+
+ If the libraries share a common directory set the following environment var:
+
+ set LIBBASE=<shared director>
+ e.g set LIBBASE=C:\Libraries
+
+ If the libraries are scattered, set the following environment vers:
+
+ set ZLIBSDK=<path to zlib>
+ e.g set ZLIBSDK=C:\mytest\zlib-1.2.3
+ set LIBPNG=<path to libpng>
+ set LIBFT2=<path to freetype>
+ set LIBART=<path to libart>
+
+ If using the Gnu Awk (gawk.exe), edit the Makefile.Win32 and change the line:
+
+ AWK = awk
+
+ to
+
+ AWK = gawk
+
+6. Compile the project.
+
+ All dependent libs are statically linked in. This has the benefit that the
+ binaries do not depend on any other DLLs.
+ In order to build the static freetype lib enter the freetype base directory
+ and type 'make'. If everything is fine a message appears that gcc is detected,
+ and that you should again type 'make'. Follow that in order to build freetype.
+ All other libs are build from the sources with the RRDTool Makefile.Win32.
+
+ Switch to the RRDTOOL .\src directory. Then:
+
+ make -f Makefile.Win32 help
+
+ to see the build options, or
+
+ make -f Makefile.Win32 all
+
+ should build the entire package.
+
+6. Happy Graphing!
+
+
+written by normw & gk.
+
+
diff --git a/program/acinclude.m4 b/program/acinclude.m4
--- /dev/null
+++ b/program/acinclude.m4
@@ -0,0 +1,548 @@
+dnl Helper Functions for the RRDtool configure.ac script
+dnl
+dnl this file gets included into aclocal.m4 when runnning aclocal
+dnl
+dnl
+dnl
+dnl Check for the presence of a particular library and its header files
+dnl if this check fails set the environment variable EX_CHECK_ALL_ERR to YES
+dnl and prints out a helful message
+dnl
+dnl
+dnl EX_CHECK_ALL(library, function, header, pkgconf name, tested-version, homepage, cppflags)
+dnl $1 $2 $3 $4 $5 $6 $7
+dnl
+dnl
+AC_DEFUN([EX_CHECK_ALL],
+[
+ AC_LANG_PUSH(C)
+ EX_CHECK_STATE=NO
+ ex_check_save_LIBS=${LIBS}
+ ex_check_save_CPPFLAGS=${CPPFLAGS}
+ ex_check_save_LDFLAGS=${LDFLAGS}
+ if test "x$7" != "x"; then
+ CPPFLAGS="$CPPFLAGS -I$7"
+ fi
+ dnl try compiling naked first
+ AC_CHECK_LIB($1,$2, [
+ AC_CHECK_HEADER($3,[LIBS="-l$1 ${LIBS}";EX_CHECK_STATE=YES],[])],[])
+ if test $EX_CHECK_STATE = NO; then
+ dnl now asking pkg-config for help
+ AC_CHECK_PROGS(PKGCONFIG,[pkg-config],no)
+ if test "$PKGCONFIG" != "no"; then
+ if $PKGCONFIG --exists $4; then
+ CPPFLAGS=${CPPFLAGS}" "`$PKGCONFIG --cflags $4`
+ LDFLAGS=${LDFLAGS}" "`$PKGCONFIG --libs-only-L $4`
+ LDFLAGS=${LDFLAGS}" "`$PKGCONFIG --libs-only-other $4`
+ LIBS=${LIBS}" "`$PKGCONFIG --libs-only-l $4`
+ dnl remove the cached value and test again
+ unset ac_cv_lib_$1_$2
+ AC_CHECK_LIB($1,$2,[
+ unset ac_cv_header_`echo $3 | sed ['s/[^a-zA-Z0-9_]/_/g;s/^[0-9]/_/']`
+ AC_CHECK_HEADER($3,[EX_CHECK_STATE=YES],[])
+ ],[])
+ else
+ AC_MSG_WARN([
+----------------------------------------------------------------------------
+* I found a copy of pkgconfig, but there is no $4.pc file around.
+ You may want to set the PKG_CONFIG_PATH variable to point to its
+ location.
+----------------------------------------------------------------------------
+ ])
+ fi
+ fi
+ fi
+
+ if test ${EX_CHECK_STATE} = NO; then
+ AC_MSG_WARN([
+----------------------------------------------------------------------------
+* I could not find a working copy of $4. Check config.log for hints on why
+ this is the case. Maybe you need to set LDFLAGS and CPPFLAGS appropriately
+ so that compiler and the linker can find lib$1 and its header files. If
+ you have not installed $4, you can get it either from its original home on
+
+ $6
+
+ You can find also find an archive copy on
+
+ http://oss.oetiker.ch/rrdtool/pub/libs
+
+ The last tested version of $4 is $5.
+
+ LIBS=$LIBS
+ LDFLAGS=$LDFLAGS
+ CPPFLAGS=$CPPFLAGS
+
+----------------------------------------------------------------------------
+ ])
+ EX_CHECK_ALL_ERR=YES
+ LIBS="${ex_check_save_LIBS}"
+ CPPFLAGS="${ex_check_save_CPPFLAGS}"
+ LDFLAGS="${ex_check_save_LDFLAGS}"
+ fi
+ AC_LANG_POP(C)
+]
+)
+
+dnl
+dnl Ptherad check from http://autoconf-archive.cryp.to/acx_pthread.m4
+dnl
+dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
+dnl
+dnl This macro figures out how to build C programs using POSIX threads.
+dnl It sets the PTHREAD_LIBS output variable to the threads library and
+dnl linker flags, and the PTHREAD_CFLAGS output variable to any special
+dnl C compiler flags that are needed. (The user can also force certain
+dnl compiler flags/libs to be tested by setting these environment
+dnl variables.)
+dnl
+dnl Also sets PTHREAD_CC to any special C compiler that is needed for
+dnl multi-threaded programs (defaults to the value of CC otherwise).
+dnl (This is necessary on AIX to use the special cc_r compiler alias.)
+dnl
+dnl NOTE: You are assumed to not only compile your program with these
+dnl flags, but also link it with them as well. e.g. you should link
+dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS
+dnl $LIBS
+dnl
+dnl If you are only building threads programs, you may wish to use
+dnl these variables in your default LIBS, CFLAGS, and CC:
+dnl
+dnl LIBS="$PTHREAD_LIBS $LIBS"
+dnl CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+dnl CC="$PTHREAD_CC"
+dnl
+dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute
+dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to
+dnl that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
+dnl
+dnl ACTION-IF-FOUND is a list of shell commands to run if a threads
+dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands to
+dnl run it if it is not found. If ACTION-IF-FOUND is not specified, the
+dnl default action will define HAVE_PTHREAD.
+dnl
+dnl Please let the authors know if this macro fails on any platform, or
+dnl if you have any other suggestions or comments. This macro was based
+dnl on work by SGJ on autoconf scripts for FFTW (www.fftw.org) (with
+dnl help from M. Frigo), as well as ac_pthread and hb_pthread macros
+dnl posted by Alejandro Forero Cuervo to the autoconf macro repository.
+dnl We are also grateful for the helpful feedback of numerous users.
+dnl
+dnl @category InstalledPackages
+dnl @author Steven G. Johnson <stevenj@alum.mit.edu>
+dnl @version 2005-01-14
+dnl @license GPLWithACException
+
+AC_DEFUN([ACX_PTHREAD], [
+AC_REQUIRE([AC_CANONICAL_HOST])
+AC_LANG_PUSH(C)
+acx_pthread_ok=no
+
+# We used to check for pthread.h first, but this fails if pthread.h
+# requires special compiler flags (e.g. on True64 or Sequent).
+# It gets checked for in the link test anyway.
+
+# First of all, check if the user has set any of the PTHREAD_LIBS,
+# etcetera environment variables, and if threads linking works using
+# them:
+if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
+ AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes)
+ AC_MSG_RESULT($acx_pthread_ok)
+ if test x"$acx_pthread_ok" = xno; then
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+ fi
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+fi
+
+# We must check for the threads library under a number of different
+# names; the ordering is very important because some systems
+# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
+# libraries is broken (non-POSIX).
+
+# Create a list of thread flags to try. Items starting with a "-" are
+# C compiler flags, and other items are library names, except for "none"
+# which indicates that we try without any flags at all, and "pthread-config"
+# which is a program returning the flags for the Pth emulation library.
+
+acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
+
+# The ordering *is* (sometimes) important. Some notes on the
+# individual items follow:
+
+# pthreads: AIX (must check this before -lpthread)
+# none: in case threads are in libc; should be tried before -Kthread and
+# other compiler flags to prevent continual compiler warnings
+# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
+# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
+# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
+# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
+# -pthreads: Solaris/gcc
+# -mthreads: Mingw32/gcc, Lynx/gcc
+# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
+# doesn't hurt to check since this sometimes defines pthreads too;
+# also defines -D_REENTRANT)
+# pthread: Linux, etcetera
+# --thread-safe: KAI C++
+# pthread-config: use pthread-config program (for GNU Pth library)
+
+case "${host_cpu}-${host_os}" in
+ *solaris*)
+
+ # On Solaris (at least, for some versions), libc contains stubbed
+ # (non-functional) versions of the pthreads routines, so link-based
+ # tests will erroneously succeed. (We need to link with -pthread or
+ # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
+ # a function called by this macro, so we could check for that, but
+ # who knows whether they'll stub that too in a future libc.) So,
+ # we'll just look for -pthreads and -lpthread first:
+
+ acx_pthread_flags="-pthread -pthreads pthread -mt $acx_pthread_flags"
+ ;;
+esac
+
+if test x"$acx_pthread_ok" = xno; then
+for flag in $acx_pthread_flags; do
+
+ case $flag in
+ none)
+ AC_MSG_CHECKING([whether pthreads work without any flags])
+ ;;
+
+ -*)
+ AC_MSG_CHECKING([whether pthreads work with $flag])
+ PTHREAD_CFLAGS="$flag"
+ ;;
+
+ pthread-config)
+ AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no)
+ if test x"$acx_pthread_config" = xno; then continue; fi
+ PTHREAD_CFLAGS="`pthread-config --cflags`"
+ PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
+ ;;
+
+ *)
+ AC_MSG_CHECKING([for the pthreads library -l$flag])
+ PTHREAD_LIBS="-l$flag"
+ ;;
+ esac
+
+ save_LIBS="$LIBS"
+ save_CFLAGS="$CFLAGS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Check for various functions. We must include pthread.h,
+ # since some functions may be macros. (On the Sequent, we
+ # need a special flag -Kthread to make this header compile.)
+ # We check for pthread_join because it is in -lpthread on IRIX
+ # while pthread_create is in libc. We check for pthread_attr_init
+ # due to DEC craziness with -lpthreads. We check for
+ # pthread_cleanup_push because it is one of the few pthread
+ # functions on Solaris that doesn't have a non-functional libc stub.
+ # We try pthread_create on general principles.
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]], [[pthread_t th; pthread_join(th, 0);
+ pthread_attr_init(0); pthread_cleanup_push(0, 0);
+ pthread_create(0,0,0,0); pthread_cleanup_pop(0); ]])],[acx_pthread_ok=yes],[])
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ AC_MSG_RESULT($acx_pthread_ok)
+ if test "x$acx_pthread_ok" = xyes; then
+ break;
+ fi
+
+ PTHREAD_LIBS=""
+ PTHREAD_CFLAGS=""
+done
+fi
+
+# Various other checks:
+if test "x$acx_pthread_ok" = xyes; then
+ save_LIBS="$LIBS"
+ LIBS="$PTHREAD_LIBS $LIBS"
+ save_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
+
+ # Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
+ AC_MSG_CHECKING([for joinable pthread attribute])
+ attr_name=unknown
+ for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <pthread.h>]], [[int attr=$attr;]])],[attr_name=$attr; break],[])
+ done
+ AC_MSG_RESULT($attr_name)
+ if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
+ AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
+ [Define to necessary symbol if this constant
+ uses a non-standard name on your system.])
+ fi
+
+ AC_MSG_CHECKING([if more special flags are required for pthreads])
+ x_rflag=no
+ case "${host_cpu}-${host_os}" in
+ *-aix* | *-freebsd* | *-darwin*) x_rflag="-D_THREAD_SAFE";;
+ *solaris* | *-osf* | *-hpux*) x_rflag="-D_REENTRANT";;
+ *-linux*)
+ if test x"$PTHREAD_CFLAGS" = "x-pthread"; then
+ # For Linux/gcc "-pthread" implies "-lpthread". We need, however, to make this explicit
+ # in PTHREAD_LIBS such that a shared library to be built properly depends on libpthread.
+ PTHREAD_LIBS="-lpthread $PTHREAD_LIBS"
+ fi;;
+ esac
+ AC_MSG_RESULT(${x_rflag})
+ if test "x$x_rflag" != xno; then
+ PTHREAD_CFLAGS="$x_rflag $PTHREAD_CFLAGS"
+ fi
+
+ LIBS="$save_LIBS"
+ CFLAGS="$save_CFLAGS"
+
+ # More AIX lossage: must compile with cc_r
+ AC_CHECK_PROG(PTHREAD_CC, cc_r, cc_r, ${CC})
+else
+ PTHREAD_CC="$CC"
+fi
+
+AC_SUBST(PTHREAD_LIBS)
+AC_SUBST(PTHREAD_CFLAGS)
+AC_SUBST(PTHREAD_CC)
+
+# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
+if test x"$acx_pthread_ok" = xyes; then
+ ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
+ :
+else
+ acx_pthread_ok=no
+ $2
+fi
+AC_LANG_POP(C)
+])dnl ACX_PTHREAD
+
+
+dnl
+dnl determine how to get IEEE math working
+dnl AC_IEEE(MESSAGE, set rd_cv_ieee_[var] variable, INCLUDES,
+dnl FUNCTION-BODY, [ACTION-IF-FOUND [,ACTION-IF-NOT-FOUND]])
+dnl
+
+dnl substitute them in all the files listed in AC_OUTPUT
+AC_SUBST(PERLFLAGS)
+
+AC_DEFUN([AC_IEEE], [
+AC_MSG_CHECKING([if IEEE math works $1])
+AC_CACHE_VAL([rd_cv_ieee_$2],
+[AC_RUN_IFELSE([AC_LANG_SOURCE([[$3
+
+#if HAVE_MATH_H
+# include <math.h>
+#endif
+
+#if HAVE_FLOAT_H
+# include <float.h>
+#endif
+
+#if HAVE_IEEEFP_H
+# include <ieeefp.h>
+#endif
+
+#if HAVE_FP_CLASS_H
+# include <fp_class.h>
+#endif
+
+/* Solaris */
+#if (! defined(HAVE_ISINF) && defined(HAVE_FPCLASS))
+# define HAVE_ISINF 1
+# define isinf(a) (fpclass(a) == FP_NINF || fpclass(a) == FP_PINF)
+#endif
+
+/* solaris 10 it defines isnan such that only forte can compile it ... bad bad */
+#if (defined(HAVE_ISNAN) && defined(isnan) && defined(HAVE_FPCLASS))
+# undef isnan
+# define isnan(a) (fpclass(a) == FP_SNAN || fpclass(a) == FP_QNAN)
+#endif
+
+/* Digital UNIX */
+#if (! defined(HAVE_ISINF) && defined(HAVE_FP_CLASS) && defined(HAVE_FP_CLASS_H))
+# define HAVE_ISINF 1
+# define isinf(a) (fp_class(a) == FP_NEG_INF || fp_class(a) == FP_POS_INF)
+#endif
+
+/* AIX */
+#if (! defined(HAVE_ISINF) && defined(HAVE_CLASS))
+# define HAVE_ISINF 1
+# define isinf(a) (class(a) == FP_MINUS_INF || class(a) == FP_PLUS_INF)
+#endif
+
+#if (! defined(HAVE_ISINF) && defined(HAVE_FPCLASSIFY) && defined(FP_PLUS_INF) && defined(FP_MINUS_INF))
+# define HAVE_ISINF 1
+# define isinf(a) (fpclassify(a) == FP_MINUS_INF || fpclassify(a) == FP_PLUS_INF)
+#endif
+
+#if (! defined(HAVE_ISINF) && defined(HAVE_FPCLASSIFY) && defined(FP_INFINITE))
+# define HAVE_ISINF 1
+# define isinf(a) (fpclassify(a) == FP_INFINITE)
+#endif
+
+#include <stdio.h>
+int main(void){
+ double rrdnan,rrdinf,rrdc,rrdzero;
+ $4;
+ /* some math to see if we get a floating point exception */
+ rrdzero=sin(0.0); /* don't let the compiler optimize us away */
+ rrdnan=0.0/rrdzero; /* especially here */
+ rrdinf=1.0/rrdzero; /* and here. I want to know if it can do the magic */
+ /* at run time without sig fpe */
+ rrdc = rrdinf + rrdnan;
+ rrdc = rrdinf / rrdnan;
+ if (! isnan(rrdnan)) {printf ("not isnan(NaN) ... "); return 1;}
+ if (rrdnan == rrdnan) {printf ("nan == nan ... "); return 1;}
+ if (! isinf(rrdinf)) {printf ("not isinf(oo) ... "); return 1;}
+ if (! isinf(-rrdinf)) {printf ("not isinf(-oo) ... "); return 1;}
+ if (! rrdinf > 0) {printf ("not inf > 0 ... "); return 1;}
+ if (! -rrdinf < 0) {printf ("not -inf < 0 ... "); return 1;}
+ return 0;
+ }]])],[rd_cv_ieee_$2=yes],[rd_cv_ieee_$2=no],[:])])
+dnl these we run regardles is cached or not
+if test x${rd_cv_ieee_$2} = "xyes"; then
+ AC_MSG_RESULT(yes)
+ $5
+else
+ AC_MSG_RESULT(no)
+ $6
+fi
+
+])
+
+AC_DEFUN([AC_FULL_IEEE],[
+AC_LANG_PUSH(C)
+_cflags=${CFLAGS}
+AC_IEEE([out of the box], works, , , ,
+ [CFLAGS="$_cflags -ieee"
+ AC_IEEE([with the -ieee switch], switch, , , ,
+ [CFLAGS="$_cflags -qfloat=nofold"
+ AC_IEEE([with the -qfloat=nofold switch], nofold, , , ,
+ [CFLAGS="$_cflags -w -qflttrap=enable:zerodivide"
+ AC_IEEE([with the -w -qflttrap=enable:zerodivide], flttrap, , , ,
+ [CFLAGS="$_cflags -mieee"
+ AC_IEEE([with the -mieee switch], mswitch, , , ,
+ [CFLAGS="$_cflags -q float=rndsngl"
+ AC_IEEE([with the -q float=rndsngl switch], qswitch, , , ,
+ [CFLAGS="$_cflags -OPT:IEEE_NaN_inf=ON"
+ AC_IEEE([with the -OPT:IEEE_NaN_inf=ON switch], ieeenaninfswitch, , , ,
+ [CFLAGS="$_cflags -OPT:IEEE_comparisons=ON"
+ AC_IEEE([with the -OPT:IEEE_comparisons=ON switch], ieeecmpswitch, , , ,
+ [CFLAGS=$_cflags
+ AC_IEEE([with fpsetmask(0)], mask,
+ [#include <floatingpoint.h>], [fpsetmask(0)],
+ [AC_DEFINE(MUST_DISABLE_FPMASK)
+ PERLFLAGS="CCFLAGS=-DMUST_DISABLE_FPMASK"],
+ [AC_IEEE([with signal(SIGFPE,SIG_IGN)], sigfpe,
+ [#include <signal.h>], [signal(SIGFPE,SIG_IGN)],
+ [AC_DEFINE(MUST_DISABLE_SIGFPE)
+ PERLFLAGS="CCFLAGS=-DMUST_DISABLE_SIGFPE"],
+ AC_MSG_ERROR([
+Your Compiler does not do propper IEEE math ... Please find out how to
+make IEEE math work with your compiler and let me know (tobi@oetiker.ch).
+Check config.log to see what went wrong ...
+]))])])])])])])])])])
+
+AC_LANG_POP(C)
+
+])
+
+
+dnl a macro to check for ability to create python extensions
+dnl AM_CHECK_PYTHON_HEADERS([ACTION-IF-POSSIBLE], [ACTION-IF-NOT-POSSIBLE])
+dnl function also defines PYTHON_INCLUDES
+AC_DEFUN([AM_CHECK_PYTHON_HEADERS],
+[AC_REQUIRE([AM_PATH_PYTHON])
+AC_MSG_CHECKING(for headers required to compile python extensions)
+dnl deduce PYTHON_INCLUDES
+py_prefix=`$PYTHON -c "import sys; print sys.prefix"`
+py_exec_prefix=`$PYTHON -c "import sys; print sys.exec_prefix"`
+PYTHON_INCLUDES="-I${py_prefix}/include/python${PYTHON_VERSION}"
+if test "$py_prefix" != "$py_exec_prefix"; then
+ PYTHON_INCLUDES="$PYTHON_INCLUDES -I${py_exec_prefix}/include/python${PYTHON_VERSION}"
+fi
+AC_SUBST(PYTHON_INCLUDES)
+dnl check if the headers exist:
+save_CPPFLAGS="$CPPFLAGS"
+CPPFLAGS="$CPPFLAGS $PYTHON_INCLUDES"
+AC_TRY_CPP([#include <Python.h>],dnl
+[AC_MSG_RESULT(found)
+$1],dnl
+[AC_MSG_RESULT(not found)
+$2])
+CPPFLAGS="$save_CPPFLAGS"
+])
+
+dnl a macro to add some color to the build process.
+dnl CONFIGURE_PART(MESSAGE)
+
+AC_DEFUN([CONFIGURE_PART],[
+case $TERM in
+ # for the most important terminal types we directly know the sequences
+ xterm|xterm*|vt220|vt220*)
+ T_MD=`awk 'BEGIN { printf("%c%c%c%c", 27, 91, 49, 109); }' </dev/null 2>/dev/null`
+ T_ME=`awk 'BEGIN { printf("%c%c%c", 27, 91, 109); }' </dev/null 2>/dev/null`
+ ;;
+ vt100|vt100*|cygwin)
+ T_MD=`awk 'BEGIN { printf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0); }' </dev/null 2>/dev/null`
+ T_ME=`awk 'BEGIN { printf("%c%c%c%c%c", 27, 91, 109, 0, 0); }' </dev/null 2>/dev/null`
+ ;;
+ *)
+ T_MD=''
+ T_ME=''
+ ;;
+esac
+ AC_MSG_RESULT()
+ AC_MSG_RESULT([${T_MD}$1${T_ME}])
+])
+
+
+dnl ---------------------------------------------------------------------------
+dnl CF_DISABLE_ECHO version: 10 updated: 2003/04/17 22:27:11
+dnl ---------------
+dnl stolen from xterm aclocal.m4
+dnl
+dnl You can always use "make -n" to see the actual options, but it's hard to
+dnl pick out/analyze warning messages when the compile-line is long.
+dnl
+dnl Sets:
+dnl ECHO_LT - symbol to control if libtool is verbose
+dnl ECHO_LD - symbol to prefix "cc -o" lines
+dnl RULE_CC - symbol to put before implicit "cc -c" lines (e.g., .c.o)
+dnl SHOW_CC - symbol to put before explicit "cc -c" lines
+dnl ECHO_CC - symbol to put before any "cc" line
+dnl
+AC_DEFUN([CF_DISABLE_ECHO],[
+AC_MSG_CHECKING(if you want to see long compiling messages)
+CF_ARG_DISABLE(echo,
+ [ --disable-echo display "compiling" commands],
+ [
+ ECHO_LT='--silent'
+ ECHO_LD='@echo linking [$]@;'
+ RULE_CC=' @echo compiling [$]<'
+ SHOW_CC=' @echo compiling [$]@'
+ ECHO_CC='@'
+],[
+ ECHO_LT=''
+ ECHO_LD=''
+ RULE_CC='# compiling'
+ SHOW_CC='# compiling'
+ ECHO_CC=''
+])
+AC_MSG_RESULT($enableval)
+AC_SUBST(ECHO_LT)
+AC_SUBST(ECHO_LD)
+AC_SUBST(RULE_CC)
+AC_SUBST(SHOW_CC)
+AC_SUBST(ECHO_CC)
+])dnl
diff --git a/program/bindings/Makefile.am b/program/bindings/Makefile.am
--- /dev/null
@@ -0,0 +1,56 @@
+.PHONY: python ruby
+
+if BUILD_TCL
+SUB_tcl = tcl
+endif
+
+SUBDIRS = $(SUB_tcl)
+
+# the following files are not mentioned in any other Makefile
+EXTRA_DIST = perl-piped/MANIFEST perl-piped/README perl-piped/Makefile.PL perl-piped/RRDp.pm perl-piped/t/base.t \
+ perl-shared/ntmake.pl perl-shared/MANIFEST perl-shared/README perl-shared/Makefile.PL perl-shared/RRDs.pm perl-shared/RRDs.xs perl-shared/t/base.t \
+ ruby/CHANGES ruby/README ruby/extconf.rb ruby/main.c ruby/test.rb \
+ python/ACKNOWLEDGEMENT python/AUTHORS python/COPYING python/README python/rrd_extra.h python/rrdtoolmodule.c python/setup.py
+
+
+# add the following to the all target
+all-local: @COMP_PERL@ @COMP_RUBY@ @COMP_PYTHON@
+
+install-data-local:
+ test -f perl-piped/Makefile && cd perl-piped && $(MAKE) install || true
+ test -f perl-shared/Makefile && cd perl-shared && $(MAKE) install || true
+ test -f ruby/Makefile && cd ruby && $(MAKE) EPREFIX=$(DESTDIR)$(exec_prefix) $(RUBY_MAKE_OPTIONS) install || true
+ test -d python/build && cd python && env BUILDLIBDIR=../../src/.libs $(PYTHON) setup.py install --skip-build --prefix=$(DESTDIR)$(prefix) --exec-prefix=$(DESTDIR)$(exec_prefix) || true
+
+# rules for buildung the ruby module
+# RUBYARCHDIR= is to work around in a makefile quirk not sure
+# it is is the right thing todo, but it makes rrdtool build on freebsd as well
+ruby:
+ cd ruby && $(RUBY) extconf.rb && $(MAKE) EPREFIX=$(exec_prefix) $(RUBY_MAKE_OPTIONS) RUBYARCHDIR=
+
+# rules for buildung the pyton module
+python:
+ cd python && env BUILDLIBDIR=../../src/.libs $(PYTHON) setup.py build_ext --rpath=$(libdir) && env LIBDIR=../../src/.libs $(PYTHON) setup.py build
+
+# rules for building the perl module
+perl_piped: perl-piped/Makefile
+ cd perl-piped && $(MAKE)
+
+perl-piped/Makefile: perl-piped/Makefile.PL
+ cd perl-piped && $(PERL) Makefile.PL $(PERL_MAKE_OPTIONS)
+
+perl_shared: perl-shared/Makefile
+ cd perl-shared && $(MAKE)
+
+perl-shared/Makefile: perl-shared/Makefile.PL
+ cd perl-shared && $(PERL) Makefile.PL $(PERLFLAGS) $(PERL_MAKE_OPTIONS) RPATH=$(libdir)
+# LIBS="$(LDFLAGS) $(LIBS)" $(PERLFLAGS) $(PERL_MAKE_OPTIONS)
+
+clean-local:
+ test -f perl-piped/Makefile && cd perl-piped && $(MAKE) clean || true
+ test -f perl-piped/Makefile && rm perl-piped/Makefile || true
+ test -f perl-shared/Makefile && cd perl-shared && $(MAKE) clean || true
+ test -f perl-shared/Makefile && rm -f perl-shared/Makefile || true
+ test -f ruby/Makefile && cd ruby && $(MAKE) clean && rm Makefile || true
+ test -d python/build && cd python && rm -rf build || true
+##END##
diff --git a/program/bindings/perl-piped/MANIFEST b/program/bindings/perl-piped/MANIFEST
--- /dev/null
@@ -0,0 +1,5 @@
+MANIFEST
+README
+Makefile.PL
+RRDp.pm
+t/base.t
diff --git a/program/bindings/perl-piped/Makefile.PL b/program/bindings/perl-piped/Makefile.PL
--- /dev/null
@@ -0,0 +1,10 @@
+use ExtUtils::MakeMaker;
+
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+WriteMakefile(
+ 'NAME' => 'RRDp',
+ 'VERSION' => '0.99.0', # finds $VERSION
+ 'linkext' => {LINKTYPE => ''},
+ 'dist' => {COMPRESS=>'gzip', SUFFIX=>'gz'},
+);
diff --git a/program/bindings/perl-piped/README b/program/bindings/perl-piped/README
--- /dev/null
@@ -0,0 +1,5 @@
+This is a Perl module for using RRDtool process via a set of pipes.
+
+perl Makefile.PL
+make test
+make install
diff --git a/program/bindings/perl-piped/RRDp.pm b/program/bindings/perl-piped/RRDp.pm
--- /dev/null
@@ -0,0 +1,200 @@
+package RRDp;
+
+=head1 NAME
+
+RRDp - Attach RRDtool from within a perl script via a set of pipes;
+
+=head1 SYNOPSIS
+
+use B<RRDp>
+
+B<RRDp::start> I<path to RRDtool executable>
+
+B<RRDp::cmd> I<rrdtool commandline>
+
+$answer = B<RRD::read>
+
+$status = B<RRD::end>
+
+B<$RRDp::user>, B<$RRDp::sys>, B<$RRDp::real>, B<$RRDp::error_mode>, B<$RRDp::error>
+
+=head1 DESCRIPTION
+
+With this module you can safely communicate with the RRDtool.
+
+After every B<RRDp::cmd> you have to issue an B<RRDp::read> command to get
+B<RRDtool>s answer to your command. The answer is returned as a pointer,
+in order to speed things up. If the last command did not return any
+data, B<RRDp::read> will return an undefined variable.
+
+If you import the PERFORMANCE variables into your namespace,
+you can access RRDtool's internal performance measurements.
+
+=over 8
+
+=item use B<RRDp>
+
+Load the RRDp::pipe module.
+
+=item B<RRDp::start> I<path to RRDtool executable>
+
+start RRDtool. The argument must be the path to the RRDtool executable
+
+=item B<RRDp::cmd> I<rrdtool commandline>
+
+pass commands on to RRDtool. check the RRDtool documentation for
+more info on the RRDtool commands.
+
+=item $answer = B<RRDp::read>
+
+read RRDtool's response to your command. Note that the $answer variable will
+only contain a pointer to the returned data. The reason for this is, that
+RRDtool can potentially return quite excessive amounts of data
+and we don't want to copy this around in memory. So when you want to
+access the contents of $answer you have to use $$answer which dereferences
+the variable.
+
+=item $status = B<RRDp::end>
+
+terminates RRDtool and returns RRDtool's status ...
+
+=item B<$RRDp::user>, B<$RRDp::sys>, B<$RRDp::real>
+
+these variables will contain totals of the user time, system time and
+real time as seen by RRDtool. User time is the time RRDtool is
+running, System time is the time spend in system calls and real time
+is the total time RRDtool has been running.
+
+The difference between user + system and real is the time spent
+waiting for things like the hard disk and new input from the perl
+script.
+
+=item B<$RRDp::error_mode> and B<$RRDp::error>
+
+If you set the variable $RRDp::error_mode to the value 'catch' before you run RRDp::read a potential
+ERROR message will not cause the program to abort but will be returned in this variable. If no error
+occurs the variable will be empty.
+
+ $RRDp::error_mode = 'catch';
+ RRDp::cmd qw(info file.rrd);
+ print $RRDp::error if $RRDp::error;
+
+=back
+
+
+=head1 EXAMPLE
+
+ use RRDp;
+ RRDp::start "/usr/local/bin/rrdtool";
+ RRDp::cmd qw(create demo.rrd --step 100
+ DS:in:GAUGE:100:U:U
+ RRA:AVERAGE:0.5:1:10);
+ $answer = RRDp::read;
+ print $$answer;
+ ($usertime,$systemtime,$realtime) = ($RRDp::user,$RRDp::sys,$RRDp::real);
+
+=head1 SEE ALSO
+
+For more information on how to use RRDtool, check the manpages.
+
+=head1 AUTHOR
+
+Tobias Oetiker <tobi@oetiker.ch>
+
+=cut
+#' this is to make cperl.el happy
+
+use strict;
+use Fcntl;
+use Carp;
+use IO::Handle;
+use IPC::Open2;
+use vars qw($Sequence $RRDpid $VERSION);
+my $Sequence;
+my $RRDpid;
+
+# Prototypes
+
+sub start ($);
+sub cmd (@);
+sub end ();
+sub read ();
+
+$VERSION=1.2023;
+
+sub start ($){
+ croak "rrdtool is already running"
+ if defined $Sequence;
+ $Sequence = 'S';
+ my $rrdtool = shift @_;
+ $RRDpid = open2 \*RRDreadHand,\*RRDwriteHand, $rrdtool,"-"
+ or croak "Can't Start rrdtool: $!";
+ RRDwriteHand->autoflush(); #flush after every write
+ fcntl RRDreadHand, F_SETFL,O_NONBLOCK|O_NDELAY; #make readhandle NON BLOCKING
+ return $RRDpid;
+}
+
+
+sub read () {
+ croak "RRDp::read can only be called after RRDp::cmd"
+ unless $Sequence eq 'C';
+ $RRDp::error = undef;
+ $Sequence = 'R';
+ my $inmask = 0;
+ my $srbuf;
+ my $minibuf;
+ my $buffer;
+ my $nfound;
+ my $timeleft;
+ my $ERR = 0;
+ vec($inmask,fileno(RRDreadHand),1) = 1; # setup select mask for Reader
+ while (1) {
+ my $rout;
+ $nfound = select($rout=$inmask,undef,undef,2);
+ if ($nfound == 0 ) {
+ # here, we could do something sensible ...
+ next;
+ }
+ sysread(RRDreadHand,$srbuf,4096);
+ $minibuf .= $srbuf;
+ while ($minibuf =~ s|^(.+?)\n||s) {
+ my $line = $1;
+ # print $line,"\n";
+ $RRDp::error = undef;
+ if ($line =~ m|^ERROR|) {
+ $RRDp::error_mode eq 'catch' ? $RRDp::error = $line : croak $line;
+ $ERR = 1;
+ }
+ elsif ($line =~ m|^OK u:([\d\.]+) s:([\d\.]+) r:([\d\.]+)|){
+ ($RRDp::sys,$RRDp::user,$RRDp::real)=($1,$2,$3);
+ return $ERR == 1 ? undef : \$buffer;
+ } else {
+ $buffer .= $line. "\n";
+ }
+ }
+ }
+}
+
+sub cmd (@){
+ croak "RRDp::cmd can only be called after RRDp::read or RRDp::start"
+ unless $Sequence eq 'R' or $Sequence eq 'S';
+ $Sequence = 'C';
+ my $cmd = join " ", @_;
+ if ($Sequence ne 'S') {
+ }
+ $cmd =~ s/\n/ /gs;
+ $cmd =~ s/\s/ /gs;
+ print RRDwriteHand "$cmd\n";
+}
+
+sub end (){
+ croak "RRDp::end can only be called after RRDp::start"
+ unless $Sequence;
+ close RRDwriteHand;
+ close RRDreadHand;
+ $Sequence = undef;
+ waitpid $RRDpid,0;
+ return $?
+}
+
+1;
diff --git a/program/bindings/perl-piped/leaktest.pl b/program/bindings/perl-piped/leaktest.pl
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/perl -w
+$ENV{PATH}="/usr/ucb";
+use strict;
+use RRDp;
+my $rrdfile='/tmp/test.rrd';
+RRDp::start '/home/oetiker/data/projects/AABN-rrdtool/src/rrdtool';
+print grep /rrdtool/,`ps au`;
+print grep /rrdtool/,`ps au`;
+my $i=0;
+while ($i<1000) {
+ RRDp::cmd 'info /tmp/test.rrd';
+ $_ = RRDp::read;
+ $i++;
+}
+$_ = RRDp::end;
+print grep /rrdtool/,`ps au`;
diff --git a/program/bindings/perl-piped/rrdpl.dsp b/program/bindings/perl-piped/rrdpl.dsp
--- /dev/null
@@ -0,0 +1,115 @@
+# Microsoft Developer Studio Project File - Name="rrd" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=rrd - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "rrdpl.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "rrdpl.mak" CFG="rrd - Win32 Debug"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "rrd - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "rrd - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "rrd - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
+# ADD CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o NUL /win32
+# ADD BASE RSC /l 0x100c /d "NDEBUG"
+# ADD RSC /l 0x100c /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF "$(CFG)" == "rrd - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "C:\perl\lib\site\auto\RRD\"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c
+# ADD CPP /W3 /I "C:\perl\lib\CORE" /D "WIN32" /D VERSION=\"0.02\" /D XS_VERSION=\"0.02\" /D "_DEBUG" /D "_CONSOLE" /FR -I../src/ -I../gd1.2 RRD.c /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o NUL /win32
+# ADD BASE RSC /l 0x100c /d "_DEBUG"
+# ADD RSC /l 0x100c /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 c:\perl\lib\core\perl.lib ..\src\debug\rrd.lib ..\gd1.2\debug\gd.lib /dll /incremental:no /debug /machine:IX86 /out:"C:\perl\lib\site\auto\RRD\rrd.dll"
+# SUBTRACT LINK32 /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "rrd - Win32 Release"
+# Name "rrd - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\RRD.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\RRD.xs
+
+!IF "$(CFG)" == "rrd - Win32 Release"
+
+!ELSEIF "$(CFG)" == "rrd - Win32 Debug"
+
+# Begin Custom Build
+InputPath=.\RRD.xs
+
+"rrd.c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)"
+ C:\Perl\bin\perl -Ic:\perl\lib -Ic:\perl\lib C:\perl\lib\ExtUtils/xsubpp\
+ -typemap C:\perl\lib\ExtUtils\typemap RRD.xs >RRD.tc && C:\Perl\bin\perl\
+ -Ic:\perl\lib -Ic:\perl\lib -MExtUtils::Command -e mv RRD.tc RRD.c
+
+# End Custom Build
+
+!ENDIF
+
+# End Source File
+# End Target
+# End Project
diff --git a/program/bindings/perl-piped/rrdpl.dsw b/program/bindings/perl-piped/rrdpl.dsw
--- /dev/null
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 5.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "rrd"=".\rrd.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/program/bindings/perl-piped/t/base.t b/program/bindings/perl-piped/t/base.t
--- /dev/null
@@ -0,0 +1,42 @@
+#! /usr/bin/perl
+
+# this exercises just the perl module .. not RRDtool as such ...
+
+BEGIN { $| = 1; print "1..5\n"; }
+END {
+ print "not ok 1\n" unless $loaded;
+ unlink "demo.rrd";
+}
+
+sub ok
+{
+ $ok_count++;
+ my($what, $result) = @_ ;
+ print "not " unless $result;
+ print "ok $ok_count $what\n";
+}
+
+use RRDp;
+
+$loaded = 1;
+$ok_count = 1;
+
+print "ok 1 module load\n";
+
+ok("RRDp::start", RRDp::start "../../src/rrdtool" > 0);
+
+$now=time();
+RRDp::cmd qw(create demo.rrd --start ), $now, qw(--step 100 ),
+ qw( DS:in:GAUGE:100:U:U RRA:AVERAGE:0.5:1:10 );
+
+$answer = RRDp::read;
+ok("RRDp::cmd", -s "demo.rrd" );
+
+RRDp::cmd qw(last demo.rrd);
+$answer = RRDp::read;
+
+ok("RRDp::read", $$answer =~ /$now/);
+
+$status = RRDp::end;
+
+ok("RRDp::end", $status == 0);
diff --git a/program/bindings/perl-shared/MANIFEST b/program/bindings/perl-shared/MANIFEST
--- /dev/null
@@ -0,0 +1,7 @@
+ntmake.pl
+MANIFEST
+README
+Makefile.PL
+RRDs.pm
+RRDs.xs
+t/base.t
diff --git a/program/bindings/perl-shared/Makefile.PL b/program/bindings/perl-shared/Makefile.PL
--- /dev/null
@@ -0,0 +1,37 @@
+use ExtUtils::MakeMaker;
+use Config;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+
+# if the last argument when calling Makefile.PL is RPATH=/... and ... is the
+# path to librrd.so then the Makefile will be written such that RRDs.so knows
+# where to find librrd.so later on ...
+my $R="";
+if ($ARGV[-1] =~ /RPATH=(\S+)/){
+ pop @ARGV;
+ my $rp = $1;
+ for ($^O){
+ /linux/ && do{ $R = "-Wl,--rpath -Wl,$rp"};
+ /hpux/ && do{ $R = "+b$rp"};
+ /solaris/ && do{ $R = "-R$rp"};
+ /aix/ && do{ $R = "-Wl,-blibpath:$rp"};
+ }
+}
+
+# darwin works without this because librrd contains its
+# install_name which will includes the final location of the
+# library after it is installed. This install_name gets transfered
+# to the perl shared object.
+
+my $librrd = "-L../../src/.libs/ $R -lrrd";
+
+WriteMakefile(
+ 'NAME' => 'RRDs',
+ 'VERSION_FROM' => 'RRDs.pm', # finds $VERSION
+ 'DEFINE' => "-DPERLPATCHLEVEL=$Config{PATCHLEVEL}",
+ 'INC' => '-I../../src',
+ # Perl will figure out which one is valid
+ 'dynamic_lib' => {'OTHERLDFLAGS' => "$librrd -lm"},
+ 'realclean' => {FILES => 't/demo?.rrd t/demo?.png' }
+);
+
diff --git a/program/bindings/perl-shared/README b/program/bindings/perl-shared/README
--- /dev/null
@@ -0,0 +1,12 @@
+These are the Perl bindings for rrdtool as a shared library. To compile do
+the following:
+
+perl Makefile.PL
+make test
+
+(win32 users try perl ntmake.pl)
+
+* if dynamic linking does not work, try
+
+perl Makefile.PL LINKTYPE=static
+make test
diff --git a/program/bindings/perl-shared/RRDs.pm b/program/bindings/perl-shared/RRDs.pm
--- /dev/null
@@ -0,0 +1,145 @@
+package RRDs;
+
+use strict;
+use vars qw(@ISA $VERSION);
+
+@ISA = qw(DynaLoader);
+
+require DynaLoader;
+
+$VERSION=1.2023;
+
+bootstrap RRDs $VERSION;
+
+1;
+__END__
+
+=head1 NAME
+
+RRDs - Access RRDtool as a shared module
+
+=head1 SYNOPSIS
+
+ use RRDs;
+ RRDs::error
+ RRDs::last ...
+ RRDs::info ...
+ RRDs::create ...
+ RRDs::update ...
+ RRDs::updatev ...
+ RRDs::graph ...
+ RRDs::fetch ...
+ RRDs::tune ...
+ RRDs::times(start, end)
+ RRDs::dump ...
+ RRDs::restore ...
+
+=head1 DESCRIPTION
+
+=head2 Calling Sequence
+
+This module accesses RRDtool functionality directly from within perl. The
+arguments to the functions listed in the SYNOPSIS are explained in the regular
+RRDtool documentation. The commandline call
+
+ rrdtool update mydemo.rrd --template in:out N:12:13
+
+gets turned into
+
+ RRDs::update ("mydemo.rrd", "--template", "in:out", "N:12:13");
+
+Note that
+
+ --template=in:out
+
+is also valid.
+
+The RRDs::times function takes two parameters: a "start" and "end" time.
+These should be specified in the B<AT-STYLE TIME SPECIFICATION> format
+used by RRDtool. See the B<rrdfetch> documentation for a detailed
+explanation on how to specify time.
+
+=head2 Error Handling
+
+The RRD functions will not abort your program even when they can not make
+sense out of the arguments you fed them.
+
+The function RRDs::error should be called to get the error status
+after each function call. If RRDs::error does not return anything
+then the previous function has completed its task successfully.
+
+ use RRDs;
+ RRDs::update ("mydemo.rrd","N:12:13");
+ my $ERR=RRDs::error;
+ die "ERROR while updating mydemo.rrd: $ERR\n" if $ERR;
+
+=head2 Return Values
+
+The functions RRDs::last, RRDs::graph, RRDs::info, RRDs::fetch and RRDs::times
+return their findings.
+
+B<RRDs::last> returns a single INTEGER representing the last update time.
+
+ $lastupdate = RRDs::last ...
+
+B<RRDs::graph> returns an pointer to an ARRAY containing the x-size and y-size of the
+created image and results of the PRINT arguments.
+
+ ($averages,$xsize,$ysize) = RRDs::graph ...
+ print "Imagesize: ${xsize}x${ysize}\n";
+ print "Averages: ", (join ", ", @$averages);
+
+B<RRDs::info> returns a pointer to a hash. The keys of the hash
+represent the property names of the RRD and the values of the hash are
+the values of the properties.
+
+ $hash = RRDs::info "example.rrd";
+ foreach my $key (keys %$hash){
+ print "$key = $$hash{$key}\n";
+ }
+
+B<RRDs::updatev> also returns a pointer to hash. The keys of the hash
+are concatenated strings of a timestamp, RRA index, and data source name for
+each consolidated data point (CDP) written to disk as a result of the
+current update call. The hash values are CDP values.
+
+B<RRDs::fetch> is the most complex of
+the pack regarding return values. There are 4 values. Two normal
+integers, a pointer to an array and a pointer to a array of pointers.
+
+ my ($start,$step,$names,$data) = RRDs::fetch ...
+ print "Start: ", scalar localtime($start), " ($start)\n";
+ print "Step size: $step seconds\n";
+ print "DS names: ", join (", ", @$names)."\n";
+ print "Data points: ", $#$data + 1, "\n";
+ print "Data:\n";
+ foreach my $line (@$data) {
+ print " ", scalar localtime($start), " ($start) ";
+ $start += $step;
+ foreach my $val (@$line) {
+ printf "%12.1f ", $val;
+ }
+ print "\n";
+ }
+
+B<RRDs::times> returns two integers which are the number of seconds since
+epoch (1970-01-01) for the supplied "start" and "end" arguments, respectively.
+
+See the examples directory for more ways to use this extension.
+
+=head1 NOTE
+
+If you are manipulating the TZ variable you should also call the posixs
+function tzset to initialize all internal state of the library for properly
+operating in the timezone of your choice.
+
+ use POSIX qw(tzset);
+ $ENV{TZ} = 'CET';
+ POSIX::tzset();
+
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
+=cut
diff --git a/program/bindings/perl-shared/RRDs.ppd b/program/bindings/perl-shared/RRDs.ppd
--- /dev/null
@@ -0,0 +1,10 @@
+<SOFTPKG NAME="RRDs" VERSION="1,100001,0,0">
+ <TITLE>RRDs</TITLE>
+ <ABSTRACT>Round Robin Database Tool</ABSTRACT>
+ <AUTHOR>Tobias Oetiker (tobi@oetiker.ch)</AUTHOR>
+ <IMPLEMENTATION>
+ <OS NAME="MSWin32" />
+ <ARCHITECTURE NAME="MSWin32-x86-multi-thread-5.8" />
+ <CODEBASE HREF="RRDs.tar.gz" />
+ </IMPLEMENTATION>
+</SOFTPKG>
diff --git a/program/bindings/perl-shared/RRDs.xs b/program/bindings/perl-shared/RRDs.xs
--- /dev/null
@@ -0,0 +1,433 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+/*
+ * rrd_tool.h includes config.h, but at least on Ubuntu Breezy Badger
+ * 5.10 with gcc 4.0.2, the C preprocessor picks up Perl's config.h
+ * which is included from the Perl includes and never reads rrdtool's
+ * config.h. Without including rrdtool's config.h, this module does
+ * not compile, so include it here with an explicit path.
+ *
+ * Because rrdtool's config.h redefines VERSION which is originally
+ * set via Perl's Makefile.PL and passed down to the C compiler's
+ * command line, save the original value and reset it after the
+ * includes.
+ */
+#define VERSION_SAVED VERSION
+#undef VERSION
+#include "../../rrd_config.h"
+#include "../../src/rrd_tool.h"
+#undef VERSION
+#define VERSION VERSION_SAVED
+#undef VERSION_SAVED
+
+/* perl 5.004 compatibility */
+#if PERLPATCHLEVEL < 5
+#define PL_sv_undef sv_undef
+#endif
+
+
+#define rrdcode(name) \
+ argv = (char **) malloc((items+1)*sizeof(char *));\
+ argv[0] = "dummy";\
+ for (i = 0; i < items; i++) { \
+ STRLEN len; \
+ char *handle= SvPV(ST(i),len);\
+ /* actually copy the data to make sure possible modifications \
+ on the argv data does not backfire into perl */ \
+ argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char)); \
+ strcpy(argv[i+1],handle); \
+ } \
+ rrd_clear_error();\
+ RETVAL=name(items+1,argv); \
+ for (i=0; i < items; i++) {\
+ free(argv[i+1]);\
+ } \
+ free(argv);\
+ \
+ if (rrd_test_error()) XSRETURN_UNDEF;
+
+#define hvs(VAL) hv_store_ent(hash, sv_2mortal(newSVpv(data->key,0)),VAL,0)
+
+#define rrdinfocode(name) \
+ /* prepare argument list */ \
+ argv = (char **) malloc((items+1)*sizeof(char *)); \
+ argv[0] = "dummy"; \
+ for (i = 0; i < items; i++) { \
+ STRLEN len; \
+ char *handle= SvPV(ST(i),len); \
+ /* actually copy the data to make sure possible modifications \
+ on the argv data does not backfire into perl */ \
+ argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char)); \
+ strcpy(argv[i+1],handle); \
+ } \
+ rrd_clear_error(); \
+ data=name(items+1, argv); \
+ for (i=0; i < items; i++) { \
+ free(argv[i+1]); \
+ } \
+ free(argv); \
+ if (rrd_test_error()) XSRETURN_UNDEF; \
+ hash = newHV(); \
+ while (data) { \
+ save=data; \
+ /* the newSV will get copied by hv so we create it as a mortal \
+ to make sure it does not keep hanging round after the fact */ \
+ switch (data->type) { \
+ case RD_I_VAL: \
+ if (isnan(data->value.u_val)) \
+ hvs(&PL_sv_undef); \
+ else \
+ hvs(newSVnv(data->value.u_val)); \
+ break; \
+ case RD_I_INT: \
+ hvs(newSViv(data->value.u_int)); \
+ break; \
+ case RD_I_CNT: \
+ hvs(newSViv(data->value.u_cnt)); \
+ break; \
+ case RD_I_STR: \
+ hvs(newSVpv(data->value.u_str,0)); \
+ rrd_freemem(data->value.u_str); \
+ break; \
+ } \
+ rrd_freemem(data->key); \
+ data = data->next; \
+ rrd_freemem(save); \
+ } \
+ rrd_freemem(data); \
+ RETVAL = newRV_noinc((SV*)hash);
+
+/*
+ * should not be needed if libc is linked (see ntmake.pl)
+#ifdef WIN32
+ #define free free
+ #define malloc malloc
+ #define realloc realloc
+#endif
+*/
+
+
+MODULE = RRDs PACKAGE = RRDs PREFIX = rrd_
+
+BOOT:
+#ifdef MUST_DISABLE_SIGFPE
+ signal(SIGFPE,SIG_IGN);
+#endif
+#ifdef MUST_DISABLE_FPMASK
+ fpsetmask(0);
+#endif
+
+
+SV*
+rrd_error()
+ CODE:
+ if (! rrd_test_error()) XSRETURN_UNDEF;
+ RETVAL = newSVpv(rrd_get_error(),0);
+ OUTPUT:
+ RETVAL
+
+
+int
+rrd_last(...)
+ PROTOTYPE: @
+ PREINIT:
+ int i;
+ char **argv;
+ CODE:
+ rrdcode(rrd_last);
+ OUTPUT:
+ RETVAL
+
+int
+rrd_first(...)
+ PROTOTYPE: @
+ PREINIT:
+ int i;
+ char **argv;
+ CODE:
+ rrdcode(rrd_first);
+ OUTPUT:
+ RETVAL
+
+
+int
+rrd_create(...)
+ PROTOTYPE: @
+ PREINIT:
+ int i;
+ char **argv;
+ CODE:
+ rrdcode(rrd_create);
+ RETVAL = 1;
+ OUTPUT:
+ RETVAL
+
+
+int
+rrd_update(...)
+ PROTOTYPE: @
+ PREINIT:
+ int i;
+ char **argv;
+ CODE:
+ rrdcode(rrd_update);
+ RETVAL = 1;
+ OUTPUT:
+ RETVAL
+
+
+int
+rrd_tune(...)
+ PROTOTYPE: @
+ PREINIT:
+ int i;
+ char **argv;
+ CODE:
+ rrdcode(rrd_tune);
+ RETVAL = 1;
+ OUTPUT:
+ RETVAL
+
+
+void
+rrd_graph(...)
+ PROTOTYPE: @
+ PREINIT:
+ char **calcpr=NULL;
+ int i,xsize,ysize;
+ double ymin,ymax;
+ char **argv;
+ AV *retar;
+ PPCODE:
+ argv = (char **) malloc((items+1)*sizeof(char *));
+ argv[0] = "dummy";
+ for (i = 0; i < items; i++) {
+ STRLEN len;
+ char *handle = SvPV(ST(i),len);
+ /* actually copy the data to make sure possible modifications
+ on the argv data does not backfire into perl */
+ argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char));
+ strcpy(argv[i+1],handle);
+ }
+ rrd_clear_error();
+ rrd_graph(items+1,argv,&calcpr,&xsize,&ysize,NULL,&ymin,&ymax);
+ for (i=0; i < items; i++) {
+ free(argv[i+1]);
+ }
+ free(argv);
+
+ if (rrd_test_error()) {
+ if(calcpr)
+ for(i=0;calcpr[i];i++)
+ rrd_freemem(calcpr[i]);
+ XSRETURN_UNDEF;
+ }
+ retar=newAV();
+ if(calcpr){
+ for(i=0;calcpr[i];i++){
+ av_push(retar,newSVpv(calcpr[i],0));
+ rrd_freemem(calcpr[i]);
+ }
+ rrd_freemem(calcpr);
+ }
+ EXTEND(sp,4);
+ PUSHs(sv_2mortal(newRV_noinc((SV*)retar)));
+ PUSHs(sv_2mortal(newSViv(xsize)));
+ PUSHs(sv_2mortal(newSViv(ysize)));
+
+void
+rrd_fetch(...)
+ PROTOTYPE: @
+ PREINIT:
+ time_t start,end;
+ unsigned long step, ds_cnt,i,ii;
+ rrd_value_t *data,*datai;
+ char **argv;
+ char **ds_namv;
+ AV *retar,*line,*names;
+ PPCODE:
+ argv = (char **) malloc((items+1)*sizeof(char *));
+ argv[0] = "dummy";
+ for (i = 0; i < items; i++) {
+ STRLEN len;
+ char *handle= SvPV(ST(i),len);
+ /* actually copy the data to make sure possible modifications
+ on the argv data does not backfire into perl */
+ argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char));
+ strcpy(argv[i+1],handle);
+ }
+ rrd_clear_error();
+ rrd_fetch(items+1,argv,&start,&end,&step,&ds_cnt,&ds_namv,&data);
+ for (i=0; i < items; i++) {
+ free(argv[i+1]);
+ }
+ free(argv);
+ if (rrd_test_error()) XSRETURN_UNDEF;
+ /* convert the ds_namv into perl format */
+ names=newAV();
+ for (ii = 0; ii < ds_cnt; ii++){
+ av_push(names,newSVpv(ds_namv[ii],0));
+ rrd_freemem(ds_namv[ii]);
+ }
+ rrd_freemem(ds_namv);
+ /* convert the data array into perl format */
+ datai=data;
+ retar=newAV();
+ for (i = start+step; i <= end; i += step){
+ line = newAV();
+ for (ii = 0; ii < ds_cnt; ii++){
+ av_push(line,(isnan(*datai) ? &PL_sv_undef : newSVnv(*datai)));
+ datai++;
+ }
+ av_push(retar,newRV_noinc((SV*)line));
+ }
+ rrd_freemem(data);
+ EXTEND(sp,5);
+ PUSHs(sv_2mortal(newSViv(start+step)));
+ PUSHs(sv_2mortal(newSViv(step)));
+ PUSHs(sv_2mortal(newRV_noinc((SV*)names)));
+ PUSHs(sv_2mortal(newRV_noinc((SV*)retar)));
+
+void
+rrd_times(start, end)
+ char *start
+ char *end
+ PREINIT:
+ struct rrd_time_value start_tv, end_tv;
+ char *parsetime_error = NULL;
+ time_t start_tmp, end_tmp;
+ PPCODE:
+ rrd_clear_error();
+ if( (parsetime_error = parsetime( start, &start_tv))) {
+ rrd_set_error( "start time: %s", parsetime_error);
+ XSRETURN_UNDEF;
+ }
+ if( (parsetime_error = parsetime( end, &end_tv))) {
+ rrd_set_error( "end time: %s", parsetime_error);
+ XSRETURN_UNDEF;
+ }
+ if( proc_start_end( &start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
+ XSRETURN_UNDEF;
+ }
+ EXTEND(sp,2);
+ PUSHs(sv_2mortal(newSVuv(start_tmp)));
+ PUSHs(sv_2mortal(newSVuv(end_tmp)));
+
+int
+rrd_xport(...)
+ PROTOTYPE: @
+ PREINIT:
+ time_t start,end;
+ int xsize;
+ unsigned long step, col_cnt,row_cnt,i,ii;
+ rrd_value_t *data,*ptr;
+ char **argv,**legend_v;
+ AV *retar,*line,*names;
+ PPCODE:
+ argv = (char **) malloc((items+1)*sizeof(char *));
+ argv[0] = "dummy";
+ for (i = 0; i < items; i++) {
+ STRLEN len;
+ char *handle = SvPV(ST(i),len);
+ /* actually copy the data to make sure possible modifications
+ on the argv data does not backfire into perl */
+ argv[i+1] = (char *) malloc((strlen(handle)+1)*sizeof(char));
+ strcpy(argv[i+1],handle);
+ }
+ rrd_clear_error();
+ rrd_xport(items+1,argv,&xsize,&start,&end,&step,&col_cnt,&legend_v,&data);
+ for (i=0; i < items; i++) {
+ free(argv[i+1]);
+ }
+ free(argv);
+ if (rrd_test_error()) XSRETURN_UNDEF;
+
+ /* convert the legend_v into perl format */
+ names=newAV();
+ for (ii = 0; ii < col_cnt; ii++){
+ av_push(names,newSVpv(legend_v[ii],0));
+ rrd_freemem(legend_v[ii]);
+ }
+ rrd_freemem(legend_v);
+
+ /* convert the data array into perl format */
+ ptr=data;
+ retar=newAV();
+ for (i = start+step; i <= end; i += step){
+ line = newAV();
+ for (ii = 0; ii < col_cnt; ii++){
+ av_push(line,(isnan(*ptr) ? &PL_sv_undef : newSVnv(*ptr)));
+ ptr++;
+ }
+ av_push(retar,newRV_noinc((SV*)line));
+ }
+ rrd_freemem(data);
+
+ EXTEND(sp,7);
+ PUSHs(sv_2mortal(newSViv(start+step)));
+ PUSHs(sv_2mortal(newSViv(end)));
+ PUSHs(sv_2mortal(newSViv(step)));
+ PUSHs(sv_2mortal(newSViv(col_cnt)));
+ PUSHs(sv_2mortal(newRV_noinc((SV*)names)));
+ PUSHs(sv_2mortal(newRV_noinc((SV*)retar)));
+
+SV*
+rrd_info(...)
+ PROTOTYPE: @
+ PREINIT:
+ info_t *data,*save;
+ int i;
+ char **argv;
+ HV *hash;
+ CODE:
+ rrdinfocode(rrd_info);
+ OUTPUT:
+ RETVAL
+
+SV*
+rrd_updatev(...)
+ PROTOTYPE: @
+ PREINIT:
+ info_t *data,*save;
+ int i;
+ char **argv;
+ HV *hash;
+ CODE:
+ rrdinfocode(rrd_update_v);
+ OUTPUT:
+ RETVAL
+
+int
+rrd_dump(...)
+ PROTOTYPE: @
+ PREINIT:
+ int i;
+ char **argv;
+ CODE:
+ rrdcode(rrd_dump);
+ RETVAL = 1;
+ OUTPUT:
+ RETVAL
+
+int
+rrd_restore(...)
+ PROTOTYPE: @
+ PREINIT:
+ int i;
+ char **argv;
+ CODE:
+ rrdcode(rrd_restore);
+ RETVAL = 1;
+ OUTPUT:
+ RETVAL
+
diff --git a/program/bindings/perl-shared/ntmake.pl b/program/bindings/perl-shared/ntmake.pl
--- /dev/null
@@ -0,0 +1,27 @@
+use ExtUtils::MakeMaker;
+use Config;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+# Run VCVARS32.BAT before generating makefile/compiling.
+WriteMakefile(
+ 'NAME' => 'RRDs',
+ 'VERSION_FROM' => 'RRDs.pm',
+# 'DEFINE' => "-DPERLPATCHLEVEL=$Config{PATCHLEVEL}",
+# keep compatible w/ ActiveState 5xx builds
+ 'DEFINE' => "-DPERLPATCHLEVEL=5",
+
+ 'INC' => '-I../../src/ "-I/Program Files/GnuWin32/include"',
+# Since we are now using GnuWin32 libraries dynamically (instead of static
+# complile with code redistributed with rrdtool), use /MD instead of /MT.
+# Yes, this means we need msvcrt.dll but GnuWin32 dlls already require it
+# and it is available on most versions of Windows.
+ 'OPTIMIZE' => '-O2 -MD',
+ 'LIBS' => '../../src/release/rrd.lib "/Program Files/GnuWin32/lib/libart_lgpl.lib" "/Program Files/GnuWin32/lib/libz.lib" "/Program Files/GnuWin32/lib/libpng.lib" "/Program Files/GnuWin32/lib/libfreetype.lib"',
+ 'realclean' => {FILES => 't/demo?.rrd t/demo?.png' },
+ ($] ge '5.005') ? (
+ 'AUTHOR' => 'Tobias Oetiker (tobi@oetiker.ch)',
+ 'ABSTRACT' => 'Round Robin Database Tool',
+ ) : ()
+
+
+);
diff --git a/program/bindings/perl-shared/t/base.t b/program/bindings/perl-shared/t/base.t
--- /dev/null
@@ -0,0 +1,171 @@
+#! /usr/bin/perl
+
+BEGIN { $| = 1; print "1..7\n"; }
+END {
+ print "not ok 1\n" unless $loaded;
+ unlink "demo.rrd";
+}
+
+sub ok
+{
+ my($what, $result) = @_ ;
+ $ok_count++;
+ print "not " unless $result;
+ print "ok $ok_count $what\n";
+}
+
+use strict;
+use vars qw(@ISA $loaded);
+
+use RRDs;
+$loaded = 1;
+my $ok_count = 1;
+
+ok("loading",1);
+
+######################### End of black magic.
+
+my $STEP = 100;
+my $RUNS = 500;
+my $GRUNS = 4;
+my $RRD1 = "demo1.rrd";
+my $RRD2 = "demo2.rrd";
+my $PNG1 = "demo1.png";
+my $PNG2 = "demo2.png";
+my $time = 30*int(time/30);
+my $START = $time-$RUNS*$STEP;
+
+my @options = ("-b", $START, "-s", $STEP,
+ "DS:a:GAUGE:2000:U:U",
+ "DS:b:GAUGE:200:U:U",
+ "DS:c:GAUGE:200:U:U",
+ "DS:d:GAUGE:200:U:U",
+ "DS:e:DERIVE:200:U:U",
+ "RRA:AVERAGE:0.5:1:5000",
+ "RRA:AVERAGE:0.5:10:500");
+
+print "* Creating RRD $RRD1 starting at $time.\n\n";
+RRDs::create $RRD1, @options;
+
+my $ERROR = RRDs::error;
+ok("create 1", !$ERROR); # 2
+if ($ERROR) {
+ die "$0: unable to create `$RRD1': $ERROR\n";
+}
+
+print "* Creating RRD $RRD2 starting at $time.\n\n";
+RRDs::create $RRD2, @options;
+
+$ERROR= RRDs::error;
+ok("create 2",!$ERROR); # 3
+if ($ERROR) {
+ die "$0: unable to create `$RRD2': $ERROR\n";
+}
+
+my $last = RRDs::last $RRD1;
+if ($ERROR = RRDs::error) {
+ die "$0: unable to get last `$RRD1': $ERROR\n";
+}
+ok("last 1", $last == $START); # 4
+
+$last = RRDs::last $RRD2;
+if ($ERROR = RRDs::error) {
+ die "$0: unable to get last `$RRD2': $ERROR\n";
+}
+ok("last 2", $last == $START); # 5
+
+print "* Filling $RRD1 and $RRD2 with $RUNS*5 values. One moment please ...\n";
+print "* If you are running over NFS this will take *MUCH* longer\n\n";
+
+srand(int($time / 100));
+
+@options = ();
+
+my $counter = 1e7;
+for (my $t=$START+1;
+ $t<$START+$STEP*$RUNS;
+ $t+=$STEP+int((rand()-0.5)*7)){
+ $counter += int(2500*sin($t/2000)*$STEP);
+ my $data = (1000+500*sin($t/1000)).":".
+ (1000+900*sin($t/2330)).":".
+ (2000*cos($t/1550)).":".
+ (3220*sin($t/3420)).":$counter";
+ push(@options, "$t:$data");
+ RRDs::update $RRD1, "$t:$data";
+ if ($ERROR = RRDs::error) {
+ warn "$0: unable to update `$RRD1': $ERROR\n";
+ }
+}
+
+ok("update 1",!$ERROR); # 3
+
+RRDs::update $RRD2, @options;
+
+ok("update 2",!$ERROR); # 3
+
+if ($ERROR = RRDs::error) {
+ die "$0: unable to update `$RRD2': $ERROR\n";
+}
+
+print "* Creating $GRUNS graphs: $PNG1 & $PNG2\n\n";
+my $now = $time;
+for (my $i=0;$i<$GRUNS;$i++) {
+ my @rrd_pngs = ($RRD1, $PNG1, $RRD2, $PNG2);
+ while (@rrd_pngs) {
+ my $RRD = shift(@rrd_pngs);
+ my $PNG = shift(@rrd_pngs);
+ my ($graphret,$xs,$ys) = RRDs::graph $PNG, "--title", 'Test GRAPH',
+ "--vertical-label", 'Dummy Units', "--start", (-$RUNS*$STEP),
+ "DEF:alpha=$RRD:a:AVERAGE",
+ "DEF:beta=$RRD:b:AVERAGE",
+ "DEF:gamma=$RRD:c:AVERAGE",
+ "DEF:delta=$RRD:d:AVERAGE",
+ "DEF:epsilon=$RRD:e:AVERAGE",
+ "CDEF:calc=alpha,beta,+,2,/",
+ "AREA:alpha#0022e9:Short",
+ "STACK:beta#00b871:Demo Text",
+ "LINE1:gamma#ff0000:Line 1",
+ "LINE2:delta#888800:Line 2",
+ "LINE3:calc#00ff44:Line 3",
+ "LINE3:epsilon#000000:Line 4",
+ "HRULE:1500#ff8800:Horizontal Line at 1500",
+ "PRINT:alpha:AVERAGE:Average Alpha %1.2lf",
+ "PRINT:alpha:MIN:Min Alpha %1.2lf %s",
+ "PRINT:alpha:MIN:Min Alpha %1.2lf",
+ "PRINT:alpha:MAX:Max Alpha %1.2lf",
+ "GPRINT:calc:AVERAGE:Average calc %1.2lf %s",
+ "GPRINT:calc:AVERAGE:Average calc %1.2lf",
+ "GPRINT:calc:MAX:Max calc %1.2lf",
+ "GPRINT:calc:MIN:Min calc %1.2lf",
+ "VRULE:".($now-3600)."#008877:60 Minutes ago",
+ "VRULE:".($now-7200)."#008877:120 Minutes ago";
+
+ if ($ERROR = RRDs::error) {
+ print "ERROR: $ERROR\n";
+ } else {
+ print "Image Size: ${xs}x${ys}\n";
+ print "Graph Return:\n",(join "\n", @$graphret),"\n\n";
+ }
+ }
+}
+
+
+
+my ($start,$step,$names,$array) = RRDs::fetch $RRD1, "AVERAGE";
+$ERROR = RRDs::error;
+print "ERROR: $ERROR\n" if $ERROR ;
+print "start=$start, step=$step\n";
+print " ";
+map {printf("%12s",$_)} @$names ;
+foreach my $line (@$array){
+ print "".localtime($start)," ";
+ $start += $step;
+ foreach my $val (@$line) {
+ if (not defined $val){
+ printf "%12s", "UNKNOWN";
+ } else {
+ printf "%12.1f", $val;
+ }
+ }
+ print "\n";
+}
diff --git a/program/bindings/python/ACKNOWLEDGEMENT b/program/bindings/python/ACKNOWLEDGEMENT
--- /dev/null
@@ -0,0 +1,7 @@
+ACKNOWLEDGMENT
+==============
+
+This is a list of people who have made contributions to py-rrdtool.
+
+Matthew W. Samsonoff <mws@rochester.rr.com>
+Brian E. Gallew <geek+python@cmu.edu>
diff --git a/program/bindings/python/AUTHORS b/program/bindings/python/AUTHORS
--- /dev/null
@@ -0,0 +1 @@
+Hye-Shik Chang <perky@fallin.lv>
diff --git a/program/bindings/python/COPYING b/program/bindings/python/COPYING
--- /dev/null
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 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.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+\f
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+\f
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+\f
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+\f
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/program/bindings/python/README b/program/bindings/python/README
--- /dev/null
@@ -0,0 +1,28 @@
+Python-RRDtool 0.2.1
+--------------------
+
+The python-rrdtool provides a interface to rrdtool, the wonderful
+graphing and logging utility. This wrapper implementation has
+worked from the scratch (without SWIG), and it's under LGPL.
+
+This module have not documented yet. Please refer pydoc.
+
+
+Project
+-------
+
+Homepage: http://www.nongnu.org/py-rrdtool/
+
+Mailing Lists:
+
+ CVS Checkins py-rrdtool-cvs@nongnu.org
+ Users & Hackers py-rrdtool-users@nongnu.org
+
+
+Author
+------
+
+Hye-Shik Chang <perky@FreeBSD.org>
+
+Any comments, suggestions, and/or patches are very welcome.
+Thank you for using py-rrdtool!
diff --git a/program/bindings/python/rrd_extra.h b/program/bindings/python/rrd_extra.h
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * This file is part of RRDtool.
+ *
+ * RRDtool 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.
+ *
+ * RRDtool 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 Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*****************************************************************************
+ * RRDtool 1.0.37 Copyright Tobias Oetiker, 1997 - 2000
+ *****************************************************************************
+ * rrd_tool.h Common Header File
+ *****************************************************************************
+ * Id: rrd_tool.h,v 1.1.1.1 2002/02/26 10:21:37 oetiker Exp
+ * Log: rrd_tool.h,v
+ * Revision 1.1.1.1 2002/02/26 10:21:37 oetiker
+ * Intial Import
+ *
+ *****************************************************************************/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _RRD_EXTRA_H
+#define _RRD_EXTRA_H
+
+#include "rrd_format.h"
+
+#ifndef WIN32
+#ifndef isnan /* POSIX */
+int isnan(double value);
+#endif
+#else /* Windows only */
+#include <float.h>
+#define isnan _isnan
+#endif
+
+void rrd_free(rrd_t *rrd);
+void rrd_init(rrd_t *rrd);
+
+int rrd_open(char *file_name, FILE **in_file, rrd_t *rrd, int rdwr);
+int readfile(char *file, char **buffer, int skipfirst);
+
+#define RRD_READONLY 0
+#define RRD_READWRITE 1
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/program/bindings/python/rrdtoolmodule.c b/program/bindings/python/rrdtoolmodule.c
--- /dev/null
@@ -0,0 +1,542 @@
+/*
+ * rrdtoolmodule.c
+ *
+ * RRDTool Python binding
+ *
+ * Author : Hye-Shik Chang <perky@fallin.lv>
+ * Date : $Date: 2003/02/22 07:41:19 $
+ * Created : 23 May 2002
+ *
+ * $Revision: 1.14 $
+ *
+ * ==========================================================================
+ * This file is part of py-rrdtool.
+ *
+ * py-rrdtool 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 of the License, or
+ * (at your option) any later version.
+ *
+ * py-rrdtool 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 Foobar; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifdef UNUSED
+#elif defined(__GNUC__)
+# define UNUSED(x) x __attribute__((unused))
+#elif defined(__LCLINT__)
+# define UNUSED(x) /*@unused@*/ x
+#else
+# define UNUSED(x) x
+#endif
+
+static const char *__version__ = "$Revision: 1.14 $";
+
+#include "Python.h"
+#include "rrd.h"
+#include "rrd_extra.h"
+
+static PyObject *ErrorObject;
+extern int optind;
+extern int opterr;
+
+/* forward declaration to keep compiler happy */
+void initrrdtool(void);
+
+static int
+create_args(char *command, PyObject *args, int *argc, char ***argv)
+{
+ PyObject *o;
+ int size, i;
+
+ size = PyTuple_Size(args);
+ *argv = PyMem_New(char *, size + 1);
+ if (*argv == NULL)
+ return -1;
+
+ for (i = 0; i < size; i++) {
+ o = PyTuple_GET_ITEM(args, i);
+ if (PyString_Check(o))
+ (*argv)[i + 1] = PyString_AS_STRING(o);
+ else {
+ PyMem_Del(*argv);
+ PyErr_Format(PyExc_TypeError, "argument %d must be string", i);
+ return -1;
+ }
+ }
+ (*argv)[0] = command;
+ *argc = size + 1;
+
+ /* reset getopt state */
+ opterr = optind = 0;
+
+ return 0;
+}
+
+static void
+destroy_args(char ***argv)
+{
+ PyMem_Del(*argv);
+ *argv = NULL;
+}
+
+static char PyRRD_create__doc__[] =
+"create(args..): Set up a new Round Robin Database\n\
+ create filename [--start|-b start time] \
+[--step|-s step] [DS:ds-name:DST:heartbeat:min:max] \
+[RRA:CF:xff:steps:rows]";
+
+static PyObject *
+PyRRD_create(PyObject UNUSED(*self), PyObject *args)
+{
+ PyObject *r;
+ char **argv;
+ int argc;
+
+ if (create_args("create", args, &argc, &argv) < 0)
+ return NULL;
+
+ if (rrd_create(argc, argv) == -1) {
+ PyErr_SetString(ErrorObject, rrd_get_error());
+ rrd_clear_error();
+ r = NULL;
+ } else {
+ Py_INCREF(Py_None);
+ r = Py_None;
+ }
+
+ destroy_args(&argv);
+ return r;
+}
+
+static char PyRRD_update__doc__[] =
+"update(args..): Store a new set of values into the rrd\n"
+" update filename [--template|-t ds-name[:ds-name]...] "
+"N|timestamp:value[:value...] [timestamp:value[:value...] ...]";
+
+static PyObject *
+PyRRD_update(PyObject UNUSED(*self), PyObject *args)
+{
+ PyObject *r;
+ char **argv;
+ int argc;
+
+ if (create_args("update", args, &argc, &argv) < 0)
+ return NULL;
+
+ if (rrd_update(argc, argv) == -1) {
+ PyErr_SetString(ErrorObject, rrd_get_error());
+ rrd_clear_error();
+ r = NULL;
+ } else {
+ Py_INCREF(Py_None);
+ r = Py_None;
+ }
+
+ destroy_args(&argv);
+ return r;
+}
+
+static char PyRRD_fetch__doc__[] =
+"fetch(args..): fetch data from an rrd.\n"
+" fetch filename CF [--resolution|-r resolution] "
+"[--start|-s start] [--end|-e end]";
+
+static PyObject *
+PyRRD_fetch(PyObject UNUSED(*self), PyObject *args)
+{
+ PyObject *r;
+ rrd_value_t *data, *datai;
+ unsigned long step, ds_cnt;
+ time_t start, end;
+ int argc;
+ char **argv, **ds_namv;
+
+ if (create_args("fetch", args, &argc, &argv) < 0)
+ return NULL;
+
+ if (rrd_fetch(argc, argv, &start, &end, &step,
+ &ds_cnt, &ds_namv, &data) == -1) {
+ PyErr_SetString(ErrorObject, rrd_get_error());
+ rrd_clear_error();
+ r = NULL;
+ } else {
+ /* Return :
+ ((start, end, step), (name1, name2, ...), [(data1, data2, ..), ...]) */
+ PyObject *range_tup, *dsnam_tup, *data_list, *t;
+ unsigned long i, j, row;
+ rrd_value_t dv;
+
+ row = ((end - start) / step + 1);
+
+ r = PyTuple_New(3);
+ range_tup = PyTuple_New(3);
+ dsnam_tup = PyTuple_New(ds_cnt);
+ data_list = PyList_New(row);
+ PyTuple_SET_ITEM(r, 0, range_tup);
+ PyTuple_SET_ITEM(r, 1, dsnam_tup);
+ PyTuple_SET_ITEM(r, 2, data_list);
+
+ datai = data;
+
+ PyTuple_SET_ITEM(range_tup, 0, PyInt_FromLong((long)start));
+ PyTuple_SET_ITEM(range_tup, 1, PyInt_FromLong((long)end));
+ PyTuple_SET_ITEM(range_tup, 2, PyInt_FromLong((long)step));
+
+ for (i = 0; i < ds_cnt; i++)
+ PyTuple_SET_ITEM(dsnam_tup, i, PyString_FromString(ds_namv[i]));
+
+ for (i = 0; i < row; i ++) {
+ t = PyTuple_New(ds_cnt);
+ PyList_SET_ITEM(data_list, i, t);
+
+ for (j = 0; j < ds_cnt; j++) {
+ dv = *(datai++);
+ if (isnan(dv)) {
+ PyTuple_SET_ITEM(t, j, Py_None);
+ Py_INCREF(Py_None);
+ } else {
+ PyTuple_SET_ITEM(t, j, PyFloat_FromDouble((double)dv));
+ }
+ }
+ }
+
+ for (i = 0; i < ds_cnt; i++)
+ free(ds_namv[i]);
+ free(ds_namv); /* rrdtool don't use PyMem_Malloc :) */
+ free(data);
+ }
+
+ destroy_args(&argv);
+ return r;
+}
+
+static char PyRRD_graph__doc__[] =
+"graph(args..): Create a graph based on data from one or several RRD\n"
+" graph filename [-s|--start seconds] "
+"[-e|--end seconds] [-x|--x-grid x-axis grid and label] "
+"[-y|--y-grid y-axis grid and label] [--alt-y-grid] [--alt-y-mrtg] "
+"[--alt-autoscale] [--alt-autoscale-max] [--units-exponent] value "
+"[-v|--vertical-label text] [-w|--width pixels] [-h|--height pixels] "
+"[-i|--interlaced] "
+"[-f|--imginfo formatstring] [-a|--imgformat GIF|PNG|GD] "
+"[-B|--background value] [-O|--overlay value] "
+"[-U|--unit value] [-z|--lazy] [-o|--logarithmic] "
+"[-u|--upper-limit value] [-l|--lower-limit value] "
+"[-g|--no-legend] [-r|--rigid] [--step value] "
+"[-b|--base value] [-c|--color COLORTAG#rrggbb] "
+"[-t|--title title] [DEF:vname=rrd:ds-name:CF] "
+"[CDEF:vname=rpn-expression] [PRINT:vname:CF:format] "
+"[GPRINT:vname:CF:format] [COMMENT:text] "
+"[HRULE:value#rrggbb[:legend]] [VRULE:time#rrggbb[:legend]] "
+"[LINE{1|2|3}:vname[#rrggbb[:legend]]] "
+"[AREA:vname[#rrggbb[:legend]]] "
+"[STACK:vname[#rrggbb[:legend]]]";
+
+static PyObject *
+PyRRD_graph(PyObject UNUSED(*self), PyObject *args)
+{
+ PyObject *r;
+ char **argv, **calcpr;
+ int argc, xsize, ysize, i;
+ double ymin, ymax;
+ if (create_args("graph", args, &argc, &argv) < 0)
+ return NULL;
+
+ if (rrd_graph(argc, argv, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax) == -1) {
+ PyErr_SetString(ErrorObject, rrd_get_error());
+ rrd_clear_error();
+ r = NULL;
+ } else {
+ r = PyTuple_New(3);
+
+ PyTuple_SET_ITEM(r, 0, PyInt_FromLong((long)xsize));
+ PyTuple_SET_ITEM(r, 1, PyInt_FromLong((long)ysize));
+
+ if (calcpr) {
+ PyObject *e, *t;
+
+ e = PyList_New(0);
+ PyTuple_SET_ITEM(r, 2, e);
+
+ for(i = 0; calcpr[i]; i++) {
+ t = PyString_FromString(calcpr[i]);
+ PyList_Append(e, t);
+ Py_DECREF(t);
+ free(calcpr[i]);
+ }
+ free(calcpr);
+ } else {
+ Py_INCREF(Py_None);
+ PyTuple_SET_ITEM(r, 2, Py_None);
+ }
+ }
+
+ destroy_args(&argv);
+ return r;
+}
+
+static char PyRRD_tune__doc__[] =
+"tune(args...): Modify some basic properties of a Round Robin Database\n"
+" tune filename [--heartbeat|-h ds-name:heartbeat] "
+"[--minimum|-i ds-name:min] [--maximum|-a ds-name:max] "
+"[--data-source-type|-d ds-name:DST] [--data-source-rename|-r old-name:new-name]";
+
+static PyObject *
+PyRRD_tune(PyObject UNUSED(*self), PyObject *args)
+{
+ PyObject *r;
+ char **argv;
+ int argc;
+
+ if (create_args("tune", args, &argc, &argv) < 0)
+ return NULL;
+
+ if (rrd_tune(argc, argv) == -1) {
+ PyErr_SetString(ErrorObject, rrd_get_error());
+ rrd_clear_error();
+ r = NULL;
+ } else {
+ Py_INCREF(Py_None);
+ r = Py_None;
+ }
+
+ destroy_args(&argv);
+ return r;
+}
+
+static char PyRRD_first__doc__[] =
+"first(filename): Return the timestamp of the first data sample in an RRD";
+
+static PyObject *
+PyRRD_first(PyObject UNUSED(*self), PyObject *args)
+{
+ PyObject *r;
+ int argc, ts;
+ char **argv;
+
+ if (create_args("first", args, &argc, &argv) < 0)
+ return NULL;
+
+ if ((ts = rrd_first(argc, argv)) == -1) {
+ PyErr_SetString(ErrorObject, rrd_get_error());
+ rrd_clear_error();
+ r = NULL;
+ } else
+ r = PyInt_FromLong((long)ts);
+
+ destroy_args(&argv);
+ return r;
+}
+
+static char PyRRD_last__doc__[] =
+"last(filename): Return the timestamp of the last data sample in an RRD";
+
+static PyObject *
+PyRRD_last(PyObject UNUSED(*self), PyObject *args)
+{
+ PyObject *r;
+ int argc, ts;
+ char **argv;
+
+ if (create_args("last", args, &argc, &argv) < 0)
+ return NULL;
+
+ if ((ts = rrd_last(argc, argv)) == -1) {
+ PyErr_SetString(ErrorObject, rrd_get_error());
+ rrd_clear_error();
+ r = NULL;
+ } else
+ r = PyInt_FromLong((long)ts);
+
+ destroy_args(&argv);
+ return r;
+}
+
+static char PyRRD_resize__doc__[] =
+"resize(args...): alters the size of an RRA.\n"
+" resize filename rra-num GROW|SHRINK rows";
+
+static PyObject *
+PyRRD_resize(PyObject UNUSED(*self), PyObject *args)
+{
+ PyObject *r;
+ char **argv;
+ int argc, ts;
+
+ if (create_args("resize", args, &argc, &argv) < 0)
+ return NULL;
+
+ if ((ts = rrd_resize(argc, argv)) == -1) {
+ PyErr_SetString(ErrorObject, rrd_get_error());
+ rrd_clear_error();
+ r = NULL;
+ } else {
+ Py_INCREF(Py_None);
+ r = Py_None;
+ }
+
+ destroy_args(&argv);
+ return r;
+}
+
+static char PyRRD_info__doc__[] =
+"info(filename): extract header information from an rrd";
+
+static PyObject *
+PyRRD_info(PyObject UNUSED(*self), PyObject *args)
+{
+ PyObject *r, *t, *ds;
+ rrd_t rrd;
+ FILE *in_file;
+ char *filename;
+ unsigned long i, j;
+
+ if (! PyArg_ParseTuple(args, "s:info", &filename))
+ return NULL;
+
+ if (rrd_open(filename, &in_file, &rrd, RRD_READONLY) == -1) {
+ PyErr_SetString(ErrorObject, rrd_get_error());
+ rrd_clear_error();
+ return NULL;
+ }
+ fclose(in_file);
+
+#define DICTSET_STR(dict, name, value) \
+ t = PyString_FromString(value); \
+ PyDict_SetItemString(dict, name, t); \
+ Py_DECREF(t);
+
+#define DICTSET_CNT(dict, name, value) \
+ t = PyInt_FromLong((long)value); \
+ PyDict_SetItemString(dict, name, t); \
+ Py_DECREF(t);
+
+#define DICTSET_VAL(dict, name, value) \
+ t = isnan(value) ? (Py_INCREF(Py_None), Py_None) : \
+ PyFloat_FromDouble((double)value); \
+ PyDict_SetItemString(dict, name, t); \
+ Py_DECREF(t);
+
+ r = PyDict_New();
+
+ DICTSET_STR(r, "filename", filename);
+ DICTSET_STR(r, "rrd_version", rrd.stat_head->version);
+ DICTSET_CNT(r, "step", rrd.stat_head->pdp_step);
+ DICTSET_CNT(r, "last_update", rrd.live_head->last_up);
+
+ ds = PyDict_New();
+ PyDict_SetItemString(r, "ds", ds);
+ Py_DECREF(ds);
+
+ for (i = 0; i < rrd.stat_head->ds_cnt; i++) {
+ PyObject *d;
+
+ d = PyDict_New();
+ PyDict_SetItemString(ds, rrd.ds_def[i].ds_nam, d);
+ Py_DECREF(d);
+
+ DICTSET_STR(d, "ds_name", rrd.ds_def[i].ds_nam);
+ DICTSET_STR(d, "type", rrd.ds_def[i].dst);
+ DICTSET_CNT(d, "minimal_heartbeat", rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt);
+ DICTSET_VAL(d, "min", rrd.ds_def[i].par[DS_min_val].u_val);
+ DICTSET_VAL(d, "max", rrd.ds_def[i].par[DS_max_val].u_val);
+ DICTSET_STR(d, "last_ds", rrd.pdp_prep[i].last_ds);
+ DICTSET_VAL(d, "value", rrd.pdp_prep[i].scratch[PDP_val].u_val);
+ DICTSET_CNT(d, "unknown_sec", rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
+ }
+
+ ds = PyList_New(rrd.stat_head->rra_cnt);
+ PyDict_SetItemString(r, "rra", ds);
+ Py_DECREF(ds);
+
+ for (i = 0; i < rrd.stat_head->rra_cnt; i++) {
+ PyObject *d, *cdp;
+
+ d = PyDict_New();
+ PyList_SET_ITEM(ds, i, d);
+
+ DICTSET_STR(d, "cf", rrd.rra_def[i].cf_nam);
+ DICTSET_CNT(d, "rows", rrd.rra_def[i].row_cnt);
+ DICTSET_CNT(d, "pdp_per_row", rrd.rra_def[i].pdp_cnt);
+ DICTSET_VAL(d, "xff", rrd.rra_def[i].par[RRA_cdp_xff_val].u_val);
+
+ cdp = PyList_New(rrd.stat_head->ds_cnt);
+ PyDict_SetItemString(d, "cdp_prep", cdp);
+ Py_DECREF(cdp);
+
+ for (j = 0; j < rrd.stat_head->ds_cnt; j++) {
+ PyObject *cdd;
+
+ cdd = PyDict_New();
+ PyList_SET_ITEM(cdp, j, cdd);
+
+ DICTSET_VAL(cdd, "value",
+ rrd.cdp_prep[i*rrd.stat_head->ds_cnt+j].scratch[CDP_val].u_val);
+ DICTSET_CNT(cdd, "unknown_datapoints",
+ rrd.cdp_prep[i*rrd.stat_head->ds_cnt+j].scratch[CDP_unkn_pdp_cnt].u_cnt);
+ }
+ }
+
+ rrd_free(&rrd);
+
+ return r;
+}
+
+/* List of methods defined in the module */
+#define meth(name, func, doc) {name, (PyCFunction)func, METH_VARARGS, doc}
+
+static PyMethodDef _rrdtool_methods[] = {
+ meth("create", PyRRD_create, PyRRD_create__doc__),
+ meth("update", PyRRD_update, PyRRD_update__doc__),
+ meth("fetch", PyRRD_fetch, PyRRD_fetch__doc__),
+ meth("graph", PyRRD_graph, PyRRD_graph__doc__),
+ meth("tune", PyRRD_tune, PyRRD_tune__doc__),
+ meth("first", PyRRD_first, PyRRD_first__doc__),
+ meth("last", PyRRD_last, PyRRD_last__doc__),
+ meth("resize", PyRRD_resize, PyRRD_resize__doc__),
+ meth("info", PyRRD_info, PyRRD_info__doc__),
+ {NULL, NULL,0,NULL}
+};
+
+#define SET_INTCONSTANT(dict, value) \
+ t = PyInt_FromLong((long)value); \
+ PyDict_SetItemString(dict, #value, t); \
+ Py_DECREF(t);
+#define SET_STRCONSTANT(dict, value) \
+ t = PyString_FromString(value); \
+ PyDict_SetItemString(dict, #value, t); \
+ Py_DECREF(t);
+
+/* Initialization function for the module */
+void
+initrrdtool(void)
+{
+ PyObject *m, *d, *t;
+
+ /* Create the module and add the functions */
+ m = Py_InitModule("rrdtool", _rrdtool_methods);
+
+ /* Add some symbolic constants to the module */
+ d = PyModule_GetDict(m);
+
+ SET_STRCONSTANT(d, __version__);
+ ErrorObject = PyErr_NewException("rrdtool.error", NULL, NULL);
+ PyDict_SetItemString(d, "error", ErrorObject);
+
+ /* Check for errors */
+ if (PyErr_Occurred())
+ Py_FatalError("can't initialize the rrdtool module");
+}
+
+/*
+ * $Id: _rrdtoolmodule.c,v 1.14 2003/02/22 07:41:19 perky Exp $
+ * ex: ts=8 sts=4 et
+ */
diff --git a/program/bindings/python/setup.py b/program/bindings/python/setup.py
--- /dev/null
@@ -0,0 +1,55 @@
+#! /usr/bin/env python
+#
+# setup.py
+#
+# py-rrdtool distutil setup
+#
+# Author : Hye-Shik Chang <perky@fallin.lv>
+# Date : $Date: 2003/02/14 02:38:16 $
+# Created : 24 May 2002
+#
+# $Revision: 1.7 $
+#
+# ==========================================================================
+# This file is part of py-rrdtool.
+#
+# py-rrdtool 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 of the License, or
+# (at your option) any later version.
+#
+# py-rrdtool 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 Foobar; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+from distutils.core import setup, Extension
+import sys, os
+
+RRDBASE = os.environ.get('LOCALBASE', '../../src')
+library_dir = os.environ.get('BUILDLIBDIR', os.path.join(RRDBASE, 'lib'))
+include_dir = os.environ.get('INCDIR', RRDBASE)
+
+setup(name = "py-rrdtool",
+ version = "0.2.1",
+ description = "Python Interface to RRDTool",
+ author = "Hye-Shik Chang",
+ author_email = "perky@fallin.lv",
+ license = "LGPL",
+ url = "http://oss.oetiker.ch/rrdtool",
+ #packages = ['rrdtool'],
+ ext_modules = [
+ Extension(
+ "rrdtoolmodule",
+ ["rrdtoolmodule.c"],
+ libraries=['rrd'],
+ library_dirs=[library_dir],
+ include_dirs=[include_dir],
+ )
+ ]
+)
diff --git a/program/bindings/ruby/CHANGES b/program/bindings/ruby/CHANGES
--- /dev/null
@@ -0,0 +1,3 @@
+2006-07-25 Loïs Lherbier
+ add y_min and y_max parameters to rrd_graph
+ update test.rb to send only strings to RRD.fetch
diff --git a/program/bindings/ruby/README b/program/bindings/ruby/README
--- /dev/null
@@ -0,0 +1,22 @@
+#
+# ruby librrd bindings
+# author: Miles Egan <miles@caddr.com>
+#
+
+- Introduction
+
+This module provides ruby bindings for librrd, with functionality
+comparable to the native perl bindings. See test.rb for a script that
+exercises all ruby-librrd functionality.
+
+- Installation
+
+Installation is standard. Simply run:
+
+ruby extconf.rb
+make
+make install
+
+I hope this works for you. Please let me know if you have any
+problems or suggestions. Someday when I'm feeling less lazy I'll
+actually document this thing. Thanks to Tobi for rrdtool!
diff --git a/program/bindings/ruby/extconf.rb b/program/bindings/ruby/extconf.rb
--- /dev/null
@@ -0,0 +1,18 @@
+# $Id: extconf.rb,v 1.2 2001/11/28 18:30:16 miles Exp $
+# Lost ticket pays maximum rate.
+
+require 'mkmf'
+
+if /linux/ =~ RUBY_PLATFORM
+ $LDFLAGS += '-Wl,--rpath -Wl,$(EPREFIX)/lib'
+elsif /solaris/ =~ RUBY_PLATFORM
+ $LDFLAGS += '-R$(EPREFIX)/lib'
+elsif /hpux/ =~ RUBY_PLATFORM
+ $LDFLAGS += '+b$(EPREFIX)/lib'
+elsif /aix/ =~ RUBY_PLATFORM
+ $LDFLAGS += '-Wl,-blibpath:$(EPREFIX)/lib'
+end
+
+dir_config("rrd","../../src","../../src/.libs")
+have_library("rrd", "rrd_create")
+create_makefile("RRD")
diff --git a/program/bindings/ruby/main.c b/program/bindings/ruby/main.c
--- /dev/null
@@ -0,0 +1,259 @@
+/* $Id$
+ * Substantial penalty for early withdrawal.
+ */
+
+#include <unistd.h>
+#include <ruby.h>
+#include <rrd.h>
+
+typedef struct string_arr_t {
+ int len;
+ char **strings;
+} string_arr;
+
+VALUE mRRD;
+VALUE rb_eRRDError;
+
+typedef int (*RRDFUNC)(int argc, char ** argv);
+#define RRD_CHECK_ERROR \
+ if (rrd_test_error()) \
+ rb_raise(rb_eRRDError, rrd_get_error()); \
+ rrd_clear_error();
+
+string_arr string_arr_new(VALUE rb_strings)
+{
+ string_arr a;
+ char buf[64];
+ int i;
+
+ Check_Type(rb_strings, T_ARRAY);
+ a.len = RARRAY(rb_strings)->len + 1;
+
+ a.strings = malloc(a.len * sizeof(char *));
+ a.strings[0] = "dummy"; /* first element is a dummy element */
+
+ for (i = 0; i < a.len - 1; i++) {
+ VALUE v = rb_ary_entry(rb_strings, i);
+ switch (TYPE(v)) {
+ case T_STRING:
+ a.strings[i + 1] = strdup(STR2CSTR(v));
+ break;
+ case T_FIXNUM:
+ snprintf(buf, 63, "%d", FIX2INT(v));
+ a.strings[i + 1] = strdup(buf);
+ break;
+ default:
+ rb_raise(rb_eTypeError, "invalid argument");
+ break;
+ }
+ }
+
+ return a;
+}
+
+void string_arr_delete(string_arr a)
+{
+ int i;
+
+ /* skip dummy first entry */
+ for (i = 1; i < a.len; i++) {
+ free(a.strings[i]);
+ }
+
+ free(a.strings);
+}
+
+void reset_rrd_state()
+{
+ optind = 0;
+ opterr = 0;
+ rrd_clear_error();
+}
+
+VALUE rrd_call(RRDFUNC func, VALUE args)
+{
+ string_arr a;
+
+ a = string_arr_new(args);
+ reset_rrd_state();
+ func(a.len, a.strings);
+ string_arr_delete(a);
+
+ RRD_CHECK_ERROR
+
+ return Qnil;
+}
+
+VALUE rb_rrd_create(VALUE self, VALUE args)
+{
+ return rrd_call(rrd_create, args);
+}
+
+VALUE rb_rrd_dump(VALUE self, VALUE args)
+{
+ return rrd_call(rrd_dump, args);
+}
+
+VALUE rb_rrd_fetch(VALUE self, VALUE args)
+{
+ string_arr a;
+ unsigned long i, j, k, step, ds_cnt;
+ rrd_value_t *raw_data;
+ char **raw_names;
+ VALUE data, names, result;
+ time_t start, end;
+
+ a = string_arr_new(args);
+ reset_rrd_state();
+ rrd_fetch(a.len, a.strings, &start, &end, &step, &ds_cnt, &raw_names, &raw_data);
+ string_arr_delete(a);
+
+ RRD_CHECK_ERROR
+
+ names = rb_ary_new();
+ for (i = 0; i < ds_cnt; i++) {
+ rb_ary_push(names, rb_str_new2(raw_names[i]));
+ free(raw_names[i]);
+ }
+ free(raw_names);
+
+ k = 0;
+ data = rb_ary_new();
+ for (i = start; i <= end; i += step) {
+ VALUE line = rb_ary_new2(ds_cnt);
+ for (j = 0; j < ds_cnt; j++) {
+ rb_ary_store(line, j, rb_float_new(raw_data[k]));
+ k++;
+ }
+ rb_ary_push(data, line);
+ }
+ free(raw_data);
+
+ result = rb_ary_new2(4);
+ rb_ary_store(result, 0, INT2FIX(start));
+ rb_ary_store(result, 1, INT2FIX(end));
+ rb_ary_store(result, 2, names);
+ rb_ary_store(result, 2, data);
+ return result;
+}
+
+VALUE rb_rrd_graph(VALUE self, VALUE args)
+{
+ string_arr a;
+ char **calcpr, **p;
+ VALUE result, print_results;
+ int xsize, ysize;
+ double ymin, ymax;
+
+ a = string_arr_new(args);
+ reset_rrd_state();
+ rrd_graph(a.len, a.strings, &calcpr, &xsize, &ysize, NULL, &ymin, &ymax);
+ string_arr_delete(a);
+
+ RRD_CHECK_ERROR
+
+ result = rb_ary_new2(3);
+ print_results = rb_ary_new();
+ p = calcpr;
+ for (p = calcpr; p && *p; p++) {
+ rb_ary_push(print_results, rb_str_new2(*p));
+ free(*p);
+ }
+ free(calcpr);
+ rb_ary_store(result, 0, print_results);
+ rb_ary_store(result, 1, INT2FIX(xsize));
+ rb_ary_store(result, 2, INT2FIX(ysize));
+ return result;
+}
+
+/*
+VALUE rb_rrd_info(VALUE self, VALUE args)
+{
+ string_arr a;
+ info_t *p;
+ VALUE result;
+
+ a = string_arr_new(args);
+ data = rrd_info(a.len, a.strings);
+ string_arr_delete(a);
+
+ RRD_CHECK_ERROR
+
+ result = rb_hash_new();
+ while (data) {
+ VALUE key = rb_str_new2(data->key);
+ switch (data->type) {
+ case RD_I_VAL:
+ if (isnan(data->u_val)) {
+ rb_hash_aset(result, key, Qnil);
+ }
+ else {
+ rb_hash_aset(result, key, rb_float_new(data->u_val));
+ }
+ break;
+ case RD_I_CNT:
+ rb_hash_aset(result, key, INT2FIX(data->u_cnt));
+ break;
+ case RD_I_STR:
+ rb_hash_aset(result, key, rb_str_new2(data->u_str));
+ free(data->u_str);
+ break;
+ }
+ p = data;
+ data = data->next;
+ free(p);
+ }
+ return result;
+}
+*/
+
+VALUE rb_rrd_last(VALUE self, VALUE args)
+{
+ string_arr a;
+ time_t last;
+
+ a = string_arr_new(args);
+ reset_rrd_state();
+ last = rrd_last(a.len, a.strings);
+ string_arr_delete(a);
+
+ RRD_CHECK_ERROR
+
+ return rb_funcall(rb_cTime, rb_intern("at"), 1, INT2FIX(last));
+}
+
+VALUE rb_rrd_resize(VALUE self, VALUE args)
+{
+ return rrd_call(rrd_resize, args);
+}
+
+VALUE rb_rrd_restore(VALUE self, VALUE args)
+{
+ return rrd_call(rrd_restore, args);
+}
+
+VALUE rb_rrd_tune(VALUE self, VALUE args)
+{
+ return rrd_call(rrd_tune, args);
+}
+
+VALUE rb_rrd_update(VALUE self, VALUE args)
+{
+ return rrd_call(rrd_update, args);
+}
+
+void Init_RRD()
+{
+ mRRD = rb_define_module("RRD");
+ rb_eRRDError = rb_define_class("RRDError", rb_eStandardError);
+
+ rb_define_module_function(mRRD, "create", rb_rrd_create, -2);
+ rb_define_module_function(mRRD, "dump", rb_rrd_dump, -2);
+ rb_define_module_function(mRRD, "fetch", rb_rrd_fetch, -2);
+ rb_define_module_function(mRRD, "graph", rb_rrd_graph, -2);
+ rb_define_module_function(mRRD, "last", rb_rrd_last, -2);
+ rb_define_module_function(mRRD, "resize", rb_rrd_resize, -2);
+ rb_define_module_function(mRRD, "restore", rb_rrd_restore, -2);
+ rb_define_module_function(mRRD, "tune", rb_rrd_tune, -2);
+ rb_define_module_function(mRRD, "update", rb_rrd_update, -2);
+}
diff --git a/program/bindings/ruby/test.rb b/program/bindings/ruby/test.rb
--- /dev/null
@@ -0,0 +1,52 @@
+#!/usr/bin/env ruby
+# $Id: test.rb,v 1.2 2002/10/22 17:34:00 miles Exp $
+# Driver does not carry cash.
+
+$: << '/scratch/rrd12build/lib/ruby/1.8/i386-linux/'
+
+require "RRD"
+
+name = "test"
+rrd = "#{name}.rrd"
+start = Time.now.to_i
+
+puts "creating #{rrd}"
+RRD.create(
+ rrd,
+ "--start", "#{start - 1}",
+ "--step", "300",
+ "DS:a:GAUGE:600:U:U",
+ "DS:b:GAUGE:600:U:U",
+ "RRA:AVERAGE:0.5:1:300")
+puts
+
+puts "updating #{rrd}"
+start.to_i.step(start.to_i + 300 * 300, 300) { |i|
+ RRD.update(rrd, "#{i}:#{rand(100)}:#{Math.sin(i / 800) * 50 + 50}")
+}
+puts
+
+puts "fetching data from #{rrd}"
+(fstart, fend, data) = RRD.fetch(rrd, "--start", start.to_s, "--end", (start + 300 * 300).to_s, "AVERAGE")
+puts "got #{data.length} data points from #{fstart} to #{fend}"
+puts
+
+puts "generating graph #{name}.png"
+RRD.graph(
+ "#{name}.png",
+ "--title", " RubyRRD Demo",
+ "--start", "#{start+3600}",
+ "--end", "start + 1000 min",
+ "--interlace",
+ "--imgformat", "PNG",
+ "--width=450",
+ "DEF:a=#{rrd}:a:AVERAGE",
+ "DEF:b=#{rrd}:b:AVERAGE",
+ "CDEF:line=TIME,2400,%,300,LT,a,UNKN,IF",
+ "AREA:b#00b6e4:beta",
+ "AREA:line#0022e9:alpha",
+ "LINE3:line#ff0000")
+puts
+
+print "This script has created #{name}.png in the current directory\n";
+print "This demonstrates the use of the TIME and % RPN operators\n";
diff --git a/program/bindings/tcl/Makefile.am b/program/bindings/tcl/Makefile.am
--- /dev/null
@@ -0,0 +1,56 @@
+
+EXTRA_DIST = README tclrrd.c
+
+VERSION = @VERSION@
+
+AM_CFLAGS = @CFLAGS@
+
+TCL_PREFIX = @TCL_PREFIX@
+TCL_SHLIB_LD = @TCL_SHLIB_LD@
+TCL_SHLIB_CFLAGS = @TCL_SHLIB_CFLAGS@
+TCL_SHLIB_SUFFIX = @TCL_SHLIB_SUFFIX@
+TCL_PACKAGE_PATH = @TCL_PACKAGE_PATH@
+TCL_LD_SEARCH_FLAGS = @TCL_LD_SEARCH_FLAGS@
+TCL_STUB_LIB_SPEC = @TCL_STUB_LIB_SPEC@
+
+CLEANFILES = tclrrd.o tclrrd.so
+
+SRC_DIR = $(top_srcdir)/src
+AM_CPPFLAGS = -I$(TCL_PREFIX)/include -I$(SRC_DIR) -DUSE_TCL_STUBS
+LIBDIRS = -L$(top_builddir)/src/.libs -L$(top_builddir)/src -L$(libdir)
+LIB_RUNTIME_DIR = $(libdir)
+
+if BUILD_TCL_SITE
+tclpkgdir = @TCL_PACKAGE_DIR@
+tclpkg_DATA = pkgIndex.tcl
+tclpkg_SCRIPTS = ifOctets.tcl
+else
+pkglib_DATA = pkgIndex.tcl
+pkglib_SCRIPTS = ifOctets.tcl
+endif
+
+# Automake doen't like `tclrrd$(VERSION)$(TCL_SHLIB_SUFFIX)' as
+# library name. So we build and install this library `by hand'.
+#
+# We do, however, specify a lib_LIBRARIES target such that
+# automake creates the directory (if neecessary).
+#
+TCL_RRD_LIB = tclrrd$(VERSION)$(TCL_SHLIB_SUFFIX)
+
+lib_LIBRARIES =
+
+all-local: $(TCL_RRD_LIB)
+
+$(TCL_RRD_LIB): tclrrd.o
+ $(TCL_SHLIB_LD) $(TCL_LD_SEARCH_FLAGS) $(LIBDIRS) $< -o $@ -lrrd_th -lm $(TCL_STUB_LIB_SPEC) $(LDFLAGS) $(LIBS)
+
+tclrrd.o: tclrrd.c
+ $(CC) $(AM_CFLAGS) $(CFLAGS) $(TCL_SHLIB_CFLAGS) $(AM_CPPFLAGS) -c $< -DVERSION=\"$(VERSION)\"
+
+pkgIndex.tcl:
+ echo "package ifneeded Rrd $(VERSION) \"load $(libdir)/tclrrd$(VERSION)[info sharedlibextension]\"" > $@
+
+install-exec-local: $(TCL_RRD_LIB)
+ @$(NORMAL_INSTALL)
+ $(INSTALL_PROGRAM) $(TCL_RRD_LIB) $(DESTDIR)$(libdir)/$(TCL_RRD_LIB)
+
diff --git a/program/bindings/tcl/README b/program/bindings/tcl/README
--- /dev/null
@@ -0,0 +1,31 @@
+TCLRRD -- A TCL interpreter extension to access the RRD library,
+ contributed to Tobias Oetiker's RRD tools.
+
+Copyright (c) 1999,2000 Frank Strauss, Technical University of Braunschweig.
+
+See the file "COPYING" for information on usage and redistribution
+of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+
+TCLRRD adds a dynamically loadable package to the Tcl 8.x interpreter
+to access all RRD functions as of RRDtool 1.0.13. All command names
+and arguments are equal to those of RRDtool. They are assigned to the
+namespace `Rrd', e.g. `Rrd::create'. Return values are a bit
+different from plain RRDtool behavior to enable more native Tcl
+usage. Errors are mapped to the TCL_ERROR return code together with
+the RRD error strings.
+
+TCLRRD makes it easy to combine RRD use with advanced SNMP functionality
+of scotty (http://wwwsnmp.cs.utwente.nl/~schoenw/scotty/). E.g., it's easy
+to use some scotty code to get the counters of some interfaces by their
+interface name and then use Rrd::update to store the values. Furthermore,
+data source types (see RRD::create documentation) and integer value ranges
+could be easily retrieved from MIB information.
+
+TCLRRD has been written on a Linux system for use with Tcl 8.x. It should
+work on many other platforms, although it has not been tested. There are
+no fool proof installation procedures. Take a look at Makefile.am and
+adapt it, if required.
+
+TCLRRD has been written for RRD 1.0.13.
+
+ Frank Strauss <strauss@ibr.cs.tu-bs.de>, 09-Mar-2000
diff --git a/program/bindings/tcl/ifOctets.tcl.in b/program/bindings/tcl/ifOctets.tcl.in
--- /dev/null
@@ -0,0 +1,45 @@
+#!/bin/sh
+# the next line restarts using tclsh -*- tcl -*- \
+exec tclsh@TCL_VERSION@ "$0" "$@"
+
+#package require Tnm 3.0
+package require Rrd @VERSION@
+
+set rrdfile "[lindex $argv 0]-[lindex $argv 1].rrd"
+
+# create rrdfile if not yet existent
+if {[file exists $rrdfile] == 0} {
+ Rrd::create $rrdfile --step 5 \
+ DS:inOctets:COUNTER:10:U:U DS:outOctets:COUNTER:10:U:U \
+ RRA:AVERAGE:0.5:1:12
+}
+
+# get an snmp session context
+set session [Tnm::snmp generator -address [lindex $argv 0]]
+
+# walk through the ifDescr column to find the right interface
+$session walk descr IF-MIB!ifDescr {
+
+ # is this the right interface?
+ if {"[Tnm::snmp value $descr 0]" == "[lindex $argv 1]"} {
+
+ # get the instance part of this table row
+ set inst [lindex [Tnm::mib split [Tnm::snmp oid $descr 0]] 1]
+
+ # get the two interface's octet counter values
+ set in [lindex [lindex [$session get IF-MIB!ifInOctets.$inst] 0] 2]
+ set out [lindex [lindex [$session get IF-MIB!ifOutOctets.$inst] 0] 2]
+
+ # write the values to the rrd
+ puts "$in $out"
+ Rrd::update $rrdfile --template inOctets:outOctets N:$in:$out
+
+ Rrd::graph gaga.png --title "gaga" \
+ DEF:in=$rrdfile:inOctets:AVERAGE \
+ DEF:out=$rrdfile:outOctets:AVERAGE \
+ AREA:in#0000FF:inOctets \
+ LINE2:out#00C000:outOctets
+
+ #puts [Rrd::fetch $rrdfile AVERAGE]
+ }
+}
diff --git a/program/bindings/tcl/tclrrd.c b/program/bindings/tcl/tclrrd.c
--- /dev/null
@@ -0,0 +1,677 @@
+/*
+ * tclrrd.c -- A TCL interpreter extension to access the RRD library.
+ *
+ * Copyright (c) 1999,2000 Frank Strauss, Technical University of Braunschweig.
+ *
+ * Thread-safe code copyright (c) 2005 Oleg Derevenetz, CenterTelecom Voronezh ISP.
+ *
+ * See the file "COPYING" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * $Id$
+ */
+
+
+
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <tcl.h>
+#include "../../src/rrd_tool.h"
+#include "../../src/rrd_format.h"
+
+/* support pre-8.4 tcl */
+
+#ifndef CONST84
+# define CONST84
+#endif
+
+extern int Tclrrd_Init(Tcl_Interp *interp);
+extern int Tclrrd_SafeInit(Tcl_Interp *interp);
+
+
+/*
+ * some rrd_XXX() and new thread-safe versions of Rrd_XXX()
+ * functions might modify the argv strings passed to it.
+ * Hence, we need to do some preparation before
+ * calling the rrd library functions.
+ */
+static char ** getopt_init(int argc, CONST84 char *argv[])
+{
+ char **argv2;
+ int i;
+
+ argv2 = calloc(argc, sizeof(char *));
+ for (i = 0; i < argc; i++) {
+ argv2[i] = strdup(argv[i]);
+ }
+ return argv2;
+}
+
+static void getopt_cleanup(int argc, char **argv2)
+{
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ if (argv2[i] != NULL) {
+ free(argv2[i]);
+ }
+ }
+ free(argv2);
+}
+
+static void getopt_free_element(argv2, argn)
+ char *argv2[];
+ int argn;
+{
+ if (argv2[argn] != NULL) {
+ free(argv2[argn]);
+ argv2[argn] = NULL;
+ }
+}
+
+static void getopt_squieeze(argc, argv2)
+ int *argc;
+ char *argv2[];
+{
+ int i, null_i = 0, argc_tmp = *argc;
+
+ for (i = 0; i < argc_tmp; i++) {
+ if (argv2[i] == NULL) {
+ (*argc)--;
+ } else {
+ argv2[null_i++] = argv2[i];
+ }
+ }
+}
+
+
+
+/* Thread-safe version */
+static int
+Rrd_Create(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
+{
+ int argv_i;
+ char **argv2;
+ char *parsetime_error = NULL;
+ time_t last_up = time(NULL) - 10;
+ long int long_tmp;
+ unsigned long int pdp_step = 300;
+ struct rrd_time_value last_up_tv;
+
+ argv2 = getopt_init(argc, argv);
+
+ for (argv_i = 1; argv_i < argc; argv_i++) {
+ if (!strcmp(argv2[argv_i], "--start") || !strcmp(argv2[argv_i], "-b")) {
+ if (argv_i++>=argc) {
+ Tcl_AppendResult(interp, "RRD Error: option '",
+ argv2[argv_i - 1], "' needs an argument", (char *) NULL);
+ getopt_cleanup(argc, argv2);
+ return TCL_ERROR;
+ }
+ if ((parsetime_error = parsetime(argv2[argv_i], &last_up_tv))) {
+ Tcl_AppendResult(interp, "RRD Error: invalid time format: '",
+ argv2[argv_i], "'", (char *) NULL);
+ getopt_cleanup(argc, argv2);
+ return TCL_ERROR;
+ }
+ if (last_up_tv.type == RELATIVE_TO_END_TIME ||
+ last_up_tv.type == RELATIVE_TO_START_TIME) {
+ Tcl_AppendResult(interp, "RRD Error: specifying time relative to the 'start' ",
+ "or 'end' makes no sense here", (char *) NULL);
+ getopt_cleanup(argc, argv2);
+ return TCL_ERROR;
+ }
+ last_up = mktime(&last_up_tv.tm) + last_up_tv.offset;
+ if (last_up < 3600*24*365*10) {
+ Tcl_AppendResult(interp, "RRD Error: the first entry to the RRD should be after 1980",
+ (char *) NULL);
+ getopt_cleanup(argc, argv2);
+ return TCL_ERROR;
+ }
+ getopt_free_element(argv2, argv_i - 1);
+ getopt_free_element(argv2, argv_i);
+ } else if (!strcmp(argv2[argv_i], "--step") || !strcmp(argv2[argv_i], "-s")) {
+ if (argv_i++>=argc) {
+ Tcl_AppendResult(interp, "RRD Error: option '",
+ argv2[argv_i - 1], "' needs an argument", (char *) NULL);
+ getopt_cleanup(argc, argv2);
+ return TCL_ERROR;
+ }
+ long_tmp = atol(argv2[argv_i]);
+ if (long_tmp < 1) {
+ Tcl_AppendResult(interp, "RRD Error: step size should be no less than one second",
+ (char *) NULL);
+ getopt_cleanup(argc, argv2);
+ return TCL_ERROR;
+ }
+ pdp_step = long_tmp;
+ getopt_free_element(argv2, argv_i - 1);
+ getopt_free_element(argv2, argv_i);
+ } else if (!strcmp(argv2[argv_i], "--")) {
+ getopt_free_element(argv2, argv_i);
+ break;
+ } else if (argv2[argv_i][0]=='-') {
+ Tcl_AppendResult(interp, "RRD Error: unknown option '",
+ argv2[argv_i], "'", (char *) NULL);
+ getopt_cleanup(argc, argv2);
+ return TCL_ERROR;
+ }
+ }
+
+ getopt_squieeze(&argc, argv2);
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "RRD Error: needs rrd filename",
+ (char *) NULL);
+ getopt_cleanup(argc, argv2);
+ return TCL_ERROR;
+ }
+
+ rrd_create_r(argv2[1], pdp_step, last_up, argc - 2, argv2 + 2);
+
+ getopt_cleanup(argc, argv2);
+
+ if (rrd_test_error()) {
+ Tcl_AppendResult(interp, "RRD Error: ",
+ rrd_get_error(), (char *) NULL);
+ rrd_clear_error();
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+
+
+/* Thread-safe version */
+static int
+Rrd_Dump(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
+{
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "RRD Error: needs rrd filename",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ rrd_dump_r(argv[1], NULL);
+
+ /* NOTE: rrd_dump() writes to stdout. No interaction with TCL. */
+
+ if (rrd_test_error()) {
+ Tcl_AppendResult(interp, "RRD Error: ",
+ rrd_get_error(), (char *) NULL);
+ rrd_clear_error();
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+
+
+/* Thread-safe version */
+static int
+Rrd_Last(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
+{
+ time_t t;
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "RRD Error: needs rrd filename",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ t = rrd_last_r(argv[1]);
+
+ if (rrd_test_error()) {
+ Tcl_AppendResult(interp, "RRD Error: ",
+ rrd_get_error(), (char *) NULL);
+ rrd_clear_error();
+ return TCL_ERROR;
+ }
+
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), t);
+
+ return TCL_OK;
+}
+
+
+
+/* Thread-safe version */
+static int
+Rrd_Update(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
+{
+ int argv_i;
+ char **argv2, *template = NULL;
+
+ argv2 = getopt_init(argc, argv);
+
+ for (argv_i = 1; argv_i < argc; argv_i++) {
+ if (!strcmp(argv2[argv_i], "--template") || !strcmp(argv2[argv_i], "-t")) {
+ if (argv_i++>=argc) {
+ Tcl_AppendResult(interp, "RRD Error: option '",
+ argv2[argv_i - 1], "' needs an argument", (char *) NULL);
+ if (template != NULL) {
+ free(template);
+ }
+ getopt_cleanup(argc, argv2);
+ return TCL_ERROR;
+ }
+ if (template != NULL) {
+ free(template);
+ }
+ template = strdup(argv2[argv_i]);
+ getopt_free_element(argv2, argv_i - 1);
+ getopt_free_element(argv2, argv_i);
+ } else if (!strcmp(argv2[argv_i], "--")) {
+ getopt_free_element(argv2, argv_i);
+ break;
+ } else if (argv2[argv_i][0]=='-') {
+ Tcl_AppendResult(interp, "RRD Error: unknown option '",
+ argv2[argv_i], "'", (char *) NULL);
+ if (template != NULL) {
+ free(template);
+ }
+ getopt_cleanup(argc, argv2);
+ return TCL_ERROR;
+ }
+ }
+
+ getopt_squieeze(&argc, argv2);
+
+ if (argc < 2) {
+ Tcl_AppendResult(interp, "RRD Error: needs rrd filename",
+ (char *) NULL);
+ if (template != NULL) {
+ free(template);
+ }
+ getopt_cleanup(argc, argv2);
+ return TCL_ERROR;
+ }
+
+ rrd_update_r(argv2[1], template, argc - 2, argv2 + 2);
+
+ if (template != NULL) {
+ free(template);
+ }
+ getopt_cleanup(argc, argv2);
+
+ if (rrd_test_error()) {
+ Tcl_AppendResult(interp, "RRD Error: ",
+ rrd_get_error(), (char *) NULL);
+ rrd_clear_error();
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+static int
+Rrd_Lastupdate(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
+{
+ time_t last_update;
+ char **argv2;
+ char **ds_namv;
+ char **last_ds;
+ char s[30];
+ Tcl_Obj *listPtr;
+ unsigned long ds_cnt, i;
+
+ argv2 = getopt_init(argc, argv);
+ if (rrd_lastupdate(argc-1, argv2, &last_update,
+ &ds_cnt, &ds_namv, &last_ds) == 0) {
+ listPtr = Tcl_GetObjResult(interp);
+ for (i=0; i<ds_cnt; i++) {
+ sprintf(s, " %28s", ds_namv[i]);
+ Tcl_ListObjAppendElement(interp, listPtr,
+ Tcl_NewStringObj(s, -1));
+ sprintf(s, "\n\n%10lu:", last_update);
+ Tcl_ListObjAppendElement(interp, listPtr,
+ Tcl_NewStringObj(s, -1));
+ for (i=0; i<ds_cnt; i++) {
+ sprintf(s, " %s", last_ds[i]);
+ Tcl_ListObjAppendElement(interp, listPtr,
+ Tcl_NewStringObj(s, -1));
+ free(last_ds[i]);
+ free(ds_namv[i]);
+ }
+ sprintf(s, "\n");
+ Tcl_ListObjAppendElement(interp, listPtr,
+ Tcl_NewStringObj(s, -1));
+ free(last_ds);
+ free(ds_namv);
+ }
+ }
+ return TCL_OK;
+}
+
+static int
+Rrd_Fetch(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
+{
+ time_t start, end, j;
+ unsigned long step, ds_cnt, i, ii;
+ rrd_value_t *data, *datai;
+ char **ds_namv;
+ Tcl_Obj *listPtr;
+ char s[30];
+ char **argv2;
+
+ argv2 = getopt_init(argc, argv);
+ if (rrd_fetch(argc, argv2, &start, &end, &step,
+ &ds_cnt, &ds_namv, &data) != -1) {
+ datai = data;
+ listPtr = Tcl_GetObjResult(interp);
+ for (j = start; j <= end; j += step) {
+ for (ii = 0; ii < ds_cnt; ii++) {
+ sprintf(s, "%.2f", *(datai++));
+ Tcl_ListObjAppendElement(interp, listPtr,
+ Tcl_NewStringObj(s, -1));
+ }
+ }
+ for (i=0; i<ds_cnt; i++) free(ds_namv[i]);
+ free(ds_namv);
+ free(data);
+ }
+ getopt_cleanup(argc, argv2);
+
+ if (rrd_test_error()) {
+ Tcl_AppendResult(interp, "RRD Error: ",
+ rrd_get_error(), (char *) NULL);
+ rrd_clear_error();
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+
+
+static int
+Rrd_Graph(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
+{
+ Tcl_Channel channel;
+ int mode, fd2;
+ ClientData fd1;
+ FILE *stream = NULL;
+ char **calcpr = NULL;
+ int rc, xsize, ysize;
+ double ymin, ymax;
+ char dimensions[50];
+ char **argv2;
+ CONST84 char *save;
+
+ /*
+ * If the "filename" is a Tcl fileID, then arrange for rrd_graph() to write to
+ * that file descriptor. Will this work with windoze? I have no idea.
+ */
+ if ((channel = Tcl_GetChannel(interp, argv[1], &mode)) != NULL) {
+ /*
+ * It >is< a Tcl fileID
+ */
+ if (!(mode & TCL_WRITABLE)) {
+ Tcl_AppendResult(interp, "channel \"", argv[1],
+ "\" wasn't opened for writing", (char *) NULL);
+ return TCL_ERROR;
+ }
+ /*
+ * Must flush channel to make sure any buffered data is written before
+ * rrd_graph() writes to the stream
+ */
+ if (Tcl_Flush(channel) != TCL_OK) {
+ Tcl_AppendResult(interp, "flush failed for \"", argv[1], "\": ",
+ strerror(Tcl_GetErrno()), (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (Tcl_GetChannelHandle(channel, TCL_WRITABLE, &fd1) != TCL_OK) {
+ Tcl_AppendResult(interp, "cannot get file descriptor associated with \"",
+ argv[1], "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ /*
+ * Must dup() file descriptor so we can fclose(stream), otherwise the fclose()
+ * would close Tcl's file descriptor
+ */
+ if ((fd2 = dup((int)fd1)) == -1) {
+ Tcl_AppendResult(interp, "dup() failed for file descriptor associated with \"",
+ argv[1], "\": ", strerror(errno), (char *) NULL);
+ return TCL_ERROR;
+ }
+ /*
+ * rrd_graph() wants a FILE*
+ */
+ if ((stream = fdopen(fd2, "wb")) == NULL) {
+ Tcl_AppendResult(interp, "fdopen() failed for file descriptor associated with \"",
+ argv[1], "\": ", strerror(errno), (char *) NULL);
+ close(fd2); /* plug potential file descriptor leak */
+ return TCL_ERROR;
+ }
+
+ save = argv[1];
+ argv[1] = "-";
+ argv2 = getopt_init(argc, argv);
+ argv[1] = save;
+ } else {
+ Tcl_ResetResult(interp); /* clear error from Tcl_GetChannel() */
+ argv2 = getopt_init(argc, argv);
+ }
+
+ rc = rrd_graph(argc, argv2, &calcpr, &xsize, &ysize, stream, &ymin, &ymax);
+ getopt_cleanup(argc, argv2);
+
+ if (stream != NULL)
+ fclose(stream); /* plug potential malloc & file descriptor leak */
+
+ if (rc != -1) {
+ sprintf(dimensions, "%d %d", xsize, ysize);
+ Tcl_AppendResult(interp, dimensions, (char *) NULL);
+ if (calcpr) {
+#if 0
+ int i;
+
+ for(i = 0; calcpr[i]; i++){
+ printf("%s\n", calcpr[i]);
+ free(calcpr[i]);
+ }
+#endif
+ free(calcpr);
+ }
+ }
+
+ if (rrd_test_error()) {
+ Tcl_AppendResult(interp, "RRD Error: ",
+ rrd_get_error(), (char *) NULL);
+ rrd_clear_error();
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+
+
+static int
+Rrd_Tune(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
+{
+ char **argv2;
+
+ argv2 = getopt_init(argc, argv);
+ rrd_tune(argc, argv2);
+ getopt_cleanup(argc, argv2);
+
+ if (rrd_test_error()) {
+ Tcl_AppendResult(interp, "RRD Error: ",
+ rrd_get_error(), (char *) NULL);
+ rrd_clear_error();
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+
+
+static int
+Rrd_Resize(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
+{
+ char **argv2;
+
+ argv2 = getopt_init(argc, argv);
+ rrd_resize(argc, argv2);
+ getopt_cleanup(argc, argv2);
+
+ if (rrd_test_error()) {
+ Tcl_AppendResult(interp, "RRD Error: ",
+ rrd_get_error(), (char *) NULL);
+ rrd_clear_error();
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+
+
+static int
+Rrd_Restore(ClientData clientData, Tcl_Interp *interp, int argc, CONST84 char *argv[])
+{
+ char **argv2;
+
+ argv2 = getopt_init(argc, argv);
+ rrd_restore(argc, argv2);
+ getopt_cleanup(argc, argv2);
+
+ if (rrd_test_error()) {
+ Tcl_AppendResult(interp, "RRD Error: ",
+ rrd_get_error(), (char *) NULL);
+ rrd_clear_error();
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+
+
+/*
+ * The following structure defines the commands in the Rrd extension.
+ */
+
+typedef struct {
+ char *name; /* Name of the command. */
+ Tcl_CmdProc *proc; /* Procedure for command. */
+ int hide; /* Hide if safe interpreter */
+} CmdInfo;
+
+static CmdInfo rrdCmds[] = {
+ { "Rrd::create", Rrd_Create, 1 }, /* Thread-safe version */
+ { "Rrd::dump", Rrd_Dump, 0 }, /* Thread-safe version */
+ { "Rrd::last", Rrd_Last, 0 }, /* Thread-safe version */
+ { "Rrd::lastupdate", Rrd_Lastupdate, 0 }, /* Thread-safe version */
+ { "Rrd::update", Rrd_Update, 1 }, /* Thread-safe version */
+ { "Rrd::fetch", Rrd_Fetch, 0 },
+ { "Rrd::graph", Rrd_Graph, 1 }, /* Due to RRD's API, a safe
+ interpreter cannot create
+ a graph since it writes to
+ a filename supplied by the
+ caller */
+ { "Rrd::tune", Rrd_Tune, 1 },
+ { "Rrd::resize", Rrd_Resize, 1 },
+ { "Rrd::restore", Rrd_Restore, 1 },
+ { (char *) NULL, (Tcl_CmdProc *) NULL, 0 }
+};
+
+
+
+static int
+init(Tcl_Interp *interp, int safe)
+{
+ CmdInfo *cmdInfoPtr;
+ Tcl_CmdInfo info;
+
+ if ( Tcl_InitStubs(interp,TCL_VERSION,0) == NULL )
+ return TCL_ERROR;
+
+ if (Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 1) == NULL) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Why a global array? In keeping with the Rrd:: namespace, why
+ * not simply create a normal variable Rrd::version and set it?
+ */
+ Tcl_SetVar2(interp, "rrd", "version", VERSION, TCL_GLOBAL_ONLY);
+
+ for (cmdInfoPtr = rrdCmds; cmdInfoPtr->name != NULL; cmdInfoPtr++) {
+ /*
+ * Check if the command already exists and return an error
+ * to ensure we detect name clashes while loading the Rrd
+ * extension.
+ */
+ if (Tcl_GetCommandInfo(interp, cmdInfoPtr->name, &info)) {
+ Tcl_AppendResult(interp, "command \"", cmdInfoPtr->name,
+ "\" already exists", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (safe && cmdInfoPtr->hide) {
+#if 0
+ /*
+ * Turns out the one cannot hide a command in a namespace
+ * due to a limitation of Tcl, one can only hide global
+ * commands. Thus, if we created the commands without
+ * the Rrd:: namespace in a safe interpreter, then the
+ * "unsafe" commands could be hidden -- which would allow
+ * an owning interpreter either un-hiding them or doing
+ * an "interp invokehidden". If the Rrd:: namespace is
+ * used, then it's still possible for the owning interpreter
+ * to fake out the missing commands:
+ *
+ * # Make all Rrd::* commands available in master interperter
+ * package require Rrd
+ * set safe [interp create -safe]
+ * # Make safe Rrd::* commands available in safe interperter
+ * interp invokehidden $safe -global load ./tclrrd1.2.11.so
+ * # Provide the safe interpreter with the missing commands
+ * $safe alias Rrd::update do_update $safe
+ * proc do_update {which_interp $args} {
+ * # Do some checking maybe...
+ * :
+ * return [eval Rrd::update $args]
+ * }
+ *
+ * Our solution for now is to just not create the "unsafe"
+ * commands in a safe interpreter.
+ */
+ if (Tcl_HideCommand(interp, cmdInfoPtr->name, cmdInfoPtr->name) != TCL_OK)
+ return TCL_ERROR;
+#endif
+ }
+ else
+ Tcl_CreateCommand(interp, cmdInfoPtr->name, cmdInfoPtr->proc,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+ }
+
+ if (Tcl_PkgProvide(interp, "Rrd", VERSION) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ return TCL_OK;
+}
+
+int
+Tclrrd_Init(Tcl_Interp *interp)
+{
+ return init(interp, 0);
+}
+
+/*
+ * See the comments above and note how few commands are considered "safe"...
+ * Using rrdtool in a safe interpreter has very limited functionality. It's
+ * tempting to just return TCL_ERROR and forget about it.
+ */
+int
+Tclrrd_SafeInit(Tcl_Interp *interp)
+{
+ return init(interp, 1);
+}
diff --git a/program/configure.ac b/program/configure.ac
--- /dev/null
+++ b/program/configure.ac
@@ -0,0 +1,676 @@
+dnl RRDtool AutoConf script ...
+dnl ---------------------------
+dnl
+dnl Created by Jeff Allen, Tobi Oetiker, Blair Zajac
+dnl
+dnl Inspiration from http://autoconf-archive.cryp.to
+
+dnl tell automake the this script is for rrdtool
+dnl the official version number is
+dnl a.b.c
+AC_INIT([rrdtool],[1.2.23])
+dnl for testing a numberical version number comes handy
+dnl the released version are
+dnl a.bccc
+dnl the devl versions will be something like
+dnl a.b999yymmddhh
+NUMVERS=1.2023
+AC_SUBST(NUMVERS)
+AC_CANONICAL_TARGET
+AM_INIT_AUTOMAKE
+AC_CONFIG_HEADERS([rrd_config.h])
+
+dnl all our local stuff like install scripts and include files
+dnl is in there
+
+
+dnl determine the type of system we are running on
+
+AC_SUBST(VERSION)
+
+AC_PREFIX_DEFAULT( /usr/local/rrdtool-$PACKAGE_VERSION )
+
+dnl Minimum Autoconf version required.
+AC_PREREQ(2.59)
+
+dnl At the TOP of the HEADER
+
+AH_TOP([
+
+#ifndef RRD_CONFIG_H
+#define RRD_CONFIG_H
+/* IEEE can be prevented from raising signals with fpsetmask(0) */
+#undef MUST_DISABLE_FPMASK
+
+/* IEEE math only works if SIGFPE gets actively set to IGNORE */
+
+#undef MUST_DISABLE_SIGFPE
+
+/* realloc does not support NULL as argument */
+#undef NO_NULL_REALLOC
+
+ ])
+
+AH_BOTTOM([
+/* enable posix_fadvise on linux */
+#if defined(HAVE_POSIX_FADVISE) && defined(HAVE_FCNTL_H)
+#define _XOPEN_SOURCE 600
+#include <fcntl.h>
+#endif
+
+/* define strrchr, strchr and memcpy, memmove in terms of bsd funcs
+ make sure you are NOT using bcopy, index or rindex in the code */
+
+#ifdef STDC_HEADERS
+# include <string.h>
+#else
+# ifndef HAVE_STRCHR
+# define strchr index
+# define strrchr rindex
+# endif
+char *strchr (), *strrchr ();
+# ifndef HAVE_MEMMOVE
+# define memcpy(d, s, n) bcopy ((s), (d), (n))
+# define memmove(d, s, n) bcopy ((s), (d), (n))
+# endif
+#endif
+
+#ifdef NO_NULL_REALLOC
+# define rrd_realloc(a,b) ( (a) == NULL ? malloc( (b) ) : realloc( (a) , (b) ))
+#else
+# define rrd_realloc(a,b) realloc((a), (b))
+#endif
+
+#ifdef NEED_MALLOC_MALLOC_H
+# include <malloc/malloc.h>
+#endif
+
+#ifdef HAVE_MATH_H
+# include <math.h>
+#endif
+
+#ifdef HAVE_FLOAT_H
+# include <float.h>
+#endif
+
+#ifdef HAVE_IEEEFP_H
+# include <ieeefp.h>
+#endif
+
+#ifdef HAVE_FP_CLASS_H
+# include <fp_class.h>
+#endif
+
+/* for Solaris */
+#if (! defined(HAVE_ISINF) && defined(HAVE_FPCLASS))
+# define HAVE_ISINF 1
+# define isinf(a) (fpclass(a) == FP_NINF || fpclass(a) == FP_PINF)
+#endif
+
+/* solaris 10 it defines isnan such that only forte can compile it ... bad bad */
+#if (defined(HAVE_ISNAN) && defined(isnan) && defined(HAVE_FPCLASS))
+# undef isnan
+# define isnan(a) (fpclass(a) == FP_SNAN || fpclass(a) == FP_QNAN)
+#endif
+
+/* for OSF1 Digital Unix */
+#if (! defined(HAVE_ISINF) && defined(HAVE_FP_CLASS) && defined(HAVE_FP_CLASS_H))
+# define HAVE_ISINF 1
+# define isinf(a) (fp_class(a) == FP_NEG_INF || fp_class(a) == FP_POS_INF)
+#endif
+
+#if (! defined(HAVE_ISINF) && defined(HAVE_FPCLASSIFY) && defined(FP_PLUS_INF) && defined(FP_MINUS_INF))
+# define HAVE_ISINF 1
+# define isinf(a) (fpclassify(a) == FP_MINUS_INF || fpclassify(a) == FP_PLUS_INF)
+#endif
+
+#if (! defined(HAVE_ISINF) && defined(HAVE_FPCLASSIFY) && defined(FP_INFINITE))
+# define HAVE_ISINF 1
+# define isinf(a) (fpclassify(a) == FP_INFINITE)
+#endif
+
+/* for AIX */
+#if (! defined(HAVE_ISINF) && defined(HAVE_CLASS))
+# define HAVE_ISINF 1
+# define isinf(a) (class(a) == FP_MINUS_INF || class(a) == FP_PLUS_INF)
+#endif
+
+#if (! defined (HAVE_FINITE) && defined (HAVE_ISFINITE))
+# define HAVE_FINITE 1
+# define finite(a) isfinite(a)
+#endif
+
+#if (! defined(HAVE_FINITE) && defined(HAVE_ISNAN) && defined(HAVE_ISINF))
+# define HAVE_FINITE 1
+# define finite(a) (! isnan(a) && ! isinf(a))
+#endif
+
+#ifndef HAVE_FINITE
+#error "Can't compile without finite function"
+#endif
+
+#ifndef HAVE_ISINF
+#error "Can't compile without isinf function"
+#endif
+
+#endif /* RRD_CONFIG_H */
+])
+
+dnl Process Special Options
+dnl -----------------------------------
+
+dnl How the vertical axis label is printed
+AC_ARG_VAR(RRDGRAPH_YLEGEND_ANGLE,
+ [Vertical label angle: 90.0 (default) or 270.0])
+AC_DEFINE_UNQUOTED(RRDGRAPH_YLEGEND_ANGLE,${RRDGRAPH_YLEGEND_ANGLE:-90.0},
+ [Vertical label angle: 90.0 (default) or 270.0])
+
+AC_ARG_ENABLE(rrdcgi,[ --disable-rrdcgi disable building of rrdcgi],
+[],[enable_rrdcgi=yes])
+
+dnl Check if we run on a system that has fonts
+AC_ARG_WITH(rrd-default-font,
+[ --with-rrd-default-font=[OPTIONS] set the full path to your default font.],
+[RRD_DEFAULT_FONT=$withval],[
+ if test -d ${WINDIR:-nodir}/cour.ttf ; then
+ RRD_DEFAULT_FONT=`cd $WINDIR;pwd`/cour.ttf
+ else
+ RRD_DEFAULT_FONT='$(fontsdir)/$(fonts_DATA)'
+ fi
+])
+
+dnl Use mmap in rrd_update instead of seek+write
+AC_ARG_ENABLE([mmap],
+[ --disable-mmap disable mmap in rrd_update, use seek+write instead],
+[],
+[enable_mmap=yes])
+
+
+ AC_ARG_ENABLE(pthread,[ --disable-pthread disable multithread support],
+[],[enable_pthread=yes])
+
+
+
+CONFIGURE_PART(Audit Compilation Environment)
+
+
+dnl Check for the compiler and static/shared library creation.
+AC_PROG_CC
+AC_PROG_CPP
+AC_PROG_LIBTOOL
+
+dnl which flags does the compile support?
+if test "$GCC" = "yes"; then
+ for flag in -fno-strict-aliasing -Wall -std=gnu99 -pedantic -Wshadow -Wpointer-arith -Wcast-align -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -Winline -W; do
+ oCFLAGS=$CFLAGS
+ CFLAGS="$CFLAGS $flag"
+ cachename=rd_cv_gcc_flag_`echo $flag|sed 's/[[^A-Za-z]]/_/g'`
+ AC_CACHE_CHECK([if gcc likes the $flag flag], $cachename,
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[return 0 ]])],[eval $cachename=yes],[eval $cachename=no])])
+ if eval test \$$cachename = no; then
+ CFLAGS=$oCFLAGS
+ fi
+ done
+fi
+
+
+
+AC_SUBST(RRD_DEFAULT_FONT)
+
+CONFIGURE_PART(Checking for Header Files)
+
+dnl Checks for header files.
+AC_HEADER_STDC
+AC_HEADER_DIRENT
+AC_CHECK_HEADERS(sys/stat.h sys/types.h fcntl.h locale.h fp_class.h malloc.h unistd.h ieeefp.h math.h sys/times.h sys/param.h sys/resource.h float.h)
+
+dnl Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_HEADER_TIME
+AC_STRUCT_TM
+
+dnl Checks for libraries.
+AC_CHECK_FUNC(acos, , AC_CHECK_LIB(m, acos))
+
+dnl add pic flag in any case this makes sure all our code is relocatable
+eval `./libtool --config | grep pic_flag`
+CFLAGS="$CFLAGS $pic_flag"
+
+CONFIGURE_PART(Test Library Functions)
+
+dnl Checks for library functions.
+AC_FUNC_STRFTIME
+AC_FUNC_VPRINTF
+
+AC_C_BIGENDIAN
+
+dnl for each function found we get a definition in config.h
+dnl of the form HAVE_FUNCTION
+
+AC_CHECK_FUNCS(tzset mbstowcs opendir readdir chdir chroot getuid setlocale strerror strerror_r snprintf vsnprintf fpclass class fp_class isnan memmove strchr mktime getrusage gettimeofday posix_fadvise madvise)
+
+AC_CHECK_DECLS(fdatasync, [], [], [#include <unistd.h>])
+AC_CHECK_DECLS(posix_fadvise, [], [], [#define _XOPEN_SOURCE 600
+#include <fcntl.h>])
+AC_CHECK_DECLS(madvise, [], [], [#include <sys/mman.h>])
+
+if test "x$enable_mmap" = xyes; then
+ case "$host" in
+ *cygwin*)
+ # the normal mmap test does not work in cygwin
+ AC_CHECK_FUNCS(mmap)
+ if [ "x${ac_cv_func_mmap}" = xyes ]; then
+ ac_cv_func_mmap_fixed_mapped=yes
+ fi
+ ;;
+ *)
+ AC_FUNC_MMAP
+ ;;
+ esac
+fi
+
+
+CONFIGURE_PART(IEEE Math Checks)
+
+dnl HP-UX 11.00 does not have finite but does have isfinite as a macro so we need
+dnl actual code to check if this works
+AC_CHECK_FUNCS(fpclassify, ,
+ [AC_MSG_CHECKING(for fpclassify with <math.h>)
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <math.h>
+volatile int x;volatile float f; ]], [[x = fpclassify(f)]])],[AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_FPCLASSIFY)],[AC_MSG_RESULT(no)])])
+AC_CHECK_FUNCS(finite, ,
+ [AC_CHECK_FUNCS(isfinite, ,
+ [AC_MSG_CHECKING(for isfinite with <math.h>)
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <math.h>
+volatile int x;volatile float f; ]], [[x = isfinite(f)]])],[AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_ISFINITE)],[AC_MSG_RESULT(no)])])])
+AC_CHECK_FUNCS(isinf, ,
+ [AC_MSG_CHECKING(for isinf with <math.h>)
+ AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <math.h>
+volatile int x;volatile float f; ]], [[x = isinf(f)]])],[AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_ISINF)],[AC_MSG_RESULT(no)])])
+
+AC_FULL_IEEE
+
+CONFIGURE_PART(Resolve Portability Issues)
+
+dnl what does realloc do if it gets called with a NULL pointer
+
+AC_CACHE_CHECK([if realloc can deal with NULL], rd_cv_null_realloc,
+[AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdlib.h>
+ int main(void){
+ char *x = NULL;
+ x = realloc (x,10);
+ if (x==NULL) return 1;
+ return 0;
+ }]])],[rd_cv_null_realloc=yes],[rd_cv_null_realloc=nope],[:])])
+
+if test x"$rd_cv_null_realloc" = xnope; then
+AC_DEFINE(NO_NULL_REALLOC)
+fi
+
+AC_LANG_PUSH(C)
+dnl solaris has some odd defines it needs in order to propperly compile ctime_r
+AC_MSG_CHECKING([if ctime_r need special care to act posixly correct])
+AC_LINK_IFELSE(
+ AC_LANG_PROGRAM(
+ [[#include <time.h>]],
+ [[ctime_r(NULL,NULL,0)]]
+ ),
+ [ CPPFLAGS="$CPPFLAGS -D_POSIX_PTHREAD_SEMANTICS"
+ AC_LINK_IFELSE(
+ AC_LANG_PROGRAM(
+ [[#include <time.h>]],
+ [[ctime_r(NULL,NULL)]]
+ ),
+ [AC_MSG_RESULT([yes, this seems to be solaris style])],
+ [AC_MSG_ERROR([Can't figure how to compile ctime_r])]
+ )
+ ],
+ [ AC_LINK_IFELSE(
+ AC_LANG_PROGRAM(
+ [[#include <time.h>]],
+ [[ctime_r(NULL,NULL)]]
+ ),
+ [AC_MSG_RESULT(no)],
+ [AC_MSG_ERROR([Can't figure how to compile ctime_r])]
+ )
+ ]
+)
+AC_LANG_POP(C)
+
+dnl Check for pthreads
+dnl http://autoconf-archive.cryp.to/acx_pthread.m4
+
+AC_SUBST(MULTITHREAD_CFLAGS)
+AC_SUBST(MULTITHREAD_LDFLAGS)
+
+if test $enable_pthread != no; then
+ ACX_PTHREAD([
+ MULTITHREAD_CFLAGS=$PTHREAD_CFLAGS
+ MULTITHREAD_LDFLAGS=$PTHREAD_LIBS
+ ],
+ [])
+fi
+
+dnl since we use lots of *_r functions all over the code we better
+dnl make sure they are known
+
+if test "x$x_rflag" != "xno"; then
+ CPPFLAGS="$CPPFLAGS $x_rflag"
+fi
+
+AM_CONDITIONAL(BUILD_MULTITHREAD,[test $enable_pthread != no])
+
+AC_LANG_PUSH(C)
+dnl see if we have to include malloc/malloc.h
+AC_MSG_CHECKING([do we need malloc/malloc.h])
+AC_LINK_IFELSE(
+ AC_LANG_PROGRAM(
+ [[#include <stdlib.h>]],
+ [[malloc(1)]]
+ ),
+ [ AC_MSG_RESULT([nope, works out of the box]) ],
+ [ AC_LINK_IFELSE(
+ AC_LANG_PROGRAM(
+ [[#include <stdlib.h>
+ #include <malloc/malloc.h>]],
+ [[malloc(1)]]
+ ),
+ [AC_DEFINE(NEED_MALLOC_MALLOC_H)
+ AC_MSG_RESULT([yes we do])],
+ [AC_MSG_ERROR([Can not figure how to compile malloc])]
+ )
+ ]
+)
+AC_LANG_POP(C)
+
+CONFIGURE_PART(Find 3rd-Party Libraries)
+
+
+AM_CONDITIONAL(BUILD_RRDCGI,[test $enable_rrdcgi != no])
+
+CORE_LIBS="$LIBS"
+
+EX_CHECK_ALL(art_lgpl_2, art_vpath_add_point, libart_lgpl/libart.h, libart-2.0, 2.3.17, ftp://ftp.gnome.org/pub/GNOME/sources/libart_lgpl/2.3/, /usr/include/libart-2.0)
+EX_CHECK_ALL(z, zlibVersion, zlib.h, zlib, 1.2.3, http://www.gzip.org/zlib/, "")
+EX_CHECK_ALL(png, png_access_version_number, png.h, libpng, 1.2.10, http://prdownloads.sourceforge.net/libpng/, "")
+EX_CHECK_ALL(freetype, FT_Init_FreeType, ft2build.h, freetype2, 2.1.10, http://prdownloads.sourceforge.net/freetype/, /usr/include/freetype2)
+
+if test "$EX_CHECK_ALL_ERR" = "YES"; then
+ AC_MSG_ERROR([Please fix the library issues listed above and try again.])
+fi
+
+ALL_LIBS="$LIBS"
+LIBS=
+
+AC_SUBST(CORE_LIBS)
+AC_SUBST(ALL_LIBS)
+
+CONFIGURE_PART(Prep for Building Language Bindings)
+
+dnl Check for Perl.
+AC_PATH_PROG(PERL, perl, no)
+
+AC_ARG_ENABLE(perl,[ --disable-perl do not build the perl modules],
+[],[enable_perl=yes])
+
+
+AC_ARG_VAR(PERLCC, [[] C compiler for Perl modules])
+AC_ARG_VAR(PERLCCFLAGS, [[] CC flags for Perl modules])
+AC_ARG_VAR(PERLLD, [[same as PERLCC] Linker for Perl modules])
+AC_ARG_VAR(PERLLDFLAGS, [[] LD flags for Perl modules])
+
+if test "x$PERL" = "xno" -o x$enable_perl = xno; then
+ COMP_PERL=
+else
+ COMP_PERL="perl_piped perl_shared"
+ AC_MSG_CHECKING(for the perl version you are running)
+ PERL_VERSION=`$PERL -MConfig -e 'print $Config{version}'`
+ AC_MSG_RESULT($PERL_VERSION)
+ if test -z "$PERLCC"; then
+ AC_MSG_CHECKING(for the C compiler perl wants to use to build its modules)
+ perlcc=`$PERL -MConfig -e 'print $Config{cc}'`
+ AC_MSG_RESULT($perlcc)
+ if test ! -x "$perlcc"; then
+ AC_PATH_PROG(PERL_CC, ${perlcc}, no)
+ if test "$PERL_CC" = "no"; then
+ AC_MSG_WARN([
+I would not find the Compiler ($perlcc) that was originally used to compile
+your perl binary. You should either make sure that this compiler is
+available on your system, pick an other compiler and set PERLCC
+appropriately, or use a different perl setup that was compiled locally.
+
+I will disable the compilation of the RRDs perl module for now.
+])
+ COMP_PERL="perl_piped"
+ fi
+ fi
+ fi
+fi
+
+AC_MSG_CHECKING(Perl Modules to build)
+AC_MSG_RESULT(${COMP_PERL:-No Perl Modules will be built})
+
+# Options to pass when configuring perl module
+ppref=$prefix
+test "$ppref" = "NONE" && ppref=$ac_default_prefix
+
+PERL_MAKE_OPTIONS="PREFIX=$ppref LIB=$ppref/lib/perl/$PERL_VERSION"
+
+dnl pass additional perl options when generating Makefile from Makefile.PL
+AC_ARG_ENABLE(perl-site-install,
+[ --enable-perl-site-install by default the rrdtool perl modules are installed
+ together with rrdtool in $prefix/lib/perl. You have to
+ put a 'use lib qw($prefix/lib/perl)' into your scripts
+ when you want to use them. When you set this option
+ the perl modules will get installed wherever
+ your perl setup thinks it is best.],
+[PERL_MAKE_OPTIONS=],[])
+
+if test ! -z "$PERLCC"; then
+ PERL_MAKE_OPTIONS="$PERL_MAKE_OPTIONS CC=$PERLCC"
+
+ if test ! -z "$PERLCCFLAGS"; then
+ PERL_MAKE_OPTIONS="$PERL_MAKE_OPTIONS CCFLAGS=$PERLCCFLAGS"
+ fi
+
+ if test -z "$PERLLD"; then
+ PERLLD=$PERLCC
+ fi
+ PERL_MAKE_OPTIONS="$PERL_MAKE_OPTIONS LD=$PERLLD"
+
+ if test ! -z "$PERLLDFLAGS"; then
+ PERL_MAKE_OPTIONS="$PERL_MAKE_OPTIONS LDFLAGS=$PERLLDFLAGS"
+ fi
+fi
+
+AC_ARG_WITH(perl-options,
+[ --with-perl-options=[OPTIONS] options to pass on command-line when
+ generating Makefile from Makefile.PL. If you set this
+ option, interesting things may happen unless you know
+ what you are doing!],
+[PERL_MAKE_OPTIONS=$withval])
+
+AC_SUBST(PERL_MAKE_OPTIONS)
+AC_SUBST(PERL)
+AC_SUBST(COMP_PERL)
+AC_SUBST(PERL_VERSION)
+
+dnl Check for Ruby.
+AC_PATH_PROG(RUBY, ruby, no)
+
+AC_ARG_ENABLE(ruby,[ --disable-ruby do not build the ruby modules],
+[],[enable_ruby=yes])
+
+AC_MSG_CHECKING(if ruby modules can be built)
+
+if test "x$RUBY" = "xno" -o x$enable_ruby = xno; then
+ COMP_RUBY=
+ AC_MSG_RESULT(No .. Ruby not found or disabled)
+else
+ if $RUBY -e 'require "mkmf"' >/dev/null 2>&1; then
+ COMP_RUBY="ruby"
+ AC_MSG_RESULT(YES)
+ else
+ COMP_RUBY=
+ AC_MSG_RESULT(Ruby found but mkmf is missing! Install the -dev package)
+ fi
+fi
+
+
+dnl pass additional ruby options when generating Makefile from Makefile.PL
+AC_ARG_ENABLE(ruby-site-install,
+[ --enable-ruby-site-install by default the rrdtool ruby modules are installed
+ together with rrdtool in $prefix/lib/ruby. You have to
+ add $prefix/lib/ruby/$ruby_version/$sitearch to you $: variable
+ for ruby to find the RRD.so file.],
+[RUBY_MAKE_OPTIONS=],[RUBY_MAKE_OPTIONS="sitedir=$prefix/lib/ruby"])
+
+
+AC_ARG_WITH(ruby-options,
+[ --with-ruby-options=[OPTIONS] options to pass on command-line when
+ generating Makefile from extconf.rb. If you set this
+ option, interesting things may happen unless you know
+ what you are doing!],
+[RUBY_MAKE_OPTIONS=$withval])
+
+AC_SUBST(RUBY_MAKE_OPTIONS)
+AC_SUBST(RUBY)
+AC_SUBST(COMP_RUBY)
+
+
+enable_tcl_site=no
+
+AC_ARG_ENABLE(tcl,[ --disable-tcl do not build the tcl modules],
+[],[enable_tcl=yes])
+
+if test "$enable_tcl" = "yes"; then
+ dnl Check for Tcl.
+ withval=""
+ AC_ARG_WITH(tcllib,[ --with-tcllib=DIR location of the tclConfig.sh])
+ enable_tcl=no
+ for dir in $withval /usr/lib /usr/local/lib; do
+ AC_MSG_CHECKING(for tclConfig.sh in $dir)
+ if test -f "$dir/tclConfig.sh" ; then
+ tcl_config=$dir/tclConfig.sh
+ enable_tcl=yes
+ AC_MSG_RESULT(yes)
+ break
+ else
+ AC_MSG_RESULT(no)
+ fi
+ done
+
+ if test "$enable_tcl" = "no"; then
+ AC_MSG_WARN([tclConfig.sh not found - Tcl interface won't be built])
+ else
+ . $tcl_config
+ TCL_PACKAGE_DIR="$TCL_PACKAGE_PATH/tclrrd$VERSION"
+ fi
+ AC_ARG_ENABLE(tcl,[ --enable-tcl-site install the tcl extension in the tcl tree],
+ [],[enable_tcl_site=yes])
+
+fi
+
+AM_CONDITIONAL(BUILD_TCL, test "$enable_tcl" = "yes" )
+AM_CONDITIONAL(BUILD_TCL_SITE, test "$enable_tcl_site" = "yes" )
+
+AC_SUBST(TCL_PREFIX)
+AC_SUBST(TCL_SHLIB_CFLAGS)
+AC_SUBST(TCL_SHLIB_LD)
+AC_SUBST(TCL_SHLIB_SUFFIX)
+AC_SUBST(TCL_PACKAGE_PATH)
+AC_SUBST(TCL_LD_SEARCH_FLAGS)
+AC_SUBST(TCL_STUB_LIB_SPEC)
+AC_SUBST(TCL_VERSION)
+AC_SUBST(TCL_PACKAGE_DIR)
+
+AC_ARG_ENABLE(python,[ --disable-python do not build the python modules],
+[],[enable_python=yes])
+
+if test "$enable_python" = "yes"; then
+dnl Check for python
+AM_PATH_PYTHON(2.3,[],[enable_python=no])
+AM_CHECK_PYTHON_HEADERS(,[enable_python=no;AC_MSG_WARN(could not find Python headers)])
+fi
+
+if test x$enable_python = xno; then
+ COMP_PYTHON=
+else
+ COMP_PYTHON="python"
+fi
+
+AC_SUBST(COMP_PYTHON)
+
+dnl Check for nroff
+AC_PATH_PROGS(NROFF, gnroff nroff)
+AC_PATH_PROGS(TROFF, groff troff)
+
+AC_ARG_VAR(RRDDOCDIR, [[DATADIR/doc/PACKAGE-VERSION] Documentation directory])
+if test -z "$RRDDOCDIR"; then
+ RRDDOCDIR='${datadir}/doc/${PACKAGE}-${VERSION}'; fi
+
+
+CONFIGURE_PART(Apply Configuration Information)
+
+AC_CONFIG_FILES([examples/shared-demo.pl])
+AC_CONFIG_FILES([examples/piped-demo.pl])
+AC_CONFIG_FILES([examples/stripes.pl])
+AC_CONFIG_FILES([examples/bigtops.pl])
+AC_CONFIG_FILES([examples/minmax.pl])
+AC_CONFIG_FILES([examples/4charts.pl])
+AC_CONFIG_FILES([examples/perftest.pl])
+AC_CONFIG_FILES([examples/Makefile])
+AC_CONFIG_FILES([doc/Makefile])
+AC_CONFIG_FILES([src/Makefile])
+AC_CONFIG_FILES([bindings/Makefile])
+AC_CONFIG_FILES([bindings/tcl/Makefile])
+AC_CONFIG_FILES([bindings/tcl/ifOctets.tcl])
+AC_CONFIG_FILES([Makefile])
+
+AC_CONFIG_COMMANDS([default],[[ chmod +x examples/*.pl]],[[]])
+AC_OUTPUT
+
+AC_MSG_CHECKING(in)
+AC_MSG_RESULT(and out again)
+
+echo $ECHO_N "ordering CD from http://tobi.oetiker.ch/wish $ECHO_C" 1>&6
+sleep 1
+echo $ECHO_N ".$ECHO_C" 1>&6
+sleep 1
+echo $ECHO_N ".$ECHO_C" 1>&6
+sleep 1
+echo $ECHO_N ".$ECHO_C" 1>&6
+sleep 1
+echo $ECHO_N ".$ECHO_C" 1>&6
+sleep 1
+AC_MSG_RESULT([ just kidding ;-)])
+echo
+echo "----------------------------------------------------------------"
+echo "Config is DONE!"
+echo
+echo " With MMAP IO: $ac_cv_func_mmap_fixed_mapped"
+echo " Perl Modules: $COMP_PERL"
+echo " Perl Binary: $PERL"
+echo " Perl Version: $PERL_VERSION"
+echo " Perl Options: $PERL_MAKE_OPTIONS"
+echo " Ruby Modules: $COMP_RUBY"
+echo " Ruby Binary: $RUBY"
+echo " Ruby Options: $RUBY_MAKE_OPTIONS"
+echo " Build Tcl Bindings: $enable_tcl"
+echo " Build Python Bindings: $enable_python"
+echo " Build rrdcgi: $enable_rrdcgi"
+echo " Build librrd MT: $enable_pthread"
+echo
+echo
+echo "Type 'make' to compile the software and use 'make install' to "
+echo "install everything to: $prefix."
+echo
+echo " ... that wishlist is NO JOKE. If you find RRDtool useful"
+echo "make me happy. Go to http://tobi.oetiker.ch/wish and"
+echo "place an order."
+echo
+echo " -- Tobi Oetiker <tobi@oetiker.ch>"
+echo "----------------------------------------------------------------"
diff --git a/program/debian/README.Debian b/program/debian/README.Debian
--- /dev/null
@@ -0,0 +1,33 @@
+rrdtool for Debian
+----------------------
+
+The RRDtool distribution is split into several packages:
+
+
+Package: rrdtool
+ Command-line utilities and documentation
+
+Package: librrd0
+ Shared library
+
+Package: librrd0-dev
+ Static library
+
+Package: librrds-perl
+ Perl interface using the shared library (RRDs)
+
+Package: librrdp-perl
+ Perl interface using command pipes to the rrdtool program (RRDp)
+
+Package: rrdtool-tcl
+ TCL interface using the shared library (librrd)
+
+
+Original by
+
+ -- Matt Zimmerman <mdz@csh.rit.edu>, Fri, 10 Sep 1999 10:53:19 -0700
+
+Hacked for 1.1.x by
+
+ -- Mike Slifcak <slif@bellsouth.net>, Tue , 18 May 2004 20:38:40 +0200
+
diff --git a/program/debian/build_freetype.sh b/program/debian/build_freetype.sh
--- /dev/null
@@ -0,0 +1,42 @@
+:
+# Build freetype2 for rrdtool on Debian/Linux
+# 12-May-2004 Mike Slifcak
+
+FOUND=`find /lib /usr/lib /usr/local/lib -name libfreetype.a | wc -l`
+FOUND=`echo $FOUND`
+if [ $FOUND -lt 1 ] ; then
+########################################
+## Build the independent object freetype2
+########################################
+cd /tmp
+rm -rf freetype*/
+if [ ! -e freetype*gz ] ; then
+ echo "get freetype-2.1.8 or stable from http://freetype.sf.net/"
+ exit 1
+fi
+tar tzf freetype*gz > /dev/null 2>&1
+RC=$?
+if [ $RC -ne 0 ] ; then
+ echo "Need one good freetype*gz. Just one. In /tmp. Thanks!"
+ exit 1
+fi
+
+echo -n "Testing "
+ls freetype*gz
+tar xzf freetype*gz
+echo "Building freetype"
+cd freetype-*/
+./configure --disable-shared > cfg.out 2>&1
+make > make.out 2>&1
+make install > inst.out 2>&1
+grep Error *.out
+if [ $? -ne 1 ] ; then
+ echo "Building freetype failed. See `pwd`/*.out for details"
+ exit 1
+fi
+cd ..
+fi ## skip freetype build
+
+echo "Building freetype succeeded."
+
+exit 0
diff --git a/program/debian/changelog b/program/debian/changelog
--- /dev/null
+++ b/program/debian/changelog
@@ -0,0 +1,379 @@
+rrdtool (1.1.0-1) unstable; urgency=low
+
+ * Fixed build dependencies
+ * Added libfreetype6-dev to build dependencies
+ * Changed rules file to compile against Debian version of freetype.
+
+ -- Peter Hirdina <Peter.Hirdina@gmx.net> Mon, 7 Jun 2004 17:10:10 +0200
+
+rrdtool (1.1.0) unstable; urgency=low
+
+ * grafted "debian" directory from 1.0.46 source
+ * no tcl package support due to build failures (see ##NO_TCL )
+
+ -- Mike Slifcak <> Wed, 12 May 2004 20:53:16 -0400
+
+rrdtool (1.0.46-3) unstable; urgency=low
+
+ * Add dependencies to librrd0-dev to ensure that static linking is
+ possible: libgd-gif1-dev, zlib1g-dev
+ * #include <stdio.h> in rrd.h (Closes: #238849)
+ * Only link rrdcgi with -lcgi, not librrd
+
+ -- Matt Zimmerman <mdz@debian.org> Fri, 19 Mar 2004 11:38:39 -0800
+
+rrdtool (1.0.46-2) unstable; urgency=medium
+
+ * Fix tcl module installation (Closes: #231171)
+
+ -- Matt Zimmerman <mdz@debian.org> Wed, 4 Feb 2004 15:35:46 -0800
+
+rrdtool (1.0.46-1) unstable; urgency=low
+
+ * New upstream release
+
+ -- Matt Zimmerman <mdz@debian.org> Mon, 12 Jan 2004 23:22:09 -0800
+
+rrdtool (1.0.45-1) unstable; urgency=low
+
+ * New upstream release
+ * Deal with changed tclrrd naming scheme
+ * Build with tcl8.4
+ * Remove old dpkg-dev build-dependency; even woody is new enough
+
+ -- Matt Zimmerman <mdz@debian.org> Wed, 17 Dec 2003 14:09:11 -0800
+
+rrdtool (1.0.42-2) unstable; urgency=low
+
+ * Build-Depends: libpng12-dev instead of libpng3-dev (when will the madness end?)
+ (Closes: #195224
+ I hope this will also fix weird version mismatch problems (Closes: #194900)
+
+ -- Matt Zimmerman <mdz@debian.org> Sat, 31 May 2003 15:06:42 -0400
+
+rrdtool (1.0.42-1) unstable; urgency=low
+
+ * New upstream release
+ * librrd0-dev Section: libdevel
+ * librrd[sp]-perl Section: perl
+ * Build with libpng3 (Closes: #189493)
+
+ -- Matt Zimmerman <mdz@debian.org> Thu, 15 May 2003 19:44:37 -0400
+
+rrdtool (1.0.40-2) unstable; urgency=low
+
+ * Correctly suppress non-image output when writing image to stdout
+ (Closes: #182217)
+
+ -- Matt Zimmerman <mdz@debian.org> Sat, 1 Mar 2003 16:59:39 -0500
+
+rrdtool (1.0.40-1) unstable; urgency=low
+
+ * New upstream release
+ * Remove unnecessary rrd_free to avoid crash on invalid option
+ (Closes: #166156)
+ * Clean example source code; some libtool cruft was getting installed in
+ /usr/share/doc/examples
+
+ -- Matt Zimmerman <mdz@debian.org> Tue, 17 Dec 2002 22:11:46 -0500
+
+rrdtool (1.0.39-2) unstable; urgency=low
+
+ * Rebuild for perl 5.8 (Closes: #158728)
+
+ -- Matt Zimmerman <mdz@debian.org> Sun, 25 Aug 2002 17:29:41 -0400
+
+rrdtool (1.0.39-1) unstable; urgency=low
+
+ * New upstream release
+
+ -- Matt Zimmerman <mdz@debian.org> Wed, 31 Jul 2002 00:40:00 -0400
+
+rrdtool (1.0.38-1) unstable; urgency=low
+
+ * New upstream release (Closes: #148486)
+
+ -- Matt Zimmerman <mdz@debian.org> Wed, 29 May 2002 12:28:28 -0400
+
+rrdtool (1.0.35-2) unstable; urgency=low
+
+ * Remove CVS directories with rm -rf. This broke autobuilds entirely.
+ (Closes: #140075)
+
+ -- Matt Zimmerman <mdz@debian.org> Tue, 26 Mar 2002 17:15:01 -0500
+
+rrdtool (1.0.35-1) unstable; urgency=low
+
+ * New upstream release.
+
+ -- Matt Zimmerman <mdz@debian.org> Sun, 24 Mar 2002 11:29:10 -0500
+
+rrdtool (1.0.33-9) unstable; urgency=low
+
+ * Call dh_shlibdeps with -l to allow it to find dependencies correctly
+ (Closes: #118629)
+ * Add call to ldconfig in postrm of librrd0
+ * Remove .cvsignore files from installed examples
+
+ -- Matt Zimmerman <mdz@debian.org> Sat, 10 Nov 2001 14:14:51 -0500
+
+rrdtool (1.0.33-8) unstable; urgency=low
+
+ * Use AM_MAINTAINER_MODE to keep the makefiles from trying to rebuild
+ autoconf/automake stuff during the build (Closes: #117599)
+ * Regenerated everything AGAIN.
+ * Use DESTDIR instead of prefix= during "make install"
+
+ -- Matt Zimmerman <mdz@debian.org> Mon, 29 Oct 2001 22:22:07 -0500
+
+rrdtool (1.0.33-7) unstable; urgency=low
+
+ * Add an example to the rrddump documentation showing what is necessary
+ to transfer an RRD from one architecture to another. (Closes: #117147)
+ * Regenerated autoconf/automake configs
+
+ -- Matt Zimmerman <mdz@debian.org> Mon, 29 Oct 2001 02:28:16 -0500
+
+rrdtool (1.0.33-6) unstable; urgency=low
+
+ * Spelling corrections from Martin Schulze (Closes: #110784)
+
+ -- Matt Zimmerman <mdz@debian.org> Sat, 8 Sep 2001 13:13:22 -0400
+
+rrdtool (1.0.33-5) unstable; urgency=low
+
+ * Ship with pre-generated files from autoconf2.50/automake, rather than
+ pulling them in at build-time, in order to work around the current
+ libtool mess. This makes the diff less manageable, but should make
+ the build more stable.
+ * debian/control: remove build-dependencies on autoconf/automake/libtool
+ * debian/rules: don't call autoconf/automake
+ * Fix shlibs file (it was trying to use a substitution)
+ * configure.in: remove makefiles that no longer exist from AC_OUTPUT.
+ Apparently this is an error in autoconf 2.50.
+ * configure.in: Remove wacky logic to pull PIC flags out of libtool, and
+ let libtool do the right thing.
+ * debian/rules: shlibs.local hackery is no longer necessary, removed
+
+ -- Matt Zimmerman <mdz@debian.org> Fri, 3 Aug 2001 17:36:43 -0400
+
+rrdtool (1.0.33-4) unstable; urgency=low
+
+ * Add build-dependencies on libtool and autoconf (Closes: #105655)
+
+ -- Matt Zimmerman <mdz@debian.org> Tue, 17 Jul 2001 15:44:25 -0400
+
+rrdtool (1.0.33-3) unstable; urgency=low
+
+ * Update config.{sub,guess} from autotools-dev 20010702.1
+ (Closes: #105037)
+ * Update ltmain.sh for good measure, from libtool 1.4-1
+ * Build-Depend on autoconf, and run autoconf and aclocal from
+ debian/rules (in addition to automake)
+
+ -- Matt Zimmerman <mdz@debian.org> Sat, 14 Jul 2001 03:19:22 -0400
+
+rrdtool (1.0.33-2) unstable; urgency=low
+
+ * Rebuild with latest tcl8.3-dev, to fix shared object extension
+
+ -- Matt Zimmerman <mdz@debian.org> Sat, 3 Mar 2001 17:28:24 -0500
+
+rrdtool (1.0.33-1) unstable; urgency=low
+
+ * New upstream version.
+ * This is yet another patch release to fix some distribution/compilation
+ problems. There should be no visible changes for Debian users from
+ 1.0.32-3.
+
+ -- Matt Zimmerman <mdz@debian.org> Thu, 1 Mar 2001 02:27:53 -0500
+
+rrdtool (1.0.32-3) unstable; urgency=low
+
+ * Fix the tcl extension module to build against tcl8.3, rather than
+ tcl8.0 (Closes: #87357)
+ * debian/control: Build-depend on tcl8.3-dev
+ * debian/rules: tclconfigdir = /usr/lib/tcl8.3
+ * tcl/Makefile.am: (upstream) -I/usr/include/tcl@TCL_VERSION@
+ * configure.in: (upstream) AC_SUBST(TCL_VERSION)
+
+ -- Matt Zimmerman <mdz@debian.org> Sat, 24 Feb 2001 02:25:46 -0500
+
+rrdtool (1.0.32-2) unstable; urgency=low
+
+ * List changes to upstream source in the copyright file (as well as the
+ changelog)
+ * Link against libgd-gif instead of libgd, to restore GIF support
+
+ -- Matt Zimmerman <mdz@debian.org> Fri, 23 Feb 2001 15:27:37 -0500
+
+rrdtool (1.0.32-1) unstable; urgency=low
+
+ * New upstream version. Hopefully this one will last more than 24
+ hours (Closes: #75771)
+
+ -- Matt Zimmerman <mdz@debian.org> Wed, 21 Feb 2001 20:13:09 -0500
+
+rrdtool (1.0.27-7) unstable; urgency=low
+
+ * debian/rules: Updated for new Perl policy
+ * debian/rules: Don't use install-stamp; it causes more problems than it
+ prevents
+ * debian/rules: Don't call dh_suidregister; we didn't use it anyway
+ * debian/control: Build-depend on the new debhelper for dh_perl
+ * debian/control: rrdtool-tcl only seems to work with 8.0, so depend on
+ tcl8.0
+ * debian/control: Build-depend on perl 5.6.0+
+ * debian/librrd?-perl: Updated for new Perl filesystem layout
+
+ -- Matt Zimmerman <mdz@debian.org> Thu, 15 Feb 2001 19:04:11 -0500
+
+rrdtool (1.0.27-6) unstable; urgency=low
+
+ * debian/rules: Handle DEB_BUILD_OPTIONS debug, nostrip
+ * debian/rules: Separate out "configure" target
+ * debian/rules: Miscellaneous cleanup
+ * Recompile librrds-perl against perl-5.6
+
+ -- Matt Zimmerman <mdz@debian.org> Tue, 19 Dec 2000 21:23:55 -0500
+
+rrdtool (1.0.27-5) unstable; urgency=low
+
+ * Fix Build-Depends to reflect our dependency on dpkg-dev >= 1.7.0 |
+ librrd0. The old dpkg can only calculate our dependencies correctly
+ if the runtime library is installed on the system, so potato users
+ must either install the potato librrd0 or the new dpkg-dev
+ (Closes: #77479)
+
+ -- Matt Zimmerman <mdz@debian.org> Mon, 20 Nov 2000 20:39:55 -0500
+
+rrdtool (1.0.27-4) unstable; urgency=low
+
+ * Updated to comply with policy 3.2.1.0:
+ - Don't build with -g by default
+ * Build-Depend on tcl8.0-dev (not tcl-dev), since that is the only
+ version we currently work with.
+ * Added automake to Build-Depends (oops)
+
+ -- Matt Zimmerman <mdz@debian.org> Sat, 18 Nov 2000 22:29:02 -0500
+
+rrdtool (1.0.27-3) unstable; urgency=low
+
+ * Big packaging cleanup release.
+ * Fixed perl module compilation to link with the shared library, rather
+ than including a copy of the static library inside the .so (also fixes
+ a lintian error, shlib-with-non-pic-code in librrds-perl)
+ * Fixed tcl module compilation to link with the shared library, rather
+ than including a copy of the static library inside the .so (also fixes
+ a lintian error, shlib-with-non-pic-code in rrdtool-tcl)
+ * The above shrunk the size of the perl and tcl library packages from
+ 60k each to about 20k (perl) and 16k (tcl)
+ * We can now use shlibdeps to calculate the dependencies for
+ librrds-perl, so those will be more correct now
+ * Rather than include Makefile.in changes in the .diff.gz, just keep our
+ modifications to Makefile.am and run automake in debian/rules. This
+ makes the .diff.gz much smaller and cleaner
+ * Fixed shared library dependencies all around to be smarter
+ * rrdtool package now Suggests librrds-perl (rather than depending on
+ perl), as it is only needed for an auxiliary script for converting
+ mrtg data
+
+ -- Matt Zimmerman <mdz@debian.org> Fri, 10 Nov 2000 19:34:05 -0500
+
+rrdtool (1.0.27-2) unstable; urgency=low
+
+ * Updated maintainer email address (now official maintainer)
+
+ -- Matt Zimmerman <mdz@debian.org> Wed, 1 Nov 2000 22:44:41 -0500
+
+rrdtool (1.0.27-1) unstable; urgency=low
+
+ * New upstream version.
+
+ -- Matt Zimmerman <mdz@csh.rit.edu> Wed, 13 Sep 2000 13:10:14 -0400
+
+rrdtool (1.0.26-1) unstable; urgency=low
+
+ * New upstream version.
+
+ -- Matt Zimmerman <mdz@csh.rit.edu> Sun, 10 Sep 2000 16:34:08 -0400
+
+rrdtool (1.0.25-1) unstable; urgency=low
+
+ * New upstream version.
+
+ -- Matt Zimmerman <mdz@csh.rit.edu> Sun, 23 Jul 2000 02:23:53 -0400
+
+rrdtool (1.0.24-2) unstable; urgency=low
+
+ * Updated for libgd 1.8.3 (build depends, recompile)
+
+ -- Matt Zimmerman <mdz@csh.rit.edu> Fri, 7 Jul 2000 19:33:29 -0400
+
+rrdtool (1.0.24-1) unstable; urgency=low
+
+ * New upstream version (Closes: #61997).
+
+ -- Matt Zimmerman <mdz@csh.rit.edu> Fri, 30 Jun 2000 13:53:27 -0400
+
+rrdtool (1.0.17-1) unstable; urgency=low
+
+ * New upstream version.
+ * Now creates rrdtool-tcl, containing tcl bindings.
+
+ -- Matt Zimmerman <mdz@csh.rit.edu> Wed, 26 Apr 2000 13:16:24 -0700
+
+rrdtool (1.0.16-1) unstable; urgency=low
+
+ * New upstream version.
+ * Not released.
+
+ -- Matt Zimmerman <mdz@csh.rit.edu> Wed, 26 Apr 2000 10:53:36 -0700
+
+rrdtool (1.0.13-2) unstable; urgency=low
+
+ * Build-Depends: cgilib, zlib1g-dev, libpng2-dev, libgd1g-dev, groff,
+ debhelper. Hopefully that's everything.
+
+ -- Matt Zimmerman <mdz@csh.rit.edu> Fri, 7 Apr 2000 14:16:55 -0700
+
+rrdtool (1.0.13-1) unstable; urgency=low
+
+ * New upstream version (Closes: #58583)
+ * Added Build-Depends for cgilib (Closes: #55270)
+
+ -- Matt Zimmerman <mdz@csh.rit.edu> Thu, 23 Mar 2000 15:55:37 -0800
+
+rrdtool (1.0.10-1) unstable; urgency=low
+
+ * New upstream version.
+
+ -- Matt Zimmerman <mdz@csh.rit.edu> Mon, 10 Jan 2000 16:46:33 -0800
+
+rrdtool (1.0.7-3) unstable; urgency=low
+
+ * Non-i386 fixes, thanks to Christopher C Chimelis <chris@debian.org>:
+ * Changed 'clean' target in debian/rules to remove the auto-generated
+ Makefiles for the Perl modules
+ * Changed Depends: for librrds-perl to use shlibdeps
+
+ -- Matt Zimmerman <mdz@csh.rit.edu> Mon, 22 Nov 1999 10:47:13 -0800
+
+rrdtool (1.0.7-2) unstable; urgency=low
+
+ * Linked RRDs.so against libpng, closes: #49701
+
+ -- Matt Zimmerman <mdz@csh.rit.edu> Tue, 9 Nov 1999 15:02:40 -0800
+
+rrdtool (1.0.7-1) unstable; urgency=low
+
+ * Initial Release.
+ * Upstream source ships with cgilib-0.4, gd-1.3, libpng-1.0.3, and
+ zlib-1.3.3 (!). Build process is modified to skip building these, and
+ instead link with the Debian versions.
+ * Modifications for gd 1.6.1 included removal of GIF support (no longer
+ included with gd as of 1.6)
+
+ -- Matt Zimmerman <mdz@csh.rit.edu> Sat, 11 Sep 1999 13:29:29 -0700
+
+
diff --git a/program/debian/control b/program/debian/control
--- /dev/null
+++ b/program/debian/control
@@ -0,0 +1,98 @@
+Source: rrdtool
+Section: utils
+Priority: extra
+Maintainer: Matt Zimmerman <mdz@debian.org>
+Standards-Version: 3.2.1
+Build-Depends: debhelper (>= 3.0.5), cgilib (>= 0.5), zlib1g-dev (>= 1.2.1), tcl8.4-dev, perl (>= 5.8.0), libart-2.0-dev (>= 2.3.16), libpng12-dev (>= 1.2.5), libfreetype6-dev (>= 2.1.7)
+
+Package: rrdtool
+Architecture: any
+Depends: ${shlibs:Depends}
+Suggests: librrds-perl
+Description: Time-series data storage and display system (programs)
+ RRD is the Acronym for Round Robin Database. RRD is a system to store and
+ display time-series data (i.e. network bandwidth, machine-room temperature,
+ server load average). It stores the data in a very compact way that will
+ not expand over time, and it presents useful graphs by processing the data
+ to enforce a certain data density. It can be used either via simple wrapper
+ scripts (from shell or Perl) or via frontends that poll network devices and
+ put friendly user interface on it.
+ .
+ This package contains command line programs used to access and manipulate
+ RRDs.
+
+Package: librrd0
+Architecture: any
+Section: libs
+Priority: optional
+Depends: ${shlibs:Depends}
+Description: Time-series data storage and display system (runtime)
+ RRD is the Acronym for Round Robin Database. RRD is a system to store and
+ display time-series data (i.e. network bandwidth, machine-room temperature,
+ server load average). It stores the data in a very compact way that will
+ not expand over time, and it presents useful graphs by processing the data
+ to enforce a certain data density. It can be used either via simple wrapper
+ scripts (from shell or Perl) or via frontends that poll network devices and
+ put friendly user interface on it.
+ .
+ This package contains shared libraries used to access and manipulate RRDs.
+
+Package: librrd0-dev
+Architecture: any
+Section: libdevel
+Depends: librrd0 (= ${Source-Version}), ${shlibs:Depends}
+Description: Time-series data storage and display system (development)
+ RRD is the Acronym for Round Robin Database. RRD is a system to store and
+ display time-series data (i.e. network bandwidth, machine-room temperature,
+ server load average). It stores the data in a very compact way that will
+ not expand over time, and it presents useful graphs by processing the data
+ to enforce a certain data density. It can be used either via simple wrapper
+ scripts (from shell or Perl) or via frontends that poll network devices and
+ put friendly user interface on it.
+ .
+ This package contains libraries used to develop software that uses RRDs.
+
+Package: librrds-perl
+Architecture: any
+Section: perl
+Depends: ${perl:Depends}, ${shlibs:Depends}
+Description: Time-series data storage and display system (perl-shared)
+ RRD is the Acronym for Round Robin Database. RRD is a system to store and
+ display time-series data (i.e. network bandwidth, machine-room temperature,
+ server load average). It stores the data in a very compact way that will
+ not expand over time, and it presents useful graphs by processing the data
+ to enforce a certain data density. It can be used either via simple wrapper
+ scripts (from shell or Perl) or via frontends that poll network devices and
+ put friendly user interface on it.
+ .
+ This package contains a Perl interface to RRDs using a shared library.
+
+Package: librrdp-perl
+Architecture: all
+Section: perl
+Depends: ${perl:Depends}, rrdtool
+Description: Time-series data storage and display system (perl-piped)
+ RRD is the Acronym for Round Robin Database. RRD is a system to store and
+ display time-series data (i.e. network bandwidth, machine-room temperature,
+ server load average). It stores the data in a very compact way that will
+ not expand over time, and it presents useful graphs by processing the data
+ to enforce a certain data density. It can be used either via simple wrapper
+ scripts (from shell or Perl) or via frontends that poll network devices and
+ put friendly user interface on it.
+ .
+ This package contains a Perl interface to RRDs using command pipes.
+
+#NO_TCL Package: rrdtool-tcl
+#NO_TCL Architecture: any
+#NO_TCL Section: utils
+#NO_TCL Depends: ${shlibs:Depends}, tcl8.4
+#NO_TCL Description: Time-series data storage and display system (tcl)
+#NO_TCL RRD is the Acronym for Round Robin Database. RRD is a system to store and
+#NO_TCL display time-series data (i.e. network bandwidth, machine-room temperature,
+#NO_TCL server load average). It stores the data in a very compact way that will
+#NO_TCL not expand over time, and it presents useful graphs by processing the data
+#NO_TCL to enforce a certain data density. It can be used either via simple wrapper
+#NO_TCL scripts (from shell or Perl) or via frontends that poll network devices and
+#NO_TCL put friendly user interface on it.
+#NO_TCL .
+#NO_TCL This package contains a tcl interface to RRDs.
diff --git a/program/debian/copyright b/program/debian/copyright
--- /dev/null
+++ b/program/debian/copyright
@@ -0,0 +1,30 @@
+This package was debianized by Matt Zimmerman <mdz@debian.org> on
+Fri, 10 Sep 1999 10:53:19 -0700.
+
+Copied from 1.0.46 to 1.1.0 snapshot by Mike Slifcak on Wed May 12 20:53:16 EDT 2004
+
+The source package was downloaded from
+ http://oss.oetiker.ch/rrdtool/pub/
+
+Upstream Author(s): Tobias Oetiker <tobi@oetiker.ch>
+
+Modifications to upstream source:
+ - Build and install shared libraries
+ - Link everything dynamically
+ - Link with Debian versions of libraries rather than using the
+ supplied (old) versions
+
+Copyright:
+
+ RRDTOOL - Round Robin Database Tool
+ A tool for fast logging of numerical data graphical display
+ of this data.
+
+ Copyright (c) 1998, 1999 Tobias Oetiker
+ All rights reserved.
+
+ RRDtool is distributed under the terms of the GNU General Public License,
+ version 2.
+
+ On Debian GNU/Linux systems, you can find a copy of the GNU General Public
+ License in `/usr/share/common-licenses/GPL'.
diff --git a/program/debian/librrd0-dev.files b/program/debian/librrd0-dev.files
--- /dev/null
@@ -0,0 +1,5 @@
+usr/lib/librrd.a
+usr/lib/librrd.so
+usr/lib/librrd_th.a
+usr/lib/librrd_th.so
+usr/include/*
diff --git a/program/debian/librrd0.files b/program/debian/librrd0.files
--- /dev/null
@@ -0,0 +1,6 @@
+usr/lib/librrd.so.1.0.0
+usr/lib/librrd.so.1
+usr/lib/librrd.la
+usr/lib/librrd_th.so.1.0.0
+usr/lib/librrd_th.so.1
+usr/lib/librrd_th.la
diff --git a/program/debian/librrd0.postinst b/program/debian/librrd0.postinst
--- /dev/null
@@ -0,0 +1,49 @@
+#! /bin/sh
+# postinst script for #PACKAGE#
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postinst> `configure' <most-recently-configured-version>
+# * <old-postinst> `abort-upgrade' <new version>
+# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
+# <new-version>
+# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
+# <failed-install-package> <version> `removing'
+# <conflicting-package> <version>
+# for details, see /usr/doc/packaging-manual/
+#
+# quoting from the policy:
+# Any necessary prompting should almost always be confined to the
+# post-installation script, and should be protected with a conditional
+# so that unnecessary prompting doesn't happen if a package's
+# installation fails and the `postinst' is called with `abort-upgrade',
+# `abort-remove' or `abort-deconfigure'.
+
+case "$1" in
+ configure)
+
+ ldconfig
+
+ ;;
+
+ abort-upgrade|abort-remove|abort-deconfigure)
+
+ ;;
+
+ *)
+ echo "postinst called with unknown argument \`$1'" >&2
+ exit 0
+ ;;
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+exit 0
+
+
diff --git a/program/debian/librrd0.postrm b/program/debian/librrd0.postrm
--- /dev/null
@@ -0,0 +1,37 @@
+#! /bin/sh
+#
+# see: dh_installdeb(1)
+
+set -e
+
+# summary of how this script can be called:
+# * <postrm> `remove'
+# * <postrm> `purge'
+# * <old-postrm> `upgrade' <new-version>
+# * <new-postrm> `failed-upgrade' <old-version>
+# * <new-postrm> `abort-install'
+# * <new-postrm> `abort-install' <old-version>
+# * <new-postrm> `abort-upgrade' <old-version>
+# * <disappearer's-postrm> `disappear' <r>overwrit>r> <new-version>
+# for details, see /usr/share/doc/packaging-manual/
+
+case "$1" in
+ remove)
+ ldconfig
+ ;;
+
+ purge|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
+ ;;
+
+ *)
+ echo "postrm called with unknown argument \`$1'" >&2
+ exit 0
+
+esac
+
+# dh_installdeb will replace this with shell code automatically
+# generated by other debhelper scripts.
+
+#DEBHELPER#
+
+
diff --git a/program/debian/librrd0.shlibs b/program/debian/librrd0.shlibs
--- /dev/null
@@ -0,0 +1 @@
+librrd 0 librrd0 (>= 1.0.10-1)
diff --git a/program/debian/librrdp-perl.files b/program/debian/librrdp-perl.files
--- /dev/null
@@ -0,0 +1,2 @@
+usr/lib/perl5/RRDp.pm
+usr/share/man/man3/RRDp.3pm
diff --git a/program/debian/librrds-perl.files b/program/debian/librrds-perl.files
--- /dev/null
@@ -0,0 +1,3 @@
+usr/lib/perl5/auto
+usr/lib/perl5/RRDs.pm
+usr/share/man/man3/RRDs.3pm
diff --git a/program/debian/rrdtool-tcl.files b/program/debian/rrdtool-tcl.files
--- /dev/null
@@ -0,0 +1 @@
+usr/lib/tclrrd*
diff --git a/program/debian/rrdtool.files b/program/debian/rrdtool.files
--- /dev/null
@@ -0,0 +1,4 @@
+usr/bin
+usr/share/man/man1
+usr/share/doc/rrdtool
+usr/share/rrdtool
diff --git a/program/debian/rules b/program/debian/rules
--- /dev/null
+++ b/program/debian/rules
@@ -0,0 +1,178 @@
+#!/usr/bin/make -f
+#-*- makefile -*-
+# Made with the aid of dh_make, by Craig Small
+# Sample debian/rules that uses debhelper. GNU copyright 1997 by Joey Hess.
+# This version is for a hypothetical package that builds an
+# architecture-dependant package, as well as an architecture-independant
+# package.
+
+package:=rrdtool
+
+top_srcdir:=$(shell pwd)
+tmp:=$(top_srcdir)/debian/tmp
+
+# TCL installation stuff (must match tcl/Makefile.am)
+tclconfigdir = /usr/lib/tcl8.4
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+# This is the debhelper compatability version to use.
+export DH_COMPAT=2
+
+# Defaults
+CFLAGS := -O2
+
+# Handle DEB_BUILD_OPTIONS
+ifneq "$(findstring debug,$(DEB_BUILD_OPTIONS))" ""
+CFLAGS += -g
+endif
+
+# Whack the flags to compile additional dependent pieces
+CFLAGS += -I/usr/include/libart-2.0 -I/usr/include/freetype2
+
+##NO_TCL CFLAGS += -I/usr/include/tcl8.4
+
+configure: configure-stamp
+configure-stamp:
+ dh_testdir
+ # Make sure we can find tcl stuff (configure won't fail)
+ test -f $(tclconfigdir)/tclConfig.sh
+
+ # match the configure script to the build environment, as needed
+ #aclocal
+ #automake
+ #autoconf
+
+ # Configure C and Tcl stuff
+##NO_TCL CFLAGS="$(CFLAGS)" ./configure --prefix=/usr --enable-shared=yes --enable-static=yes --with-tcllib=$(tclconfigdir) --enable-local-libpng=yes --enable-local-zlib=yes
+ CFLAGS="$(CFLAGS)" ./configure --prefix=/usr --enable-shared=yes --enable-static=yes --enable-local-libpng=yes --enable-local-zlib=yes
+
+ # Configure Perl stuff
+ cd bindings/perl-piped && \
+ rm -f Makefile && \
+ perl Makefile.PL INSTALLDIRS=vendor
+
+ cd bindings/perl-shared && \
+ rm -f Makefile && \
+ perl Makefile.PL INSTALLDIRS=vendor
+
+ touch configure-stamp
+
+build: build-stamp
+build-stamp: configure-stamp
+ dh_testdir
+
+ # Build most stuff
+ $(MAKE)
+
+ # We now build perl-piped and perl-shared separately
+ cd bindings/perl-piped && make OPTIMIZE="$(CFLAGS)"
+ cd bindings/perl-shared && make OPTIMIZE="$(CFLAGS)"
+
+ touch build-stamp
+
+clean:
+ dh_testdir
+ dh_testroot
+ rm -f build-stamp configure-stamp
+
+ # Add here commands to clean up after the build process.
+ -$(MAKE) distclean
+
+ -rm bindings/perl*/Makefile.old bindings/perl*/Makefile
+
+ dh_clean
+
+install: build-stamp
+ dh_testdir
+ dh_testroot
+ dh_clean -k
+ dh_installdirs
+
+ # Add here commands to install the package into debian/tmp.
+##NO_TCL $(MAKE) install site-tcl-install DESTDIR=$(tmp)
+ $(MAKE) install DESTDIR=$(tmp)
+
+ mkdir -p $(tmp)/usr/share/doc
+ mv $(tmp)/usr/doc $(tmp)/usr/share/doc/rrdtool
+ mkdir -p $(tmp)/usr/share/rrdtool
+ mv $(tmp)/usr/examples $(tmp)/usr/share/rrdtool
+ mv $(tmp)/usr/html $(tmp)/usr/share/rrdtool
+
+ ## touch-up perl docs
+ mv $(tmp)/usr/man $(tmp)/usr/share
+ mkdir -p $(tmp)/usr/share/man/man3
+ mv $(tmp)/usr/share/man/man1/RRDs.1 $(tmp)/usr/share/man/man3/RRDs.3pm
+ mv $(tmp)/usr/share/man/man1/RRDp.1 $(tmp)/usr/share/man/man3/RRDp.3pm
+ mv $(tmp)/usr/lib/perl $(tmp)/usr/lib/perl5
+
+ ## trim off the doc sources
+ find $(tmp) -name "*.pod" | xargs rm -f
+
+ dh_movefiles
+
+# Build architecture-independent files here.
+binary-indep: build install
+# dh_testversion
+ dh_testdir -i
+ dh_testroot -i
+ dh_installdocs -i
+# dh_installexamples -i
+# dh_installmenu -i
+# dh_installemacsen -i
+# dh_installpam -i
+# dh_installinit -i
+# dh_installcron -i
+# dh_installmanpages -i
+# dh_installinfo -i
+# dh_undocumented
+#LATER dh_installchangelogs -i ChangeLog
+ dh_link -i
+ dh_compress -i
+ dh_fixperms -i
+ # You may want to make some executables suid here.
+# dh_suidregister -i
+ dh_installdeb -i
+ dh_perl -i
+ dh_gencontrol -i
+ dh_md5sums -i
+ dh_builddeb -i
+
+# Build architecture-dependent files here.
+binary-arch: build install
+# dh_testversion
+ dh_testdir -a
+ dh_testroot -a
+ dh_installdocs -a
+# dh_installexamples -a
+# dh_installmenu -a
+# dh_installemacsen -a
+# dh_installpam -a
+# dh_installinit -a
+# dh_installcron -a
+# dh_installmanpages -a
+# dh_installinfo -a
+# dh_undocumented
+#LATER dh_installchangelogs -a ChangeLog
+ifeq "$(findstring nostrip,$(DEB_BUILD_OPTIONS))" ""
+ dh_strip -a
+endif
+ dh_link -a
+ dh_compress -a -Xexamples/
+ dh_fixperms -a
+ # You may want to make some executables suid here.
+# dh_suidregister -a
+# dh_makeshlibs -a
+ dh_installdeb -a
+ dh_perl -a
+ dh_shlibdeps -a -ldebian/librrd0/usr/lib
+ dh_gencontrol -a
+ dh_md5sums -a
+ dh_builddeb -a
+
+source diff:
+ @echo >&2 'source and diff are obsolete - use dpkg-source -b'; false
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install
diff --git a/program/debian/watch b/program/debian/watch
--- /dev/null
+++ b/program/debian/watch
@@ -0,0 +1 @@
+http://oss.oetiker.ch /rrdtool/pub rrdtool-(.*)\.tar\.gz debian
diff --git a/program/doc/Makefile.am b/program/doc/Makefile.am
--- /dev/null
+++ b/program/doc/Makefile.am
@@ -0,0 +1,72 @@
+## Process this file with automake to produce Makefile.in
+
+SUFFIXES = .pod .1 .man .html .txt .pm .pdf .inc
+
+#AUTOMAKE_OPTIONS = foreign
+
+#ACLOCAL_M4 = $(top_srcdir)/config/aclocal.m4
+
+CLEANFILES = *.1 *.html *.txt *-dircache RRD?.pod *.pdf *~ core *itemcache *.rej *.orig *.tmp
+
+POD = bin_dec_hex.pod rrddump.pod rrdgraph_examples.pod rrdrestore.pod rrdupdate.pod \
+ cdeftutorial.pod rrdfetch.pod rrdgraph_graph.pod rrdthreads.pod rrdxport.pod \
+ rpntutorial.pod rrdfirst.pod rrdgraph_rpn.pod rrdtool.pod \
+ rrd-beginners.pod rrdinfo.pod rrdtune.pod rrdbuild.pod \
+ rrdcgi.pod rrdgraph.pod rrdlast.pod rrdlastupdate.pod \
+ rrdcreate.pod rrdgraph_data.pod rrdresize.pod rrdtutorial.pod
+
+
+PMP = RRDs.pod RRDp.pod
+
+MAN = $(POD:.pod=.1)
+TXT = $(MAN:.1=.txt)
+HTML = $(POD:.pod=.html) $(PMP:.pod=.html)
+PDF = $(MAN:.1=.pdf)
+
+# what should go into the distribution
+EXTRA_DIST= $(POD) $(HTML) $(MAN) $(TXT) rrdtool-dump.dtd rrdtool-xport.dtd
+
+idocdir = $(RRDDOCDIR)/txt
+idoc_DATA = $(POD) $(TXT)
+ihtmldir = $(RRDDOCDIR)/html
+ihtml_DATA = $(HTML)
+imandir = $(mandir)/man1
+iman_DATA = $(MAN)
+
+all-local: link txt man html-local
+
+.src.pod:
+ perl -n -e 'if (/^=include\s+(\S+)/){open F,"$$1.inc" || die $$?;print <F>; close F} else {print}' $< > $@
+
+.pod.1 .pm.1 .pl.1:
+ pod2man --release=$(VERSION) --center=rrdtool $< > $@
+
+.1.txt:
+ GROFF_NO_SGR=1 @NROFF@ -man -Tlp $< > $@
+
+.1.pdf:
+ @TROFF@ -man $< | ps2pdf - $@
+
+.pm.html .pod.html .pl.html:
+ pod2html --infile=$< --outfile=$@ --noindex --htmlroot=. --podpath=. --title=$*
+
+RRDs.pod:
+ $(LN_S) $(top_srcdir)/bindings/perl-shared/RRDs.pm RRDs.pod
+
+RRDp.pod:
+ $(LN_S) $(top_srcdir)/bindings/perl-piped/RRDp.pm RRDp.pod
+
+link: RRDp.pod RRDs.pod
+
+man: $(MAN)
+
+html-local: $(HTML)
+
+txt: $(TXT)
+
+pdf-local: $(PDF)
+
+pod: $(POD)
+
+install-data-hook:
+ cd $(DESTDIR)$(ihtmldir) && rm -f index.html && $(LN_S) rrdtool.html index.html
diff --git a/program/doc/bin_dec_hex.pod b/program/doc/bin_dec_hex.pod
--- /dev/null
@@ -0,0 +1,371 @@
+=head1 NAME
+
+bin_dec_hex - How to use binary, decimal, and hexadecimal notation.
+
+=for html <div align="right"><a href="bin_dec_hex.pdf">PDF</a> version.</div>
+
+=head1 DESCRIPTION
+
+Most people use the decimal numbering system. This system uses ten
+symbols to represent numbers. When those ten symbols are used up, they
+start all over again and increment the position to the left. The
+digit 0 is only shown if it is the only symbol in the sequence, or if
+it is not the first one.
+
+If this sounds cryptic to you, this is what I've just said in numbers:
+
+ 0
+ 1
+ 2
+ 3
+ 4
+ 5
+ 6
+ 7
+ 8
+ 9
+ 10
+ 11
+ 12
+ 13
+
+and so on.
+
+Each time the digit nine is incremented, it is reset to 0 and the
+position before (to the left) is incremented (from 0 to 1). Then
+number 9 can be seen as "00009" and when we should increment 9, we
+reset it to zero and increment the digit just before the 9 so the
+number becomes "00010". Leading zeros we don't write except if it is
+the only digit (number 0). And of course, we write zeros if they occur
+anywhere inside or at the end of a number:
+
+ "00010" -> " 0010" -> " 010" -> " 10", but not " 1 ".
+
+This was pretty basic, you already knew this. Why did I tell it?
+Well, computers usually do not represent numbers with 10 different
+digits. They only use two different symbols, namely "0" and "1". Apply
+the same rules to this set of digits and you get the binary numbering
+system:
+
+ 0
+ 1
+ 10
+ 11
+ 100
+ 101
+ 110
+ 111
+ 1000
+ 1001
+ 1010
+ 1011
+ 1100
+ 1101
+
+and so on.
+
+If you count the number of rows, you'll see that these are again 14
+different numbers. The numbers are the same and mean the same as in
+the first list, we just used a different representation. This means
+that you have to know the representation used, or as it is called the
+numbering system or base. Normally, if we do not explicitly specify
+the numbering system used, we implicitly use the decimal system. If we
+want to use any other numbering system, we'll have to make that
+clear. There are a few widely adopted methods to do so. One common
+form is to write 1010(2) which means that you wrote down a number in
+its binary representation. It is the number ten. If you would write
+1010 without specifying the base, the number is interpreted as one
+thousand and ten using base 10.
+
+In books, another form is common. It uses subscripts (little
+characters, more or less in between two rows). You can leave out the
+parentheses in that case and write down the number in normal
+characters followed by a little two just behind it.
+
+As the numbering system used is also called the base, we talk of the
+number 1100 base 2, the number 12 base 10.
+
+Within the binary system, it is common to write leading zeros. The
+numbers are written down in series of four, eight or sixteen depending
+on the context.
+
+We can use the binary form when talking to computers
+(...programming...), but the numbers will have large
+representations. The number 65'535 (often in the decimal system a ' is
+used to separate blocks of three digits for readability) would be
+written down as 1111111111111111(2) which is 16 times the digit 1.
+This is difficult and prone to errors. Therefore, we usually would use
+another base, called hexadecimal. It uses 16 different symbols. First
+the symbols from the decimal system are used, thereafter we continue
+with alphabetic characters. We get 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+A, B, C, D, E and F. This system is chosen because the hexadecimal
+form can be converted into the binary system very easily (and back).
+
+There is yet another system in use, called the octal system. This was
+more common in the old days, but is not used very often anymore. As
+you might find it in use sometimes, you should get used to it and
+we'll show it below. It's the same story as with the other
+representations, but with eight different symbols.
+
+ Binary (2)
+ Octal (8)
+ Decimal (10)
+ Hexadecimal (16)
+
+ (2) (8) (10) (16)
+ 00000 0 0 0
+ 00001 1 1 1
+ 00010 2 2 2
+ 00011 3 3 3
+ 00100 4 4 4
+ 00101 5 5 5
+ 00110 6 6 6
+ 00111 7 7 7
+ 01000 10 8 8
+ 01001 11 9 9
+ 01010 12 10 A
+ 01011 13 11 B
+ 01100 14 12 C
+ 01101 15 13 D
+ 01110 16 14 E
+ 01111 17 15 F
+ 10000 20 16 10
+ 10001 21 17 11
+ 10010 22 18 12
+ 10011 23 19 13
+ 10100 24 20 14
+ 10101 25 21 15
+
+Most computers used nowadays are using bytes of eight bits. This means
+that they store eight bits at a time. You can see why the octal system
+is not the most practical for that: You'd need three digits to represent
+the eight bits and this means that you'd have to use one complete digit
+to represent only two bits (2+3+3=8). This is a waste. For hexadecimal
+digits, you need only two digits which are used completely:
+
+ (2) (8) (10) (16)
+ 11111111 377 255 FF
+
+You can see why binary and hexadecimal can be converted quickly: For
+each hexadecimal digit there are exactly four binary digits. Take a
+binary number: take four digits from the right and make a hexadecimal
+digit from it (see the table above). Repeat this until there are no
+more digits. And the other way around: Take a hexadecimal number. For
+each digit, write down its binary equivalent.
+
+Computers (or rather the parsers running on them) would have a hard
+time converting a number like 1234(16). Therefore hexadecimal numbers
+are specified with a prefix. This prefix depends on the language
+you're writing in. Some of the prefixes are "0x" for C, "$" for
+Pascal, "#" for HTML. It is common to assume that if a number starts
+with a zero, it is octal. It does not matter what is used as long as
+you know what it is. I will use "0x" for hexadecimal, "%" for binary
+and "0" for octal. The following numbers are all the same, just their represenatation (base) is different: 021 0x11 17 %00010001
+
+To do arithmetics and conversions you need to understand one more thing.
+It is something you already know but perhaps you do not "see" it yet:
+
+If you write down 1234, (no prefix, so it is decimal) you are talking
+about the number one thousand, two hundred and thirty four. In sort of
+a formula:
+
+ 1 * 1000 = 1000
+ 2 * 100 = 200
+ 3 * 10 = 30
+ 4 * 1 = 4
+
+This can also be written as:
+
+ 1 * 10^3
+ 2 * 10^2
+ 3 * 10^1
+ 4 * 10^0
+
+where ^ means "to the power of".
+
+We are using the base 10, and the positions 0,1,2 and 3.
+The right-most position should NOT be multiplied with 10. The second
+from the right should be multiplied one time with 10. The third from
+the right is multiplied with 10 two times. This continues for whatever
+positions are used.
+
+It is the same in all other representations:
+
+0x1234 will be
+
+ 1 * 16^3
+ 2 * 16^2
+ 3 * 16^1
+ 4 * 16^0
+
+01234 would be
+
+ 1 * 8^3
+ 2 * 8^2
+ 3 * 8^1
+ 4 * 8^0
+
+This example can not be done for binary as that system only uses two
+symbols. Another example:
+
+%1010 would be
+
+ 1 * 2^3
+ 0 * 2^2
+ 1 * 2^1
+ 0 * 2^0
+
+It would have been easier to convert it to its hexadecimal form and
+just translate %1010 into 0xA. After a while you get used to it. You will
+not need to do any calculations anymore, but just know that 0xA means 10.
+
+To convert a decimal number into a hexadecimal you could use the next
+method. It will take some time to be able to do the estimates, but it
+will be easier when you use the system more frequently. We'll look at
+yet another way afterwards.
+
+First you need to know how many positions will be used in the other
+system. To do so, you need to know the maximum numbers you'll be
+using. Well, that's not as hard as it looks. In decimal, the maximum
+number that you can form with two digits is "99". The maximum for
+three: "999". The next number would need an extra position. Reverse
+this idea and you will see that the number can be found by taking 10^3
+(10*10*10 is 1000) minus 1 or 10^2 minus one.
+
+This can be done for hexadecimal as well:
+
+ 16^4 = 0x10000 = 65536
+ 16^3 = 0x1000 = 4096
+ 16^2 = 0x100 = 256
+ 16^1 = 0x10 = 16
+
+If a number is smaller than 65'536 it will fit in four positions.
+If the number is bigger than 4'095, you must use position 4.
+How many times you can subtract 4'096 from the number without going below
+zero is the first digit you write down. This will always be a number
+from 1 to 15 (0x1 to 0xF). Do the same for the other positions.
+
+Let's try with 41'029. It is smaller than 16^4 but bigger than 16^3-1. This
+means that we have to use four positions.
+We can subtract 16^3 from 41'029 ten times without going below zero.
+The left-most digit will therefore be "A", so we have 0xA????.
+The number is reduced to 41'029 - 10*4'096 = 41'029-40'960 = 69.
+69 is smaller than 16^3 but not bigger than 16^2-1. The second digit
+is therefore "0" and we now have 0xA0??.
+69 is smaller than 16^2 and bigger than 16^1-1. We can subtract 16^1
+(which is just plain 16) four times and write down "4" to get 0xA04?.
+Subtract 64 from 69 (69 - 4*16) and the last digit is 5 --> 0xA045.
+
+The other method builds ub the number from the right. Let's try 41'029
+again. Divide by 16 and do not use fractions (only whole numbers).
+
+ 41'029 / 16 is 2'564 with a remainder of 5. Write down 5.
+ 2'564 / 16 is 160 with a remainder of 4. Write the 4 before the 5.
+ 160 / 16 is 10 with no remainder. Prepend 45 with 0.
+ 10 / 16 is below one. End here and prepend 0xA. End up with 0xA045.
+
+Which method to use is up to you. Use whatever works for you. I use
+them both without being able to tell what method I use in each case,
+it just depends on the number, I think. Fact is, some numbers will
+occur frequently while programming. If the number is close to one I am
+familiar with, then I will use the first method (like 32'770 which is
+into 32'768 + 2 and I just know that it is 0x8000 + 0x2 = 0x8002).
+
+For binary the same approach can be used. The base is 2 and not 16,
+and the number of positions will grow rapidly. Using the second method
+has the advantage that you can see very easily if you should write down
+a zero or a one: if you divide by two the remainder will be zero if it
+is an even number and one if it is an odd number:
+
+ 41029 / 2 = 20514 remainder 1
+ 20514 / 2 = 10257 remainder 0
+ 10257 / 2 = 5128 remainder 1
+ 5128 / 2 = 2564 remainder 0
+ 2564 / 2 = 1282 remainder 0
+ 1282 / 2 = 641 remainder 0
+ 641 / 2 = 320 remainder 1
+ 320 / 2 = 160 remainder 0
+ 160 / 2 = 80 remainder 0
+ 80 / 2 = 40 remainder 0
+ 40 / 2 = 20 remainder 0
+ 20 / 2 = 10 remainder 0
+ 10 / 2 = 5 remainder 0
+ 5 / 2 = 2 remainder 1
+ 2 / 2 = 1 remainder 0
+ 1 / 2 below 0 remainder 1
+
+Write down the results from right to left: %1010000001000101
+
+Group by four:
+
+ %1010000001000101
+ %101000000100 0101
+ %10100000 0100 0101
+ %1010 0000 0100 0101
+
+Convert into hexadecimal: 0xA045
+
+Group %1010000001000101 by three and convert into octal:
+
+ %1010000001000101
+ %1010000001000 101
+ %1010000001 000 101
+ %1010000 001 000 101
+ %1010 000 001 000 101
+ %1 010 000 001 000 101
+ %001 010 000 001 000 101
+ 1 2 0 1 0 5 --> 0120105
+
+ So: %1010000001000101 = 0120105 = 0xA045 = 41029
+ Or: 1010000001000101(2) = 120105(8) = A045(16) = 41029(10)
+ Or: 1010000001000101(2) = 120105(8) = A045(16) = 41029
+
+
+At first while adding numbers, you'll convert them to their decimal
+form and then back into their original form after doing the addition.
+If you use the other numbering system often, you will see that you'll
+be able to do arithmetics directly in the base that is used.
+In any representation it is the same, add the numbers on the right,
+write down the right-most digit from the result, remember the other
+digits and use them in the next round. Continue with the second digit
+from the right and so on:
+
+ %1010 + %0111 --> 10 + 7 --> 17 --> %00010001
+
+will become
+
+ %1010
+ %0111 +
+ ||||
+ |||+-- add 0 + 1, result is 1, nothing to remember
+ ||+--- add 1 + 1, result is %10, write down 0 and remember 1
+ |+---- add 0 + 1 + 1(remembered), result = 0, remember 1
+ +----- add 1 + 0 + 1(remembered), result = 0, remember 1
+ nothing to add, 1 remembered, result = 1
+ --------
+ %10001 is the result, I like to write it as %00010001
+
+For low values, try to do the calculations yourself, then check them with
+a calculator. The more you do the calculations yourself, the more you'll
+find that you didn't make mistakes. In the end, you'll do calculi in
+other bases as easily as you do them in decimal.
+
+When the numbers get bigger, you'll have to realize that a computer is
+not called a computer just to have a nice name. There are many
+different calculators available, use them. For Unix you could use "bc"
+which is short for Binary Calculator. It calculates not only in
+decimal, but in all bases you'll ever want to use (among them Binary).
+
+For people on Windows:
+Start the calculator (start->programs->accessories->calculator)
+and if necessary click view->scientific. You now have a scientific
+calculator and can compute in binary or hexadecimal.
+
+=head1 AUTHOR
+
+I hope you enjoyed the examples and their descriptions. If you do, help
+other people by pointing them to this document when they are asking
+basic questions. They will not only get their answer, but at the same
+time learn a whole lot more.
+
+Alex van den Bogaerdt E<lt>alex@ergens.op.het.netE<gt>
diff --git a/program/doc/cdeftutorial.pod b/program/doc/cdeftutorial.pod
--- /dev/null
@@ -0,0 +1,889 @@
+=head1 NAME
+
+cdeftutorial - Alex van den Bogaerdt's CDEF tutorial
+
+=head1 DESCRIPTION
+
+Intention of this document: to provide some examples of the commonly
+used parts of RRDtool's CDEF language.
+
+If you think some important feature is not explained properly, and if
+adding it to this document would benefit most users, please do ask me
+to add it. I will then try to provide an answer in the next release
+of this tutorial. No feedback equals no changes! Additions to
+this document are also welcome. -- Alex van den Bogaerdt
+E<lt>alex@ergens.op.het.netE<gt>
+
+=head2 Why this tutorial?
+
+One of the powerful parts of RRDtool is its ability to do all sorts
+of calculations on the data retrieved from its databases. However,
+RRDtool's many options and syntax make it difficult for the average
+user to understand. The manuals are good at explaining what these
+options do; however they do not (and should not) explain in detail
+why they are useful. As with my RRDtool tutorial: if you want a
+simple document in simple language you should read this tutorial.
+If you are happy with the official documentation, you may find this
+document too simple or even boring. If you do choose to read this
+tutorial, I also expect you to have read and fully understand my
+other tutorial.
+
+=head2 More reading
+
+If you have difficulties with the way I try to explain it please read
+Steve Rader's L<rpntutorial>. It may help you understand how this all works.
+
+=head1 What are CDEFs?
+
+When retrieving data from an RRD, you are using a "DEF" to work with
+that data. Think of it as a variable that changes over time (where
+time is the x-axis). The value of this variable is what is found in
+the database at that particular time and you can't do any
+modifications on it. This is what CDEFs are for: they takes values
+from DEFs and perform calculations on them.
+
+=head1 Syntax
+
+ DEF:var_name_1=some.rrd:ds_name:CF
+ CDEF:var_name_2=RPN_expression
+
+You first define "var_name_1" to be data collected from data source
+"ds_name" found in RRD "some.rrd" with consolidation function "CF".
+
+Assume the ifInOctets SNMP counter is saved in mrtg.rrd as the DS "in".
+Then the following DEF defines a variable for the average of that
+data source:
+
+ DEF:inbytes=mrtg.rrd:in:AVERAGE
+
+Say you want to display bits per second (instead of bytes per second
+as stored in the database.) You have to define a calculation
+(hence "CDEF") on variable "inbytes" and use that variable (inbits)
+instead of the original:
+
+ CDEF:inbits=inbytes,8,*
+
+This tells RRDtool to multiply inbytes by eight to get inbits. I'll
+explain later how this works. In the graphing or printing functions,
+you can now use inbits where you would use inbytes otherwise.
+
+Note that the variable name used in the CDEF (inbits) must not be the
+same as the variable named in the DEF (inbytes)!
+
+=head1 RPN-expressions
+
+RPN is short-hand for Reverse Polish Notation. It works as follows.
+You put the variables or numbers on a stack. You also put operations
+(things-to-do) on the stack and this stack is then processed. The result
+will be placed on the stack. At the end, there should be exactly one
+number left: the outcome of the series of operations. If there is not
+exactly one number left, RRDtool will complain loudly.
+
+Above multiplication by eight will look like:
+
+=over 4
+
+=item 1.
+
+Start with an empty stack
+
+=item 2.
+
+Put the content of variable inbytes on the stack
+
+=item 3.
+
+Put the number eight on the stack
+
+=item 4.
+
+Put the operation multiply on the stack
+
+=item 5.
+
+Process the stack
+
+=item 6.
+
+Retrieve the value from the stack and put it in variable inbits
+
+=back
+
+We will now do an example with real numbers. Suppose the variable
+inbytes would have value 10, the stack would be:
+
+=over 4
+
+=item 1.
+
+||
+
+=item 2.
+
+|10|
+
+=item 3.
+
+|10|8|
+
+=item 4.
+
+|10|8|*|
+
+=item 5.
+
+|80|
+
+=item 6.
+
+||
+
+=back
+
+Processing the stack (step 5) will retrieve one value from the stack
+(from the right at step 4). This is the operation multiply and this
+takes two values off the stack as input. The result is put back on the
+stack (the value 80 in this case). For multiplication the order doesn't
+matter, but for other operations like subtraction and division it does.
+Generally speaking you have the following order:
+
+ y = A - B --> y=minus(A,B) --> CDEF:y=A,B,-
+
+This is not very intuitive (at least most people don't think so). For
+the function f(A,B) you reverse the position of "f", but you do not
+reverse the order of the variables.
+
+=head1 Converting your wishes to RPN
+
+First, get a clear picture of what you want to do. Break down the problem
+in smaller portions until they cannot be split anymore. Then it is rather
+simple to convert your ideas into RPN.
+
+Suppose you have several RRDs and would like to add up some counters in
+them. These could be, for instance, the counters for every WAN link you
+are monitoring.
+
+You have:
+
+ router1.rrd with link1in link2in
+ router2.rrd with link1in link2in
+ router3.rrd with link1in link2in
+
+Suppose you would like to add up all these counters, except for link2in
+inside router2.rrd. You need to do:
+
+(in this example, "router1.rrd:link1in" means the DS link1in inside the
+RRD router1.rrd)
+
+ router1.rrd:link1in
+ router1.rrd:link2in
+ router2.rrd:link1in
+ router3.rrd:link1in
+ router3.rrd:link2in
+ -------------------- +
+ (outcome of the sum)
+
+As a mathematical function, this could be written:
+
+C<add(router1.rrd:link1in , router1.rrd:link2in , router2.rrd:link1in , router3.rrd:link1in , router3.rrd:link2.in)>
+
+With RRDtool and RPN, first, define the inputs:
+
+ DEF:a=router1.rrd:link1in:AVERAGE
+ DEF:b=router1.rrd:link2in:AVERAGE
+ DEF:c=router2.rrd:link1in:AVERAGE
+ DEF:d=router3.rrd:link1in:AVERAGE
+ DEF:e=router3.rrd:link2in:AVERAGE
+
+Now, the mathematical function becomes: C<add(a,b,c,d,e)>
+
+In RPN, there's no operator that sums more than two values so you need
+to do several additions. You add a and b, add c to the result, add d
+to the result and add e to the result.
+
+ push a: a stack contains the value of a
+ push b and add: b,+ stack contains the result of a+b
+ push c and add: c,+ stack contains the result of a+b+c
+ push d and add: d,+ stack contains the result of a+b+c+d
+ push e and add: e,+ stack contains the result of a+b+c+d+e
+
+What was calculated here would be written down as:
+
+ ( ( ( (a+b) + c) + d) + e) >
+
+This is in RPN: C<CDEF:result=a,b,+,c,+,d,+,e,+>
+
+This is correct but it can be made more clear to humans. It does
+not matter if you add a to b and then add c to the result or first
+add b to c and then add a to the result. This makes it possible to
+rewrite the RPN into C<CDEF:result=a,b,c,d,e,+,+,+,+> which is
+evaluated differently:
+
+ push value of variable a on the stack: a
+ push value of variable b on the stack: a b
+ push value of variable c on the stack: a b c
+ push value of variable d on the stack: a b c d
+ push value of variable e on the stack: a b c d e
+ push operator + on the stack: a b c d e +
+ and process it: a b c P (where P == d+e)
+ push operator + on the stack: a b c P +
+ and process it: a b Q (where Q == c+P)
+ push operator + on the stack: a b Q +
+ and process it: a R (where R == b+Q)
+ push operator + on the stack: a R +
+ and process it: S (where S == a+R)
+
+As you can see the RPN expression C<a,b,c,d,e,+,+,+,+,+> will evaluate in
+C<((((d+e)+c)+b)+a)> and it has the same outcome as C<a,b,+,c,+,d,+,e,+>.
+This is called the commutative law of addition,
+but you may forget this right away, as long as you remember what it
+means.
+
+Now look at an expression that contains a multiplication:
+
+First in normal math: C<let result = a+b*c>. In this case you can't
+choose the order yourself, you have to start with the multiplication
+and then add a to it. You may alter the position of b and c, you must
+not alter the position of a and b.
+
+You have to take this in consideration when converting this expression
+into RPN. Read it as: "Add the outcome of b*c to a" and then it is
+easy to write the RPN expression: C<result=a,b,c,*,+>
+Another expression that would return the same: C<result=b,c,*,a,+>
+
+In normal math, you may encounter something like "a*(b+c)" and this
+can also be converted into RPN. The parenthesis just tell you to first
+add b and c, and then multiply a with the result. Again, now it is
+easy to write it in RPN: C<result=a,b,c,+,*>. Note that this is very
+similar to one of the expressions in the previous paragraph, only the
+multiplication and the addition changed places.
+
+When you have problems with RPN or when RRDtool is complaining, it's
+usually a good thing to write down the stack on a piece of paper
+and see what happens. Have the manual ready and pretend to be RRDtool.
+Just do all the math by hand to see what happens, I'm sure this will
+solve most, if not all, problems you encounter.
+
+=head1 Some special numbers
+
+=head2 The unknown value
+
+Sometimes collecting your data will fail. This can be very common,
+especially when querying over busy links. RRDtool can be configured
+to allow for one (or even more) unknown value(s) and calculate the missing
+update. You can, for instance, query your device every minute. This is
+creating one so called PDP or primary data point per minute. If you
+defined your RRD to contain an RRA that stores 5-minute values, you need
+five of those PDPs to create one CDP (consolidated data point).
+These PDPs can become unknown in two cases:
+
+=over 4
+
+=item 1.
+
+The updates are too far apart. This is tuned using the "heartbeat" setting.
+
+=item 2.
+
+The update was set to unknown on purpose by inserting no value (using the
+template option) or by using "U" as the value to insert.
+
+=back
+
+When a CDP is calculated, another mechanism determines if this CDP is valid
+or not. If there are too many PDPs unknown, the CDP is unknown as well.
+This is determined by the xff factor. Please note that one unknown counter
+update can result in two unknown PDPs! If you only allow for one unknown
+PDP per CDP, this makes the CDP go unknown!
+
+Suppose the counter increments with one per second and you retrieve it
+every minute:
+
+ counter value resulting rate
+ 10'000
+ 10'060 1; (10'060-10'000)/60 == 1
+ 10'120 1; (10'120-10'060)/60 == 1
+ unknown unknown; you don't know the last value
+ 10'240 unknown; you don't know the previous value
+ 10'300 1; (10'300-10'240)/60 == 1
+
+If the CDP was to be calculated from the last five updates, it would get
+two unknown PDPs and three known PDPs. If xff would have been set to 0.5
+which by the way is a commonly used factor, the CDP would have a known
+value of 1. If xff would have been set to 0.2 then the resulting CDP
+would be unknown.
+
+You have to decide the proper values for heartbeat, number of PDPs per
+CDP and the xff factor. As you can see from the previous text they define
+the behavior of your RRA.
+
+=head2 Working with unknown data in your database
+
+As you have read in the previous chapter, entries in an RRA can be
+set to the unknown value. If you do calculations with this type of
+value, the result has to be unknown too. This means that an expression
+such as C<result=a,b,+> will be unknown if either a or b is unknown.
+It would be wrong to just ignore the unknown value and return the value
+of the other parameter. By doing so, you would assume "unknown" means "zero"
+and this is not true.
+
+There has been a case where somebody was collecting data for over a year.
+A new piece of equipment was installed, a new RRD was created and the
+scripts were changed to add a counter from the old database and a counter
+from the new database. The result was disappointing, a large part of
+the statistics seemed to have vanished mysteriously ...
+They of course didn't, values from the old database (known values) were
+added to values from the new database (unknown values) and the result was
+unknown.
+
+In this case, it is fairly reasonable to use a CDEF that alters unknown
+data into zero. The counters of the device were unknown (after all, it
+wasn't installed yet!) but you know that the data rate through the device
+had to be zero (because of the same reason: it was not installed).
+
+There are some examples below that make this change.
+
+=head2 Infinity
+
+Infinite data is another form of a special number. It cannot be
+graphed because by definition you would never reach the infinite
+value. You can think of positive and negative infinity depending on
+the position relative to zero.
+
+RRDtool is capable of representing (-not- graphing!) infinity by stopping
+at its current maximum (for positive infinity) or minimum (for negative
+infinity) without knowing this maximum (minimum).
+
+Infinity in RRDtool is mostly used to draw an AREA without knowing its
+vertical dimensions. You can think of it as drawing an AREA with an
+infinite height and displaying only the part that is visible in the
+current graph. This is probably a good way to approximate infinity
+and it sure allows for some neat tricks. See below for examples.
+
+=head2 Working with unknown data and infinity
+
+Sometimes you would like to discard unknown data and pretend it is zero
+(or any other value for that matter) and sometimes you would like to
+pretend that known data is unknown (to discard known-to-be-wrong data).
+This is why CDEFs have support for unknown data. There are also examples
+available that show unknown data by using infinity.
+
+=head1 Some examples
+
+=head2 Example: using a recently created RRD
+
+You are keeping statistics on your router for over a year now. Recently
+you installed an extra router and you would like to show the combined
+throughput for these two devices.
+
+If you just add up the counters from router.rrd and router2.rrd, you
+will add known data (from router.rrd) to unknown data (from router2.rrd) for
+the bigger part of your stats. You could solve this in a few ways:
+
+=over 4
+
+=item *
+
+While creating the new database, fill it with zeros from the start to now.
+You have to make the database start at or before the least recent time in
+the other database.
+
+=item *
+
+Alternatively, you could use CDEF and alter unknown data to zero.
+
+=back
+
+Both methods have their pros and cons. The first method is troublesome and
+if you want to do that you have to figure it out yourself. It is not
+possible to create a database filled with zeros, you have to put them in
+manually. Implementing the second method is described next:
+
+What we want is: "if the value is unknown, replace it with zero". This
+could be written in pseudo-code as: if (value is unknown) then (zero)
+else (value). When reading the L<rrdgraph> manual you notice the "UN"
+function that returns zero or one. You also notice the "IF" function
+that takes zero or one as input.
+
+First look at the "IF" function. It takes three values from the stack,
+the first value is the decision point, the second value is returned to
+the stack if the evaluation is "true" and if not, the third value is
+returned to the stack. We want the "UN" function to decide what happens
+so we combine those two functions in one CDEF.
+
+Lets write down the two possible paths for the "IF" function:
+
+ if true return a
+ if false return b
+
+In RPN: C<result=x,a,b,IF> where "x" is either true or false.
+
+Now we have to fill in "x", this should be the "(value is unknown)" part
+and this is in RPN: C<result=value,UN>
+
+We now combine them: C<result=value,UN,a,b,IF> and when we fill in the
+appropriate things for "a" and "b" we're finished:
+
+C<CDEF:result=value,UN,0,value,IF>
+
+You may want to read Steve Rader's RPN guide if you have difficulties
+with the way I explained this last example.
+
+If you want to check this RPN expression, just mimic RRDtool behavior:
+
+ For any known value, the expression evaluates as follows:
+ CDEF:result=value,UN,0,value,IF (value,UN) is not true so it becomes 0
+ CDEF:result=0,0,value,IF "IF" will return the 3rd value
+ CDEF:result=value The known value is returned
+
+ For the unknown value, this happens:
+ CDEF:result=value,UN,0,value,IF (value,UN) is true so it becomes 1
+ CDEF:result=1,0,value,IF "IF" sees 1 and returns the 2nd value
+ CDEF:result=0 Zero is returned
+
+Of course, if you would like to see another value instead of zero, you
+can use that other value.
+
+Eventually, when all unknown data is removed from the RRD, you may want
+to remove this rule so that unknown data is properly displayed.
+
+=head2 Example: better handling of unknown data, by using time
+
+The above example has one drawback. If you do log unknown data in
+your database after installing your new equipment, it will also be
+translated into zero and therefore you won't see that there was a
+problem. This is not good and what you really want to do is:
+
+=over 4
+
+=item *
+
+If there is unknown data, look at the time that this sample was taken.
+
+=item *
+
+If the unknown value is before time xxx, make it zero.
+
+=item *
+
+If it is after time xxx, leave it as unknown data.
+
+=back
+
+This is doable: you can compare the time that the sample was taken
+to some known time. Assuming you started to monitor your device on
+Friday September 17, 1999, 00:35:57 MET DST. Translate this time in seconds
+since 1970-01-01 and it becomes 937'521'357. If you process unknown values
+that were received after this time, you want to leave them unknown and
+if they were "received" before this time, you want to translate them
+into zero (so you can effectively ignore them while adding them to your
+other routers counters).
+
+Translating Friday September 17, 1999, 00:35:57 MET DST into 937'521'357 can
+be done by, for instance, using gnu date:
+
+ date -d "19990917 00:35:57" +%s
+
+You could also dump the database and see where the data starts to be
+known. There are several other ways of doing this, just pick one.
+
+Now we have to create the magic that allows us to process unknown
+values different depending on the time that the sample was taken.
+This is a three step process:
+
+=over 4
+
+=item 1.
+
+If the timestamp of the value is after 937'521'357, leave it as is.
+
+=item 2.
+
+If the value is a known value, leave it as is.
+
+=item 3.
+
+Change the unknown value into zero.
+
+=back
+
+Lets look at part one:
+
+ if (true) return the original value
+
+We rewrite this:
+
+ if (true) return "a"
+ if (false) return "b"
+
+We need to calculate true or false from step 1. There is a function
+available that returns the timestamp for the current sample. It is
+called, how surprisingly, "TIME". This time has to be compared to
+a constant number, we need "GT". The output of "GT" is true or false
+and this is good input to "IF". We want "if (time > 937521357) then
+(return a) else (return b)".
+
+This process was already described thoroughly in the previous chapter
+so lets do it quick:
+
+ if (x) then a else b
+ where x represents "time>937521357"
+ where a represents the original value
+ where b represents the outcome of the previous example
+
+ time>937521357 --> TIME,937521357,GT
+
+ if (x) then a else b --> x,a,b,IF
+ substitute x --> TIME,937521357,GT,a,b,IF
+ substitute a --> TIME,937521357,GT,value,b,IF
+ substitute b --> TIME,937521357,GT,value,value,UN,0,value,IF,IF
+
+We end up with:
+C<CDEF:result=TIME,937521357,GT,value,value,UN,0,value,IF,IF>
+
+This looks very complex, however, as you can see, it was not too hard to
+come up with.
+
+=head2 Example: Pretending weird data isn't there
+
+Suppose you have a problem that shows up as huge spikes in your graph.
+You know this happens and why, so you decide to work around the problem.
+Perhaps you're using your network to do a backup at night and by doing
+so you get almost 10mb/s while the rest of your network activity does
+not produce numbers higher than 100kb/s.
+
+There are two options:
+
+=over 4
+
+=item 1.
+
+If the number exceeds 100kb/s it is wrong and you want it masked out
+by changing it into unknown.
+
+=item 2.
+
+You don't want the graph to show more than 100kb/s.
+
+=back
+
+Pseudo code: if (number > 100) then unknown else number
+or
+Pseudo code: if (number > 100) then 100 else number.
+
+The second "problem" may also be solved by using the rigid option of
+RRDtool graph, however this has not the same result. In this example
+you can end up with a graph that does autoscaling. Also, if you use
+the numbers to display maxima they will be set to 100kb/s.
+
+We use "IF" and "GT" again. "if (x) then (y) else (z)" is written
+down as "CDEF:result=x,y,z,IF"; now fill in x, y and z.
+For x you fill in "number greater than 100kb/s" becoming
+"number,100000,GT" (kilo is 1'000 and b/s is what we measure!).
+The "z" part is "number" in both cases and the "y" part is either
+"UNKN" for unknown or "100000" for 100kb/s.
+
+The two CDEF expressions would be:
+
+ CDEF:result=number,100000,GT,UNKN,number,IF
+ CDEF:result=number,100000,GT,100000,number,IF
+
+=head2 Example: working on a certain time span
+
+If you want a graph that spans a few weeks, but would only want to
+see some routers' data for one week, you need to "hide" the rest of
+the time frame. Don't ask me when this would be useful, it's just
+here for the example :)
+
+We need to compare the time stamp to a begin date and an end date.
+Comparing isn't difficult:
+
+ TIME,begintime,GE
+ TIME,endtime,LE
+
+These two parts of the CDEF produce either 0 for false or 1 for true.
+We can now check if they are both 0 (or 1) using a few IF statements
+but, as Wataru Satoh pointed out, we can use the "*" or "+" functions
+as logical AND and logical OR.
+
+For "*", the result will be zero (false) if either one of the two
+operators is zero. For "+", the result will only be false (0) when
+two false (0) operators will be added. Warning: *any* number not
+equal to 0 will be considered "true". This means that, for instance,
+"-1,1,+" (which should be "true or true") will become FALSE ...
+In other words, use "+" only if you know for sure that you have positive
+numbers (or zero) only.
+
+Let's compile the complete CDEF:
+
+ DEF:ds0=router1.rrd:AVERAGE
+ CDEF:ds0modified=TIME,begintime,GE,TIME,endtime,LE,*,UNKN,ds0,IF
+
+This will return the value of ds0 if both comparisons return true. You
+could also do it the other way around:
+
+ DEF:ds0=router1.rrd:AVERAGE
+ CDEF:ds0modified=TIME,begintime,LT,TIME,endtime,GT,+,UNKN,ds0,IF
+
+This will return an UNKNOWN if either comparison returns true.
+
+=head2 Example: You suspect to have problems and want to see unknown data.
+
+Suppose you add up the number of active users on several terminal servers.
+If one of them doesn't give an answer (or an incorrect one) you get "NaN"
+in the database ("Not a Number") and NaN is evaluated as Unknown.
+
+In this case, you would like to be alerted to it and the sum of the
+remaining values is of no value to you.
+
+It would be something like:
+
+ DEF:users1=location1.rrd:onlineTS1:LAST
+ DEF:users2=location1.rrd:onlineTS2:LAST
+ DEF:users3=location2.rrd:onlineTS1:LAST
+ DEF:users4=location2.rrd:onlineTS2:LAST
+ CDEF:allusers=users1,users2,users3,users4,+,+,+
+
+If you now plot allusers, unknown data in one of users1..users4 will
+show up as a gap in your graph. You want to modify this to show a
+bright red line, not a gap.
+
+Define an extra CDEF that is unknown if all is okay and is infinite if
+there is an unknown value:
+
+ CDEF:wrongdata=allusers,UN,INF,UNKN,IF
+
+"allusers,UN" will evaluate to either true or false, it is the (x) part
+of the "IF" function and it checks if allusers is unknown.
+The (y) part of the "IF" function is set to "INF" (which means infinity)
+and the (z) part of the function returns "UNKN".
+
+The logic is: if (allusers == unknown) then return INF else return UNKN.
+
+You can now use AREA to display this "wrongdata" in bright red. If it
+is unknown (because allusers is known) then the red AREA won't show up.
+If the value is INF (because allusers is unknown) then the red AREA will
+be filled in on the graph at that particular time.
+
+ AREA:allusers#0000FF:combined user count
+ AREA:wrongdata#FF0000:unknown data
+
+=head2 Same example useful with STACKed data:
+
+If you use stack in the previous example (as I would do) then you don't
+add up the values. Therefore, there is no relationship between the
+four values and you don't get a single value to test.
+Suppose users3 would be unknown at one point in time: users1 is plotted,
+users2 is stacked on top of users1, users3 is unknown and therefore
+nothing happens, users4 is stacked on top of users2.
+Add the extra CDEFs anyway and use them to overlay the "normal" graph:
+
+ DEF:users1=location1.rrd:onlineTS1:LAST
+ DEF:users2=location1.rrd:onlineTS2:LAST
+ DEF:users3=location2.rrd:onlineTS1:LAST
+ DEF:users4=location2.rrd:onlineTS2:LAST
+ CDEF:allusers=users1,users2,users3,users4,+,+,+
+ CDEF:wrongdata=allusers,UN,INF,UNKN,IF
+ AREA:users1#0000FF:users at ts1
+ STACK:users2#00FF00:users at ts2
+ STACK:users3#00FFFF:users at ts3
+ STACK:users4#FFFF00:users at ts4
+ AREA:wrongdata#FF0000:unknown data
+
+If there is unknown data in one of users1..users4, the "wrongdata" AREA
+will be drawn and because it starts at the X-axis and has infinite height
+it will effectively overwrite the STACKed parts.
+
+You could combine the two CDEF lines into one (we don't use "allusers")
+if you like. But there are good reasons for writing two CDEFS:
+
+=over 4
+
+=item *
+
+It improves the readability of the script.
+
+=item *
+
+It can be used inside GPRINT to display the total number of users.
+
+=back
+
+If you choose to combine them, you can substitute the "allusers" in the
+second CDEF with the part after the equal sign from the first line:
+
+ CDEF:wrongdata=users1,users2,users3,users4,+,+,+,UN,INF,UNKN,IF
+
+If you do so, you won't be able to use these next GPRINTs:
+
+ COMMENT:"Total number of users seen"
+ GPRINT:allusers:MAX:"Maximum: %6.0lf"
+ GPRINT:allusers:MIN:"Minimum: %6.0lf"
+ GPRINT:allusers:AVERAGE:"Average: %6.0lf"
+ GPRINT:allusers:LAST:"Current: %6.0lf\n"
+
+=head1 The examples from the RRD graph manual page
+
+=head2 Degrees Celsius vs. Degrees Fahrenheit
+
+To convert Celsius into Fahrenheit use the formula
+F=9/5*C+32
+
+ rrdtool graph demo.png --title="Demo Graph" \
+ DEF:cel=demo.rrd:exhaust:AVERAGE \
+ CDEF:far=9,5,/,cel,*,32,+ \
+ LINE2:cel#00a000:"D. Celsius" \
+ LINE2:far#ff0000:"D. Fahrenheit\c"
+
+This example gets the DS called "exhaust" from database "demo.rrd"
+and puts the values in variable "cel". The CDEF used is evaluated
+as follows:
+
+ CDEF:far=9,5,/,cel,*,32,+
+ 1. push 9, push 5
+ 2. push function "divide" and process it
+ the stack now contains 9/5
+ 3. push variable "cel"
+ 4. push function "multiply" and process it
+ the stack now contains 9/5*cel
+ 5. push 32
+ 6. push function "plus" and process it
+ the stack contains now the temperature in Fahrenheit
+
+=head2 Changing unknown into zero
+
+ rrdtool graph demo.png --title="Demo Graph" \
+ DEF:idat1=interface1.rrd:ds0:AVERAGE \
+ DEF:idat2=interface2.rrd:ds0:AVERAGE \
+ DEF:odat1=interface1.rrd:ds1:AVERAGE \
+ DEF:odat2=interface2.rrd:ds1:AVERAGE \
+ CDEF:agginput=idat1,UN,0,idat1,IF,idat2,UN,0,idat2,IF,+,8,* \
+ CDEF:aggoutput=odat1,UN,0,odat1,IF,odat2,UN,0,odat2,IF,+,8,* \
+ AREA:agginput#00cc00:Input Aggregate \
+ LINE1:aggoutput#0000FF:Output Aggregate
+
+These two CDEFs are built from several functions. It helps to split
+them when viewing what they do. Starting with the first CDEF we would
+get:
+
+ idat1,UN --> a
+ 0 --> b
+ idat1 --> c
+ if (a) then (b) else (c)
+
+The result is therefore "0" if it is true that "idat1" equals "UN".
+If not, the original value of "idat1" is put back on the stack.
+Lets call this answer "d". The process is repeated for the next
+five items on the stack, it is done the same and will return answer
+"h". The resulting stack is therefore "d,h".
+The expression has been simplified to "d,h,+,8,*" and it will now be
+easy to see that we add "d" and "h", and multiply the result with eight.
+
+The end result is that we have added "idat1" and "idat2" and in the
+process we effectively ignored unknown values. The result is multiplied
+by eight, most likely to convert bytes/s to bits/s.
+
+=head2 Infinity demo
+
+ rrdtool graph example.png --title="INF demo" \
+ DEF:val1=some.rrd:ds0:AVERAGE \
+ DEF:val2=some.rrd:ds1:AVERAGE \
+ DEF:val3=some.rrd:ds2:AVERAGE \
+ DEF:val4=other.rrd:ds0:AVERAGE \
+ CDEF:background=val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF \
+ CDEF:wipeout=val1,val2,val3,val4,+,+,+,UN,INF,UNKN,IF \
+ AREA:background#F0F0F0 \
+ AREA:val1#0000FF:Value1 \
+ STACK:val2#00C000:Value2 \
+ STACK:val3#FFFF00:Value3 \
+ STACK:val4#FFC000:Value4 \
+ AREA:whipeout#FF0000:Unknown
+
+This demo demonstrates two ways to use infinity. It is a bit tricky
+to see what happens in the "background" CDEF.
+
+ "val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF"
+
+This RPN takes the value of "val4" as input and then immediately
+removes it from the stack using "POP". The stack is now empty but
+as a side effect we now know the time that this sample was taken.
+This time is put on the stack by the "TIME" function.
+
+"TIME,7200,%" takes the modulo of time and 7'200 (which is two hours).
+The resulting value on the stack will be a number in the range from
+0 to 7199.
+
+For people who don't know the modulo function: it is the remainder
+after an integer division. If you divide 16 by 3, the answer would
+be 5 and the remainder would be 1. So, "16,3,%" returns 1.
+
+We have the result of "TIME,7200,%" on the stack, lets call this
+"a". The start of the RPN has become "a,3600,LE" and this checks
+if "a" is less or equal than "3600". It is true half of the time.
+We now have to process the rest of the RPN and this is only a simple
+"IF" function that returns either "INF" or "UNKN" depending on the
+time. This is returned to variable "background".
+
+The second CDEF has been discussed earlier in this document so we
+won't do that here.
+
+Now you can draw the different layers. Start with the background
+that is either unknown (nothing to see) or infinite (the whole
+positive part of the graph gets filled).
+
+Next you draw the data on top of this background, it will overlay
+the background. Suppose one of val1..val4 would be unknown, in that
+case you end up with only three bars stacked on top of each other.
+You don't want to see this because the data is only valid when all
+four variables are valid. This is why you use the second CDEF, it
+will overlay the data with an AREA so the data cannot be seen anymore.
+
+If your data can also have negative values you also need to overwrite
+the other half of your graph. This can be done in a relatively simple
+way: what you need is the "wipeout" variable and place a negative
+sign before it: "CDEF:wipeout2=wipeout,-1,*"
+
+=head2 Filtering data
+
+You may do some complex data filtering:
+
+ MEDIAN FILTER: filters shot noise
+
+ DEF:var=database.rrd:traffic:AVERAGE
+ CDEF:prev1=PREV(var)
+ CDEF:prev2=PREV(prev1)
+ CDEF:prev3=PREV(prev2)
+ CDEF:median=prev1,prev2,prev3,+,+,3,/
+ LINE3:median#000077:filtered
+ LINE1:prev2#007700:'raw data'
+
+
+ DERIVATE:
+
+ DEF:var=database.rrd:traffic:AVERAGE
+ CDEF:prev1=PREV(var)
+ CDEF:time=TIME
+ CDEF:prevtime=PREV(time)
+ CDEF:derivate=var,prev1,-,time,prevtime,-,/
+ LINE3:derivate#000077:derivate
+ LINE1:var#007700:'raw data'
+
+
+=head1 Out of ideas for now
+
+This document was created from questions asked by either myself or by
+other people on the RRDtool mailing list. Please let me know if you
+find errors in it or if you have trouble understanding it. If you
+think there should be an addition, mail me:
+E<lt>alex@ergens.op.het.netE<gt>
+
+Remember: B<No feedback equals no changes!>
+
+=head1 SEE ALSO
+
+The RRDtool manpages
+
+=head1 AUTHOR
+
+Alex van den Bogaerdt
+E<lt>alex@ergens.op.het.netE<gt>
diff --git a/program/doc/name.inc b/program/doc/name.inc
--- /dev/null
+++ b/program/doc/name.inc
@@ -0,0 +1,11 @@
+=head1 NAME
+
+=cut
+
+WARNING: DO NOT EDIT THE POD FILES. THEY ARE AUTO-GENERATED
+
+=pod
+
+rrdtool graph - Round Robin Database tool grapher functions
+
+Documentation for version 1.2.0
diff --git a/program/doc/rpntutorial.pod b/program/doc/rpntutorial.pod
--- /dev/null
@@ -0,0 +1,198 @@
+=head1 NAME
+
+rpntutorial - Reading RRDtool RPN Expressions by Steve Rader
+
+=head1 DESCRIPTION
+
+This tutorial should help you get to grips with RRDtool RPN expressions
+as seen in CDEF arguments of RRDtool graph.
+
+=head1 Reading Comparison Operators
+
+The LT, LE, GT, GE and EQ RPN logic operators are not as tricky as
+they appear. These operators act on the two values on the stack
+preceding them (to the left). Read these two values on the stack
+from left to right inserting the operator in the middle. If the
+resulting statement is true, then replace the three values from the
+stack with "1". If the statement if false, replace the three values
+with "0".
+
+For example, think about "2,1,GT". This RPN expression could be
+read as "is two greater than one?" The answer to that question is
+"true". So the three values should be replaced with "1". Thus the
+RPN expression 2,1,GT evaluates to 1.
+
+Now consider "2,1,LE". This RPN expression could be read as "is
+two less than or equal to one?". The natural response is "no"
+and thus the RPN expression 2,1,LE evaluates to 0.
+
+=head1 Reading the IF Operator
+
+The IF RPN logic operator can be straightforward also. The key
+to reading IF operators is to understand that the condition part
+of the traditional "if X than Y else Z" notation has *already*
+been evaluated. So the IF operator acts on only one value on the
+stack: the third value to the left of the IF value. The second
+value to the left of the IF corresponds to the true ("Y") branch.
+And the first value to the left of the IF corresponds to the false
+("Z") branch. Read the RPN expression "X,Y,Z,IF" from left to
+right like so: "if X then Y else Z".
+
+For example, consider "1,10,100,IF". It looks bizarre to me.
+But when I read "if 1 then 10 else 100" it's crystal clear: 1 is true
+so the answer is 10. Note that only zero is false; all other values
+are true. "2,20,200,IF" ("if 2 then 20 else 200") evaluates to 20.
+And "0,1,2,IF" ("if 0 then 1 else 2) evaluates to 2.
+
+
+Notice that none of the above examples really simulate the whole
+"if X then Y else Z" statement. This is because computer programmers
+read this statement as "if Some Condition then Y else Z". So it's
+important to be able to read IF operators along with the LT, LE,
+GT, GE and EQ operators.
+
+=head1 Some Examples
+
+While compound expressions can look overly complex, they can be
+considered elegantly simple. To quickly comprehend RPN expressions,
+you must know the the algorithm for evaluating RPN expressions:
+iterate searches from the left to the right looking for an operator.
+When it's found, apply that operator by popping the operator and some
+number of values (and by definition, not operators) off the stack.
+
+For example, the stack "1,2,3,+,+" gets "2,3,+" evaluated (as "2+3")
+during the first iteration and is replaced by 5. This results in
+the stack "1,5,+". Finally, "1,5,+" is evaluated resulting in the
+answer 6. For convenience, it's useful to write this set of
+operations as:
+
+ 1) 1,2,3,+,+ eval is 2,3,+ = 5 result is 1,5,+
+ 2) 1,5,+ eval is 1,5,+ = 6 result is 6
+ 3) 6
+
+Let's use that notation to conveniently solve some complex RPN expressions
+with multiple logic operators:
+
+ 1) 20,10,GT,10,20,IF eval is 20,10,GT = 1 result is 1,10,20,IF
+
+read the eval as pop "20 is greater than 10" so push 1
+
+ 2) 1,10,20,IF eval is 1,10,20,IF = 10 result is 10
+
+read pop "if 1 then 10 else 20" so push 10. Only 10 is left so
+10 is the answer.
+
+Let's read a complex RPN expression that also has the traditional
+multiplication operator:
+
+ 1) 128,8,*,7000,GT,7000,128,8,*,IF eval 128,8,* result is 1024
+ 2) 1024,7000,GT,7000,128,8,*,IF eval 1024,7000,GT result is 0
+ 3) 0,128,8,*,IF eval 128,8,* result is 1024
+ 4) 0,7000,1024,IF result is 1024
+
+
+Now let's go back to the first example of multiple logic operators,
+but replace the value 20 with the variable "input":
+
+ 1) input,10,GT,10,input,IF eval is input,10,GT ( lets call this A )
+
+Read eval as "if input > 10 then true" and replace "input,10,GT"
+with "A":
+
+ 2) A,10,input,IF eval is A,10,input,IF
+
+read "if A then 10 else input". Now replace A with it's verbose
+description againg and--voila!--you have a easily readable description
+of the expression:
+
+ if input > 10 then 10 else input
+
+Finally, let's go back to the first most complex example and replace
+the value 128 with "input":
+
+ 1) input,8,*,7000,GT,7000,input,8,*,IF eval input,8,* result is A
+
+where A is "input * 8"
+
+ 2) A,7000,GT,7000,input,8,*,IF eval is A,7000,GT result is B
+
+where B is "if ((input * 8) > 7000) then true"
+
+ 3) B,7000,input,8,*,IF eval is input,8,* result is C
+
+where C is "input * 8"
+
+ 4) B,7000,C,IF
+
+At last we have a readable decoding of the complex RPN expression with
+a variable:
+
+ if ((input * 8) > 7000) then 7000 else (input * 8)
+
+=head1 Exercises
+
+Exercise 1:
+
+Compute "3,2,*,1,+ and "3,2,1,+,*" by hand. Rewrite them in
+traditional notation. Explain why they have different answers.
+
+Answer 1:
+
+ 3*2+1 = 7 and 3*(2+1) = 9. These expressions have
+ different answers because the altering of the plus and
+ times operators alter the order of their evaluation.
+
+
+Exercise 2:
+
+One may be tempted to shorten the expression
+
+ input,8,*,56000,GT,56000,input,*,8,IF
+
+by removing the redundant use of "input,8,*" like so:
+
+ input,56000,GT,56000,input,IF,8,*
+
+Use traditional notation to show these expressions are not the same.
+Write an expression that's equivalent to the first expression, but
+uses the LE and DIV operators.
+
+Answer 2:
+
+ if (input <= 56000/8 ) { input*8 } else { 56000 }
+ input,56000,8,DIV,LT,input,8,*,56000,IF
+
+
+Exercise 3:
+
+Briefly explain why traditional mathematic notation requires the
+use of parentheses. Explain why RPN notation does not require
+the use of parentheses.
+
+Answer 3:
+
+ Traditional mathematic expressions are evaluated by
+ doing multiplication and division first, then addition and
+ subtraction. Parentheses are used to force the evaluation of
+ addition before multiplication (etc). RPN does not require
+ parentheses because the ordering of objects on the stack
+ can force the evaluation of addition before multiplication.
+
+
+Exercise 4:
+
+Explain why it was desirable for the RRDtool developers to implement
+RPN notation instead of traditional mathematical notation.
+
+Answer 4:
+
+ The algorithm that implements traditional mathematical
+ notation is more complex then algorithm used for RPN.
+ So implementing RPN allowed Tobias Oetiker to write less
+ code! (The code is also less complex and therefore less
+ likely to have bugs.)
+
+
+=head1 AUTHOR
+
+Steve Rader E<lt>rader@wiscnet.netE<gt>
diff --git a/program/doc/rrd-beginners.pod b/program/doc/rrd-beginners.pod
--- /dev/null
@@ -0,0 +1,326 @@
+=head1 NAME
+
+rrd-beginners - RRDtool Beginners' Guide
+
+=head1 SYNOPSIS
+
+Helping new RRDtool users to understand the basics of RRDtool
+
+=head1 DESCRIPTION
+
+This manual is an attempt to assist beginners in understanding the concepts
+of RRDtool. It sheds a light on differences between RRDtool and other
+databases. With help of an example, it explains the structure of RRDtool
+database. This is followed by an overview of the "graph" feature of RRDtool.
+At the end, it has sample scripts that illustrate the
+usage/wrapping of RRDtool within Shell or Perl scripts.
+
+=head2 What makes RRDtool so special?
+
+RRDtool is GNU licensed software developed by Tobias Oetiker, a system
+manager at the Swiss Federal Institute of Technology. Though it is a
+database, there are distinct differences between RRDtool databases and other
+databases as listed below:
+
+=over
+
+=item *
+
+RRDtool stores data; that makes it a back-end tool. The RRDtool command set
+allows the creation of graphs; that makes it a front-end tool as well. Other
+databases just store data and can not create graphs.
+
+=item *
+
+In case of linear databases, new data gets appended at the bottom of
+the database table. Thus its size keeps on increasing, whereas the size of
+an RRDtool database is determined at creation time. Imagine an RRDtool
+database as the perimeter of a circle. Data is added along the
+perimeter. When new data reaches the starting point, it overwrites
+existing data. This way, the size of an RRDtool database always
+remains constant. The name "Round Robin" stems from this behavior.
+
+=item *
+
+Other databases store the values as supplied. RRDtool can be configured to
+calculate the rate of change from the previous to the current value and
+store this information instead.
+
+=item *
+
+Other databases get updated when values are supplied. The RRDtool database
+is structured in such a way that it needs data at predefined time
+intervals. If it does not get a new value during the interval, it stores an
+UNKNOWN value for that interval. So, when using the RRDtool database, it is
+imperative to use scripts that run at regular intervals to ensure a constant
+data flow to update the RRDtool database.
+
+=back
+
+RRDtool is designed to store time series of data. With every data
+update, an associated time stamp is stored. Time is always expressed
+in seconds passed since epoch (01-01-1970). RRDtool can be installed
+on Unix as well as Windows. It comes with a command set to carry out
+various operations on RRD databases. This command set can be accessed
+from the command line, as well as from Shell or Perl scripts. The
+scripts act as wrappers for accessing data stored in RRDtool
+databases.
+
+=head2 Understanding by an example
+
+The structure of an RRD database is different than other linear databases.
+Other databases define tables with columns, and many other parameters. These
+definitions sometimes are very complex, especially in large databases.
+RRDtool databases are primarily used for monitoring purposes and
+hence are very simple in structure. The parameters
+that need to be defined are variables that hold values and archives of those
+values. Being time sensitive, a couple of time related parameters are also
+defined. Because of its structure, the definition of an RRDtool database also
+includes a provision to specify specific actions to take in the absence of
+update values. Data Source (DS), heartbeat, Date Source Type (DST), Round
+Robin Archive (RRA), and Consolidation Function (CF) are some of the
+terminologies related to RRDtool databases.
+
+The structure of a database and the terminology associated with it can be
+best explained with an example.
+
+ rrdtool create target.rrd \
+ --start 1023654125 \
+ --step 300 \
+ DS:mem:GAUGE:600:0:671744 \
+ RRA:AVERAGE:0.5:12:24 \
+ RRA:AVERAGE:0.5:288:31
+
+This example creates a database named F<target.rrd>. Start time
+(1'023'654'125) is specified in total number of seconds since epoch
+(time in seconds since 01-01-1970). While updating the database, the
+update time is also specified. This update time MUST be large (later)
+then start time and MUST be in seconds since epoch.
+
+The step of 300 seconds indicates that database expects new values every
+300 seconds. The wrapper script should be scheduled to run every B<step>
+seconds so that it updates the database every B<step> seconds.
+
+DS (Data Source) is the actual variable which relates to the parameter on
+the device that is monitored. Its syntax is
+
+ DS:variable_name:DST:heartbeat:min:max
+
+B<DS> is a key word. C<variable_name> is a name under which the parameter is
+saved in the database. There can be as many DSs in a database as needed. After
+every step interval, a new value of DS is supplied to update the database.
+This value is also called Primary Data Point B<(PDP)>. In our example
+mentioned above, a new PDP is generated every 300 seconds.
+
+Note, that if you do NOT supply new datapoints exactly every 300 seconds,
+this is not a problem, RRDtool will interpolate the data accordingly.
+
+B<DST> (Data Source Type) defines the type of the DS. It can be
+COUNTER, DERIVE, ABSOLUTE, GAUGE. A DS declared as COUNTER will save
+the rate of change of the value over a step period. This assumes that
+the value is always increasing (the difference between the current and
+the previous value is greater than 0). Traffic counters on a router
+are an ideal candidate for using COUNTER as DST. DERIVE is the same as
+COUNTER, but it allows negative values as well. If you want to see the
+rate of I<change> in free diskspace on your server, then you might
+want to use the DERIVE data type. ABSOLUTE also saves the rate of
+change, but it assumes that the previous value is set to 0. The
+difference between the current and the previous value is always equal
+to the current value. Thus it just stores the current value divided by
+the step interval (300 seconds in our example). GAUGE does not save
+the rate of change. It saves the actual value itself. There are no
+divisions or calculations. Memory consumption in a server is a typical
+example of gauge. The difference between the different types DSTs can be
+explained better with the following example:
+
+ Values = 300, 600, 900, 1200
+ Step = 300 seconds
+ COUNTER DS = 1, 1, 1, 1
+ DERIVE DS = 1, 1, 1, 1
+ ABSOLUTE DS = 1, 2, 3, 4
+ GAUGE DS = 300, 600, 900, 1200
+
+The next parameter is B<heartbeat>. In our example, heartbeat is 600
+seconds. If the database does not get a new PDP within 300 seconds, it
+will wait for another 300 seconds (total 600 seconds). If it doesn't
+receive any PDP within 600 seconds, it will save an UNKNOWN value into
+the database. This UNKNOWN value is a special feature of RRDtool - it
+is much better than to assume a missing value was 0 (zero) or any
+other number which might also be a valid data value. For example, the
+traffic flow counter on a router keeps increasing. Lets say, a value
+is missed for an interval and 0 is stored instead of UNKNOWN. Now when
+the next value becomes available, it will calculate the difference
+between the current value and the previous value (0) which is not
+correct. So, inserting the value UNKNOWN makes much more sense here.
+
+The next two parameters are the minimum and maximum value,
+respectively. If the variable to be stored has predictable maximum and
+minimum values, this should be specified here. Any update value
+falling out of this range will be stored as UNKNOWN.
+
+The next line declares a round robin archive (RRA). The syntax for
+declaring an RRA is
+
+ RRA:CF:xff:step:rows
+
+RRA is the keyword to declare RRAs. The consolidation function (CF)
+can be AVERAGE, MINIMUM, MAXIMUM, and LAST. The concept of the
+consolidated data point (CDP) comes into the picture here. A CDP is
+CFed (averaged, maximum/minimum value or last value) from I<step>
+number of PDPs. This RRA will hold I<rows> CDPs.
+
+Lets have a look at the example above. For the first RRA, 12 (steps)
+PDPs (DS variables) are AVERAGEed (CF) to form one CDP. 24 (rows) of
+theses CDPs are archived. Each PDP occurs at 300 seconds. 12 PDPs
+represent 12 times 300 seconds which is 1 hour. It means 1 CDP (which
+is equal to 12 PDPs) represents data worth 1 hour. 24 such CDPs
+represent 1 day (1 hour times 24 CDPs). This means, this RRA is an
+archive for one day. After 24 CDPs, CDP number 25 will replace the 1st
+CDP. The second RRA saves 31 CDPs; each CPD represents an AVERAGE
+value for a day (288 PDPs, each covering 300 seconds = 24
+hours). Therefore this RRA is an archive for one month. A single
+database can have many RRAs. If there are multiple DSs, each
+individual RRA will save data for all the DSs in the database. For
+example, if a database has 3 DSs and daily, weekly, monthly, and
+yearly RRAs are declared, then each RRA will hold data from all 3 data
+sources.
+
+=head2 Graphical Magic
+
+Another important feature of RRDtool is its ability to create
+graphs. The "graph" command uses the "fetch" command internally to
+retrieve values from the database. With the retrieved values it draws
+graphs as defined by the parameters supplied on the command line. A
+single graph can show different DS (Data Sources) from a database. It
+is also possible to show the values from more than one database in a
+single graph. Often, it is necessary to perform some math on the
+values retrieved from the database before plotting them. For example,
+in SNMP replies, memory consumption values are usually specified in
+KBytes and traffic flow on interfaces is specified in Bytes. Graphs
+for these values will be more meaningful if values are represented in
+MBytes and mbps. The RRDtool graph command allows to define such
+conversions. Apart from mathematical calculations, it is also possible
+to perform logical operations such as greater than, less than, and
+if/then/else. If a database contains more than one RRA archive, then a
+question may arise - how does RRDtool decide which RRA archive to use
+for retrieving the values? RRDtool looks at several things when making
+its choice. First it makes sure that the RRA covers as much of the
+graphing time frame as possible. Second it looks at the resolution of
+the RRA compared to the resolution of the graph. It tries to find one
+which has the same or higher better resolution. With the "-r" option
+you can force RRDtool to assume a different resolution than the one
+calculated from the pixel width of the graph.
+
+Values of different variables can be presented in 5 different shapes
+in a graph - AREA, LINE1, LINE2, LINE3, and STACK. AREA is represented
+by a solid colored area with values as the boundary of this
+area. LINE1/2/3 (increasing width) are just plain lines representing
+the values. STACK is also an area but it is "stack"ed on top AREA or
+LINE1/2/3. Another important thing to note is that variables are
+plotted in the order they are defined in the graph command. Therefore
+care must be taken to define STACK only after defining AREA/LINE. It
+is also possible to put formatted comments within the graph. Detailed
+instructions can be found in the graph manual.
+
+=head2 Wrapping RRDtool within Shell/Perl script
+
+After understanding RRDtool it is now a time to actually use RRDtool
+in scripts. Tasks involved in network management are data collection,
+data storage, and data retrieval. In the following example, the
+previously created target.rrd database is used. Data collection and
+data storage is done using Shell scripts. Data retrieval and report
+generation is done using Perl scripts. These scripts are shown below:
+
+=head3 Shell script (collects data, updates database)
+
+ #!/bin/sh
+ a=0
+ while [ "$a" == 0 ]; do
+ snmpwalk -c public 192.168.1.250 hrSWRunPerfMem > snmp_reply
+ total_mem=`awk 'BEGIN {tot_mem=0}
+ { if ($NF == "KBytes")
+ {tot_mem=tot_mem+$(NF-1)}
+ }
+ END {print tot_mem}' snmp_reply`
+ # I can use N as a replacement for the current time
+ rrdtool update target.rrd N:$total_mem
+ # sleep until the next 300 seconds are full
+ perl -e 'sleep 300 - time % 300'
+ done # end of while loop
+
+=head3 Perl script (retrieves data from database and generates graphs and statistics)
+
+ #!/usr/bin/perl -w
+ # This script fetches data from target.rrd, creates a graph of memory
+ # consumption on the target (Dual P3 Processor 1 GHz, 656 MB RAM)
+
+ # call the RRD perl module
+ use lib qw( /usr/local/rrdtool-1.0.41/lib/perl ../lib/perl );
+ use RRDs;
+ my $cur_time = time(); # set current time
+ my $end_time = $cur_time - 86400; # set end time to 24 hours ago
+ my $start_time = $end_time - 2592000; # set start 30 days in the past
+
+ # fetch average values from the RRD database between start and end time
+ my ($start,$step,$ds_names,$data) =
+ RRDs::fetch("target.rrd", "AVERAGE",
+ "-r", "600", "-s", "$start_time", "-e", "$end_time");
+ # save fetched values in a 2-dimensional array
+ my $rows = 0;
+ my $columns = 0;
+ my $time_variable = $start;
+ foreach $line (@$data) {
+ $vals[$rows][$columns] = $time_variable;
+ $time_variable = $time_variable + $step;
+ foreach $val (@$line) {
+ $vals[$rows][++$columns] = $val;}
+ $rows++;
+ $columns = 0;
+ }
+ my $tot_time = 0;
+ my $count = 0;
+ # save the values from the 2-dimensional into a 1-dimensional array
+ for $i ( 0 .. $#vals ) {
+ $tot_mem[$count] = $vals[$i][1];
+ $count++;
+ }
+ my $tot_mem_sum = 0;
+ # calculate the total of all values
+ for $i ( 0 .. ($count-1) ) {
+ $tot_mem_sum = $tot_mem_sum + $tot_mem[$i];
+ }
+ # calculate the average of the array
+ my $tot_mem_ave = $tot_mem_sum/($count);
+ # create the graph
+ RRDs::graph ("/images/mem_$count.png", \
+ "--title= Memory Usage", \
+ "--vertical-label=Memory Consumption (MB)", \
+ "--start=$start_time", \
+ "--end=$end_time", \
+ "--color=BACK#CCCCCC", \
+ "--color=CANVAS#CCFFFF", \
+ "--color=SHADEB#9999CC", \
+ "--height=125", \
+ "--upper-limit=656", \
+ "--lower-limit=0", \
+ "--rigid", \
+ "--base=1024", \
+ "DEF:tot_mem=target.rrd:mem:AVERAGE", \
+ "CDEF:tot_mem_cor=tot_mem,0,671744,LIMIT,UN,0,tot_mem,IF,1024,/",\
+ "CDEF:machine_mem=tot_mem,656,+,tot_mem,-",\
+ "COMMENT:Memory Consumption between $start_time",\
+ "COMMENT: and $end_time ",\
+ "HRULE:656#000000:Maximum Available Memory - 656 MB",\
+ "AREA:machine_mem#CCFFFF:Memory Unused", \
+ "AREA:tot_mem_cor#6699CC:Total memory consumed in MB");
+ my $err=RRDs::error;
+ if ($err) {print "problem generating the graph: $err\n";}
+ # print the output
+ print "Average memory consumption is ";
+ printf "%5.2f",$tot_mem_ave/1024;
+ print " MB. Graphical representation can be found at /images/mem_$count.png.";
+
+=head1 AUTHOR
+
+Ketan Patel E<lt>k2pattu@yahoo.comE<gt>
+
diff --git a/program/doc/rrdbuild.pod b/program/doc/rrdbuild.pod
--- /dev/null
+++ b/program/doc/rrdbuild.pod
@@ -0,0 +1,205 @@
+=head1 NAME
+
+rrdbuild - Instructions for building RRDtool
+
+=head1 DESCRIPTION
+
+=head2 Overview
+
+If you downloaded the source of rrdtool you have to compile it. This
+document will give some information on how this is done.
+
+RRDtool relies on services of thrid part libraries. Some of these libraries
+may already be installed on your system. You have to compile copies of the other
+ones before you can build RRDtool.
+
+This document will tell you about all the necessary steps to get going.
+
+=head2 Building
+
+Before you start to build RRDtool, you have to decide two things:
+
+=over
+
+=item 1.
+
+In which directory you want to build the software.
+
+=item 2.
+
+Where you want to install the software.
+
+=back
+
+Once you have decided. Save the two locations into environment variables.
+Depending on the shell you are using, you can do either (bash,zsh):
+
+ BUILD_DIR=/tmp/rrdbuild
+ INSTALL_DIR=/usr/local/rrdtool-1.2.23
+
+Or if you run tcsh:
+
+ set BUILD_DIR=/tmp/rrdbuild
+ set INSTALL_DIR=/usr/local/rrdtool-1.2.23
+
+If your F</tmp> is mounted with the option noexec (RHEL seems todo that) you have to choose
+a different directory!
+
+Now make sure the BUILD_DIR exists and go there:
+
+ mkdir -p $BUILD_DIR
+ cd $BUILD_DIR
+
+Lets first assume you already have all the necessary libraries
+pre-installed. Note that these instructions assume that your copies of
+B<tar> and B<make> are actually B<GNU tar> and B<GNU make> respectively. It
+could be that they are installed as B<gtar> and B<gmake> on your system.
+
+ wget http://oss.oetiker.ch/rrdtool/pub/rrdtool-1.2.23.tar.gz
+ tar zxf rrdtool-1.2.23.tar.gz
+ cd rrdtool-1.2.23
+ ./configure --prefix=$INSTALL_DIR && make && make install
+
+Ok, this was very optimistic. This try will probably have ended with
+B<configure> complaining about several missing libraries. If you are on a
+Linux or *bsd system you may want to just install the missing bits from your
+software repository. When you do that, make sure you also get the B<-dev>
+package for each library you install. Once you have the missing bits on
+board, just re-run the last line of the instructions above.
+
+But again this may have been too optimistic, and you actually have to
+compile your own copies of the required libraries.
+
+=head3 Build Tipps for AIX
+
+If you are woking with AIX, you may find the the B<--disable-shared> option
+will cause things to break for you. In that case you may have to install the
+shared libraries into the rrdtool PREFIX and work with B<--disable-static>
+instead.
+
+Another hint to get rrdtool working on AIX is to use the IBM XL C Compiler:
+
+ export CC=/usr/vac/bin/cc
+ export PERLCC=$CC
+
+(Better instructions for AIX welcome!)
+
+=head2 Building Libraries
+
+In order to build the libraries you need a compiler on your system.
+Unfortunately compilers are not all alike. This has an effect on the CFLAGS
+you want to set. The examples below are for the popular GCC compiler suite.
+If you have an other compile you have to use the following settings:
+
+=over
+
+=item Sun Forte
+
+ CFLAGS="-xO3 -kPIC"
+
+=back
+
+=over
+
+=item Building zlib
+
+ cd $BUILD_DIR
+ wget http://oss.oetiker.ch/rrdtool/pub/libs/zlib-1.2.3.tar.gz
+ tar zxf zlib-1.2.3.tar.gz
+ cd zlib-1.2.3
+ env CFLAGS="-O3 -fPIC" ./configure --prefix=$BUILD_DIR/lb
+ make
+ make install
+
+=item Building libpng
+
+Libpng itself requires zlib to build, so we need to help a bit. If you
+already have a copy of zlib on your system (which is very likley) you can
+drop the settings of LDFLAGS and CPPFLAGS. Note that the backslash (\) at
+the end of line 4 means that line 4 and line 5 are on one line.
+
+ cd $BUILD_DIR
+ wget http://oss.oetiker.ch/rrdtool/pub/libs/libpng-1.2.10.tar.gz
+ tar zxvf libpng-1.2.10.tar.gz
+ cd libpng-1.2.10
+ env CPPFLAGS="-I$BUILD_DIR/lb/include" LDFLAGS="-L$BUILD_DIR/lb/lib" CFLAGS="-O3 -fPIC" \
+ ./configure --disable-shared --prefix=$BUILD_DIR/lb
+ make
+ make install
+
+=item Building freetype
+
+ cd $BUILD_DIR
+ wget http://oss.oetiker.ch/rrdtool/pub/libs/freetype-2.1.10.tar.bz2
+ tar jxvf freetype-2.1.10.tar.bz2
+ cd freetype-2.1.10
+ env CPPFLAGS="-I$BUILD_DIR/lb/include" LDFLAGS="-L$BUILD_DIR/lb/lib" CFLAGS="-O3 -fPIC" \
+ ./configure --disable-shared --prefix=$BUILD_DIR/lb
+ make
+ make install
+
+If you run into problems building freetype on Solaris, you may want to try to
+add the following at the end of the configure line:
+
+ GNUMAKE=gmake EGREP=egrep
+
+=item Building libart_lgpl
+
+ cd $BUILD_DIR
+ wget http://oss.oetiker.ch/rrdtool/pub/libs/libart_lgpl-2.3.17.tar.gz
+ tar zxvf libart_lgpl-2.3.17.tar.gz
+ cd libart_lgpl-2.3.17
+ env CFLAGS="-O3 -fPIC" ./configure --disable-shared --prefix=$BUILD_DIR/lb
+ make
+ make install
+
+=back
+
+Now all the dependent libraries are built and you can try again. Since these
+are static libraries, you may have to use F<ranlib> to make them accessible.
+Especially BSD systems like Mac OS X may require this, Linux and Solaris
+will do just fine without since their F<ar> command does ranlibs job as well.
+
+ ranlib $BUILD_DIR/lb/lib/*.a
+
+This time you tell configure where it should be looking for libraries and
+include files. This is done via environment variables. Depending on the
+shell you are running, the syntax for setting environment variables is
+different. Under csh/tcsh you use:
+
+ set IR=-I$BUILD_DIR/lb/include
+ setenv CPPFLAGS "$IR $IR/libart-2.0 $IR/freetype2 $IR/libpng"
+ setenv LDFLAGS -L$BUILD_DIR/lb/lib
+ setenv CFLAGS -O3
+
+If you are running bash/sh/ash/ksh/zsh use this:
+
+ IR=-I$BUILD_DIR/lb/include
+ CPPFLAGS="$IR $IR/libart-2.0 $IR/freetype2 $IR/libpng"
+ LDFLAGS="-L$BUILD_DIR/lb/lib"
+ CFLAGS=-O3
+ export CPPFLAGS LDFLAGS CFLAGS
+
+And finally try building again. We disable the python and tcl bindings
+because it seems that a fair number of people have ill configured python and
+tcl setups that would prevent rrdtool from building if they are included in
+their current state.
+
+ cd $BUILD_DIR/rrdtool-1.2.23
+ ./configure --prefix=$INSTALL_DIR --disable-python --disable-tcl
+ make clean
+ make
+ make install
+
+SOLARIS HINT: if you want to build the perl module for the native perl (the
+one shipping with solaris) you will need the sun forte compiler
+installed on your box or you have to hand-tune bindings/perl-shared/Makefile
+while building!
+
+Now go to I<$INSTALL_DIR>B</share/rrdtool/examples/> and run them to see if your
+build has been successful.
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
diff --git a/program/doc/rrdcgi.pod b/program/doc/rrdcgi.pod
--- /dev/null
+++ b/program/doc/rrdcgi.pod
@@ -0,0 +1,225 @@
+=head1 NAME
+
+rrdcgi - Create web pages containing RRD graphs based on templates
+
+=head1 SYNOPSIS
+
+C<#!/path/to/>B<rrdcgi> S<[B<--filter>]>
+
+=head1 DESCRIPTION
+
+B<rrdcgi> is a sort of very limited script interpreter. Its purpose
+is to run as a cgi-program and parse a web page template containing special
+E<lt>RRD:: tags. B<rrdcgi> will interpret and act according to these tags.
+In the end it will printout a web page including the necessary CGI headers.
+
+B<rrdcgi> parses the contents of the template in 3 steps. In each step it looks
+only for a subset of tags. This allows nesting of tags.
+
+The argument parser uses the same semantics as you are used from your C-shell.
+
+=over 8
+
+=item B<--filter>
+
+Assume that rrdcgi is run as a filter and not as a cgi.
+
+=back
+
+=head2 Keywords
+
+=over 8
+
+=item RRD::CV I<name>
+
+Inserts the CGI variable of the given name.
+
+=item RRD::CV::QUOTE I<name>
+
+Inserts the CGI variable of the given name but quotes it, ready for
+use as an argument in another RRD:: tag. So even when there are spaces in the
+value of the CGI variable it will still be considered to be one argument.
+
+=item RRD::CV::PATH I<name>
+
+Inserts the CGI variable of the given name, quotes it and makes sure
+it starts neither with a '/' nor contains '..'. This is to make
+sure that no problematic pathnames can be introduced through the
+CGI interface.
+
+=item RRD::GETENV I<variable>
+
+Get the value of an environment variable.
+
+ <RRD::GETENV REMOTE_USER>
+
+might give you the name of the remote user given you are using
+some sort of access control on the directory.
+
+
+=item RRD::GOODFOR I<seconds>
+
+Specify the number of seconds this page should remain valid. This will prompt
+the rrdcgi to output a Last-Modified, an Expire and if the number of
+seconds is I<negative> a Refresh header.
+
+=item RRD::INCLUDE I<filename>
+
+Include the contents of the specified file into the page returned from the cgi.
+
+=item RRD::SETENV I<variable> I<value>
+
+If you want to present your graphs in another time zone than your own, you
+could use
+
+ <RRD::SETENV TZ UTC>
+
+to make sure everything is presented in Universal Time. Note that the
+values permitted to TZ depend on your OS.
+
+=item RRD::SETVAR I<variable> I<value>
+
+Analog to SETENV but for local variables.
+
+=item RRD::GETVAR I<variable>
+
+Analog to GETENV but for local variables.
+
+=item RRD::TIME::LAST I<rrd-file> I<strftime-format>
+
+This gets replaced by the last modification time of the selected RRD. The
+time is I<strftime>-formatted with the string specified in the second argument.
+
+=item RRD::TIME::NOW I<strftime-format>
+
+This gets replaced by the current time of day. The time is
+I<strftime>-formatted with the string specified in the argument.
+
+Note that if you return : (colons) from your strftime format you may
+have to escape them using \ if the time is to be used as an argument
+to a GRAPH command.
+
+=item RRD::TIME::STRFTIME I<START|END> I<start-spec> I<end-spec> I<strftime-format>
+
+This gets replaced by a strftime-formatted time using the format
+I<strftime-format> on either I<start-spec> or I<end-spec> depending on
+whether I<START> or I<END> is specified. Both I<start-spec> and I<end-spec>
+must be supplied as either could be relative to the other. This is intended
+to allow pretty titles on graphs with times that are easier for non RRDtool
+folks to figure out than "-2weeks".
+
+Note that again, if you return : (colon) from your strftime format,
+you may have to escape them using \ if the time is to be used as an
+argument to a GRAPH command.
+
+=item RRD::GRAPH I<rrdgraph arguments>
+
+This tag creates the RRD graph defined by its argument and then is
+replaced by an appropriate E<lt>IMG ... E<gt> tag referring to the graph.
+The B<--lazy> option in RRD graph can be used to make sure that graphs
+are only regenerated when they are out of date. The arguments
+to the B<RRD::GRAPH> tag work as described in the B<rrdgraph> manual page.
+
+Use the B<--lazy> option in your RRD::GRAPH tags, to reduce the load
+on your server. This option makes sure that graphs are only regenerated when
+the old ones are out of date.
+
+If you do not specify your own B<--imginfo> format, the following will
+be used:
+
+ <IMG SRC="%s" WIDTH="%lu" HEIGHT="%lu">
+
+Note that %s stands for the filename part of the graph generated, all
+directories given in the PNG file argument will get dropped.
+
+=item RRD::PRINT I<number>
+
+If the preceding B<RRD::GRAPH> tag contained and B<PRINT> arguments,
+then you can access their output with this tag. The I<number> argument refers to the
+number of the B<PRINT> argument. This first B<PRINT> has I<number> 0.
+
+=item RRD::INTERNAL <var>
+
+This tag gets replaced by an internal var. Currently these vars are known:
+VERSION, COMPILETIME.
+These vars represent the compiled-in values.
+
+=back
+
+=head1 EXAMPLE 1
+
+The example below creates a web pages with a single RRD graph.
+
+ #!/usr/local/bin/rrdcgi
+ <HTML>
+ <HEAD><TITLE>RRDCGI Demo</TITLE></HEAD>
+ <BODY>
+ <H1>RRDCGI Example Page</H1>
+ <P>
+ <RRD::GRAPH demo.png --lazy --title="Temperatures"
+ DEF:cel=demo.rrd:exhaust:AVERAGE
+ LINE2:cel#00a000:"D. Celsius">
+
+ </P>
+ </BODY>
+ </HTML>
+
+=head1 EXAMPLE 2
+
+This script is slightly more elaborate, it allows you to run it from
+a form which sets RRD_NAME. RRD_NAME is then used to select which RRD
+you want to use as source for your graph.
+
+ #!/usr/local/bin/rrdcgi
+ <HTML>
+ <HEAD><TITLE>RRDCGI Demo</TITLE></HEAD>
+ <BODY>
+ <H1>RRDCGI Example Page for <RRD::CV RRD_NAME></H1>
+ <H2>Selection</H2>
+ <FORM><INPUT NAME=RRD_NAME TYPE=RADIO VALUE=roomA> Room A,
+ <INPUT NAME=RRD_NAME TYPE=RADIO VALUE=roomB> Room B.
+ <INPUT TYPE=SUBMIT></FORM>
+ <H2>Graph</H2>
+ <P>
+ <RRD::GRAPH <RRD::CV::PATH RRD_NAME>.png --lazy
+ --title "Temperatures for "<RRD::CV::QUOTE RRD_NAME>
+ DEF:cel=<RRD::CV::PATH RRD_NAME>.rrd:exhaust:AVERAGE
+ LINE2:cel#00a000:"D. Celsius">
+
+ </P>
+ </BODY>
+ </HTML>
+
+=head1 EXAMPLE 3
+
+This example shows how to handle the case where the RRD, graphs and
+cgi-bins are seperate directories
+
+ #!/.../bin/rrdcgi
+ <HTML>
+ <HEAD><TITLE>RRDCGI Demo</TITLE></HEAD>
+ <BODY>
+ <H1>RRDCGI test Page</H1>
+ <RRD::GRAPH
+ /.../web/pngs/testhvt.png
+ --imginfo '<IMG SRC=/.../pngs/%s WIDTH=%lu HEIGHT=%lu >'
+ --lazy --start -1d --end now
+ DEF:http_src=/.../rrds/test.rrd:http_src:AVERAGE
+ AREA:http_src#00ff00:http_src
+ >
+ </BODY>
+ </HTML>
+
+Note 1: Replace /.../ with the relevant directories
+
+Note 2: The SRC=/.../pngs should be paths from the view of the
+webserver/browser
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
+
+
+
+
diff --git a/program/doc/rrdcreate.pod b/program/doc/rrdcreate.pod
--- /dev/null
@@ -0,0 +1,553 @@
+=head1 NAME
+
+rrdcreate - Set up a new Round Robin Database
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<create> I<filename>
+S<[B<--start>|B<-b> I<start time>]>
+S<[B<--step>|B<-s> I<step>]>
+S<[B<DS:>I<ds-name>B<:>I<DST>B<:>I<dst arguments>]>
+S<[B<RRA:>I<CF>B<:>I<cf arguments>]>
+
+=head1 DESCRIPTION
+
+The create function of RRDtool lets you set up new Round Robin
+Database (B<RRD>) files. The file is created at its final, full size
+and filled with I<*UNKNOWN*> data.
+
+=over 8
+
+=item I<filename>
+
+The name of the B<RRD> you want to create. B<RRD> files should end
+with the extension F<.rrd>. However, B<RRDtool> will accept any
+filename.
+
+=item B<--start>|B<-b> I<start time> (default: now - 10s)
+
+Specifies the time in seconds since 1970-01-01 UTC when the first
+value should be added to the B<RRD>. B<RRDtool> will not accept
+any data timed before or at the time specified.
+
+See also AT-STYLE TIME SPECIFICATION section in the
+I<rrdfetch> documentation for other ways to specify time.
+
+=item B<--step>|B<-s> I<step> (default: 300 seconds)
+
+Specifies the base interval in seconds with which data will be fed
+into the B<RRD>.
+
+=item B<DS:>I<ds-name>B<:>I<DST>B<:>I<dst arguments>
+
+A single B<RRD> can accept input from several data sources (B<DS>),
+for example incoming and outgoing traffic on a specific communication
+line. With the B<DS> configuration option you must define some basic
+properties of each data source you want to store in the B<RRD>.
+
+I<ds-name> is the name you will use to reference this particular data
+source from an B<RRD>. A I<ds-name> must be 1 to 19 characters long in
+the characters [a-zA-Z0-9_].
+
+I<DST> defines the Data Source Type. The remaining arguments of a
+data source entry depend on the data source type. For GAUGE, COUNTER,
+DERIVE, and ABSOLUTE the format for a data source entry is:
+
+B<DS:>I<ds-name>B<:>I<GAUGE | COUNTER | DERIVE | ABSOLUTE>B<:>I<heartbeat>B<:>I<min>B<:>I<max>
+
+For COMPUTE data sources, the format is:
+
+B<DS:>I<ds-name>B<:>I<COMPUTE>B<:>I<rpn-expression>
+
+In order to decide which data source type to use, review the
+definitions that follow. Also consult the section on "HOW TO MEASURE"
+for further insight.
+
+=over 4
+
+=item B<GAUGE>
+
+is for things like temperatures or number of people in a room or the
+value of a RedHat share.
+
+=item B<COUNTER>
+
+is for continuous incrementing counters like the ifInOctets counter in
+a router. The B<COUNTER> data source assumes that the counter never
+decreases, except when a counter overflows. The update function takes
+the overflow into account. The counter is stored as a per-second
+rate. When the counter overflows, RRDtool checks if the overflow
+happened at the 32bit or 64bit border and acts accordingly by adding
+an appropriate value to the result.
+
+=item B<DERIVE>
+
+will store the derivative of the line going from the last to the
+current value of the data source. This can be useful for gauges, for
+example, to measure the rate of people entering or leaving a
+room. Internally, derive works exactly like COUNTER but without
+overflow checks. So if your counter does not reset at 32 or 64 bit you
+might want to use DERIVE and combine it with a MIN value of 0.
+
+=over
+
+=item NOTE on COUNTER vs DERIVE
+
+by Don Baarda E<lt>don.baarda@baesystems.comE<gt>
+
+If you cannot tolerate ever mistaking the occasional counter reset for a
+legitimate counter wrap, and would prefer "Unknowns" for all legitimate
+counter wraps and resets, always use DERIVE with min=0. Otherwise, using
+COUNTER with a suitable max will return correct values for all legitimate
+counter wraps, mark some counter resets as "Unknown", but can mistake some
+counter resets for a legitimate counter wrap.
+
+For a 5 minute step and 32-bit counter, the probability of mistaking a
+counter reset for a legitimate wrap is arguably about 0.8% per 1Mbps of
+maximum bandwidth. Note that this equates to 80% for 100Mbps interfaces, so
+for high bandwidth interfaces and a 32bit counter, DERIVE with min=0 is
+probably preferable. If you are using a 64bit counter, just about any max
+setting will eliminate the possibility of mistaking a reset for a counter
+wrap.
+
+=back
+
+=item B<ABSOLUTE>
+
+is for counters which get reset upon reading. This is used for fast counters
+which tend to overflow. So instead of reading them normally you reset them
+after every read to make sure you have a maximum time available before the
+next overflow. Another usage is for things you count like number of messages
+since the last update.
+
+=item B<COMPUTE>
+
+is for storing the result of a formula applied to other data sources
+in the B<RRD>. This data source is not supplied a value on update, but
+rather its Primary Data Points (PDPs) are computed from the PDPs of
+the data sources according to the rpn-expression that defines the
+formula. Consolidation functions are then applied normally to the PDPs
+of the COMPUTE data source (that is the rpn-expression is only applied
+to generate PDPs). In database software, such data sets are referred
+to as "virtual" or "computed" columns.
+
+=back
+
+I<heartbeat> defines the maximum number of seconds that may pass
+between two updates of this data source before the value of the
+data source is assumed to be I<*UNKNOWN*>.
+
+I<min> and I<max> define the expected range values for data supplied by a
+data source. If I<min> and/or I<max> any value outside the defined range
+will be regarded as I<*UNKNOWN*>. If you do not know or care about min and
+max, set them to U for unknown. Note that min and max always refer to the
+processed values of the DS. For a traffic-B<COUNTER> type DS this would be
+the maximum and minimum data-rate expected from the device.
+
+I<If information on minimal/maximal expected values is available,
+always set the min and/or max properties. This will help RRDtool in
+doing a simple sanity check on the data supplied when running update.>
+
+I<rpn-expression> defines the formula used to compute the PDPs of a
+COMPUTE data source from other data sources in the same <RRD>. It is
+similar to defining a B<CDEF> argument for the graph command. Please
+refer to that manual page for a list and description of RPN operations
+supported. For COMPUTE data sources, the following RPN operations are
+not supported: COUNT, PREV, TIME, and LTIME. In addition, in defining
+the RPN expression, the COMPUTE data source may only refer to the
+names of data source listed previously in the create command. This is
+similar to the restriction that B<CDEF>s must refer only to B<DEF>s
+and B<CDEF>s previously defined in the same graph command.
+
+=item B<RRA:>I<CF>B<:>I<cf arguments>
+
+
+The purpose of an B<RRD> is to store data in the round robin archives
+(B<RRA>). An archive consists of a number of data values or statistics for
+each of the defined data-sources (B<DS>) and is defined with an B<RRA> line.
+
+When data is entered into an B<RRD>, it is first fit into time slots
+of the length defined with the B<-s> option, thus becoming a I<primary
+data point>.
+
+The data is also processed with the consolidation function (I<CF>) of
+the archive. There are several consolidation functions that
+consolidate primary data points via an aggregate function: B<AVERAGE>,
+B<MIN>, B<MAX>, B<LAST>. The format of B<RRA> line for these
+consolidation functions is:
+
+B<RRA:>I<AVERAGE | MIN | MAX | LAST>B<:>I<xff>B<:>I<steps>B<:>I<rows>
+
+I<xff> The xfiles factor defines what part of a consolidation interval may
+be made up from I<*UNKNOWN*> data while the consolidated value is still
+regarded as known. It is given as the ratio of allowed I<*UNKNOWN*> PDPs
+to the number of PDPs in the interval. Thus, it ranges from 0 to 1 (exclusive).
+
+
+I<steps> defines how many of these I<primary data points> are used to build
+a I<consolidated data point> which then goes into the archive.
+
+I<rows> defines how many generations of data values are kept in an B<RRA>.
+
+=back
+
+=head1 Aberrant Behavior Detection with Holt-Winters Forecasting
+
+In addition to the aggregate functions, there are a set of specialized
+functions that enable B<RRDtool> to provide data smoothing (via the
+Holt-Winters forecasting algorithm), confidence bands, and the
+flagging aberrant behavior in the data source time series:
+
+=over
+
+=item *
+
+B<RRA:>I<HWPREDICT>B<:>I<rows>B<:>I<alpha>B<:>I<beta>B<:>I<seasonal period>[B<:>I<rra-num>]
+
+=item *
+
+B<RRA:>I<SEASONAL>B<:>I<seasonal period>B<:>I<gamma>B<:>I<rra-num>
+
+=item *
+
+B<RRA:>I<DEVSEASONAL>B<:>I<seasonal period>B<:>I<gamma>B<:>I<rra-num>
+
+=item *
+
+B<RRA:>I<DEVPREDICT>B<:>I<rows>B<:>I<rra-num>
+
+=item *
+
+B<RRA:>I<FAILURES>B<:>I<rows>B<:>I<threshold>B<:>I<window length>B<:>I<rra-num>
+
+=back
+
+These B<RRAs> differ from the true consolidation functions in several ways.
+First, each of the B<RRA>s is updated once for every primary data point.
+Second, these B<RRAs> are interdependent. To generate real-time confidence
+bounds, a matched set of HWPREDICT, SEASONAL, DEVSEASONAL, and
+DEVPREDICT must exist. Generating smoothed values of the primary data points
+requires both a HWPREDICT B<RRA> and SEASONAL B<RRA>. Aberrant behavior
+detection requires FAILURES, HWPREDICT, DEVSEASONAL, and SEASONAL.
+
+The actual predicted, or smoothed, values are stored in the HWPREDICT
+B<RRA>. The predicted deviations are stored in DEVPREDICT (think a standard
+deviation which can be scaled to yield a confidence band). The FAILURES
+B<RRA> stores binary indicators. A 1 marks the indexed observation as
+failure; that is, the number of confidence bounds violations in the
+preceding window of observations met or exceeded a specified threshold. An
+example of using these B<RRAs> to graph confidence bounds and failures
+appears in L<rrdgraph>.
+
+The SEASONAL and DEVSEASONAL B<RRAs> store the seasonal coefficients for the
+Holt-Winters forecasting algorithm and the seasonal deviations, respectively.
+There is one entry per observation time point in the seasonal cycle. For
+example, if primary data points are generated every five minutes and the
+seasonal cycle is 1 day, both SEASONAL and DEVSEASONAL will have 288 rows.
+
+In order to simplify the creation for the novice user, in addition to
+supporting explicit creation of the HWPREDICT, SEASONAL, DEVPREDICT,
+DEVSEASONAL, and FAILURES B<RRAs>, the B<RRDtool> create command supports
+implicit creation of the other four when HWPREDICT is specified alone and
+the final argument I<rra-num> is omitted.
+
+I<rows> specifies the length of the B<RRA> prior to wrap around. Remember
+that there is a one-to-one correspondence between primary data points and
+entries in these RRAs. For the HWPREDICT CF, I<rows> should be larger than
+the I<seasonal period>. If the DEVPREDICT B<RRA> is implicitly created, the
+default number of rows is the same as the HWPREDICT I<rows> argument. If the
+FAILURES B<RRA> is implicitly created, I<rows> will be set to the I<seasonal
+period> argument of the HWPREDICT B<RRA>. Of course, the B<RRDtool>
+I<resize> command is available if these defaults are not sufficient and the
+creator wishes to avoid explicit creations of the other specialized function
+B<RRAs>.
+
+I<seasonal period> specifies the number of primary data points in a seasonal
+cycle. If SEASONAL and DEVSEASONAL are implicitly created, this argument for
+those B<RRAs> is set automatically to the value specified by HWPREDICT. If
+they are explicitly created, the creator should verify that all three
+I<seasonal period> arguments agree.
+
+I<alpha> is the adaption parameter of the intercept (or baseline)
+coefficient in the Holt-Winters forecasting algorithm. See L<rrdtool> for a
+description of this algorithm. I<alpha> must lie between 0 and 1. A value
+closer to 1 means that more recent observations carry greater weight in
+predicting the baseline component of the forecast. A value closer to 0 means
+that past history carries greater weight in predicting the baseline
+component.
+
+I<beta> is the adaption parameter of the slope (or linear trend) coefficient
+in the Holt-Winters forecasting algorithm. I<beta> must lie between 0 and 1
+and plays the same role as I<alpha> with respect to the predicted linear
+trend.
+
+I<gamma> is the adaption parameter of the seasonal coefficients in the
+Holt-Winters forecasting algorithm (HWPREDICT) or the adaption parameter in
+the exponential smoothing update of the seasonal deviations. It must lie
+between 0 and 1. If the SEASONAL and DEVSEASONAL B<RRAs> are created
+implicitly, they will both have the same value for I<gamma>: the value
+specified for the HWPREDICT I<alpha> argument. Note that because there is
+one seasonal coefficient (or deviation) for each time point during the
+seasonal cycle, the adaptation rate is much slower than the baseline. Each
+seasonal coefficient is only updated (or adapts) when the observed value
+occurs at the offset in the seasonal cycle corresponding to that
+coefficient.
+
+If SEASONAL and DEVSEASONAL B<RRAs> are created explicitly, I<gamma> need not
+be the same for both. Note that I<gamma> can also be changed via the
+B<RRDtool> I<tune> command.
+
+I<rra-num> provides the links between related B<RRAs>. If HWPREDICT is
+specified alone and the other B<RRAs> are created implicitly, then
+there is no need to worry about this argument. If B<RRAs> are created
+explicitly, then carefully pay attention to this argument. For each
+B<RRA> which includes this argument, there is a dependency between
+that B<RRA> and another B<RRA>. The I<rra-num> argument is the 1-based
+index in the order of B<RRA> creation (that is, the order they appear
+in the I<create> command). The dependent B<RRA> for each B<RRA>
+requiring the I<rra-num> argument is listed here:
+
+=over
+
+=item *
+
+HWPREDICT I<rra-num> is the index of the SEASONAL B<RRA>.
+
+=item *
+
+SEASONAL I<rra-num> is the index of the HWPREDICT B<RRA>.
+
+=item *
+
+DEVPREDICT I<rra-num> is the index of the DEVSEASONAL B<RRA>.
+
+=item *
+
+DEVSEASONAL I<rra-num> is the index of the HWPREDICT B<RRA>.
+
+=item *
+
+FAILURES I<rra-num> is the index of the DEVSEASONAL B<RRA>.
+
+=back
+
+I<threshold> is the minimum number of violations (observed values outside
+the confidence bounds) within a window that constitutes a failure. If the
+FAILURES B<RRA> is implicitly created, the default value is 7.
+
+I<window length> is the number of time points in the window. Specify an
+integer greater than or equal to the threshold and less than or equal to 28.
+The time interval this window represents depends on the interval between
+primary data points. If the FAILURES B<RRA> is implicitly created, the
+default value is 9.
+
+=head1 The HEARTBEAT and the STEP
+
+Here is an explanation by Don Baarda on the inner workings of RRDtool.
+It may help you to sort out why all this *UNKNOWN* data is popping
+up in your databases:
+
+RRDtool gets fed samples at arbitrary times. From these it builds Primary
+Data Points (PDPs) at exact times on every "step" interval. The PDPs are
+then accumulated into RRAs.
+
+The "heartbeat" defines the maximum acceptable interval between
+samples. If the interval between samples is less than "heartbeat",
+then an average rate is calculated and applied for that interval. If
+the interval between samples is longer than "heartbeat", then that
+entire interval is considered "unknown". Note that there are other
+things that can make a sample interval "unknown", such as the rate
+exceeding limits, or even an "unknown" input sample.
+
+The known rates during a PDP's "step" interval are used to calculate
+an average rate for that PDP. Also, if the total "unknown" time during
+the "step" interval exceeds the "heartbeat", the entire PDP is marked
+as "unknown". This means that a mixture of known and "unknown" sample
+times in a single PDP "step" may or may not add up to enough "unknown"
+time to exceed "heartbeat" and hence mark the whole PDP "unknown". So
+"heartbeat" is not only the maximum acceptable interval between
+samples, but also the maximum acceptable amount of "unknown" time per
+PDP (obviously this is only significant if you have "heartbeat" less
+than "step").
+
+The "heartbeat" can be short (unusual) or long (typical) relative to
+the "step" interval between PDPs. A short "heartbeat" means you
+require multiple samples per PDP, and if you don't get them mark the
+PDP unknown. A long heartbeat can span multiple "steps", which means
+it is acceptable to have multiple PDPs calculated from a single
+sample. An extreme example of this might be a "step" of 5 minutes and a
+"heartbeat" of one day, in which case a single sample every day will
+result in all the PDPs for that entire day period being set to the
+same average rate. I<-- Don Baarda E<lt>don.baarda@baesystems.comE<gt>>
+
+ time|
+ axis|
+ begin__|00|
+ |01|
+ u|02|----* sample1, restart "hb"-timer
+ u|03| /
+ u|04| /
+ u|05| /
+ u|06|/ "hbt" expired
+ u|07|
+ |08|----* sample2, restart "hb"
+ |09| /
+ |10| /
+ u|11|----* sample3, restart "hb"
+ u|12| /
+ u|13| /
+ step1_u|14| /
+ u|15|/ "swt" expired
+ u|16|
+ |17|----* sample4, restart "hb", create "pdp" for step1 =
+ |18| / = unknown due to 10 "u" labled secs > "hb"
+ |19| /
+ |20| /
+ |21|----* sample5, restart "hb"
+ |22| /
+ |23| /
+ |24|----* sample6, restart "hb"
+ |25| /
+ |26| /
+ |27|----* sample7, restart "hb"
+ step2__|28| /
+ |22| /
+ |23|----* sample8, restart "hb", create "pdp" for step1, create "cdp"
+ |24| /
+ |25| /
+
+graphics by I<vladimir.lavrov@desy.de>.
+
+
+=head1 HOW TO MEASURE
+
+Here are a few hints on how to measure:
+
+=over
+
+
+=item Temperature
+
+Usually you have some type of meter you can read to get the temperature.
+The temperature is not really connected with a time. The only connection is
+that the temperature reading happened at a certain time. You can use the
+B<GAUGE> data source type for this. RRDtool will then record your reading
+together with the time.
+
+=item Mail Messages
+
+Assume you have a method to count the number of messages transported by
+your mailserver in a certain amount of time, giving you data like '5
+messages in the last 65 seconds'. If you look at the count of 5 like an
+B<ABSOLUTE> data type you can simply update the RRD with the number 5 and the
+end time of your monitoring period. RRDtool will then record the number of
+messages per second. If at some later stage you want to know the number of
+messages transported in a day, you can get the average messages per second
+from RRDtool for the day in question and multiply this number with the
+number of seconds in a day. Because all math is run with Doubles, the
+precision should be acceptable.
+
+=item It's always a Rate
+
+RRDtool stores rates in amount/second for COUNTER, DERIVE and ABSOLUTE
+data. When you plot the data, you will get on the y axis
+amount/second which you might be tempted to convert to an absolute
+amount by multiplying by the delta-time between the points. RRDtool
+plots continuous data, and as such is not appropriate for plotting
+absolute amounts as for example "total bytes" sent and received in a
+router. What you probably want is plot rates that you can scale to
+bytes/hour, for example, or plot absolute amounts with another tool
+that draws bar-plots, where the delta-time is clear on the plot for
+each point (such that when you read the graph you see for example GB
+on the y axis, days on the x axis and one bar for each day).
+
+=back
+
+
+=head1 EXAMPLE
+
+ rrdtool create temperature.rrd --step 300 \
+ DS:temp:GAUGE:600:-273:5000 \
+ RRA:AVERAGE:0.5:1:1200 \
+ RRA:MIN:0.5:12:2400 \
+ RRA:MAX:0.5:12:2400 \
+ RRA:AVERAGE:0.5:12:2400
+
+This sets up an B<RRD> called F<temperature.rrd> which accepts one
+temperature value every 300 seconds. If no new data is supplied for
+more than 600 seconds, the temperature becomes I<*UNKNOWN*>. The
+minimum acceptable value is -273 and the maximum is 5'000.
+
+A few archive areas are also defined. The first stores the
+temperatures supplied for 100 hours (1'200 * 300 seconds = 100
+hours). The second RRA stores the minimum temperature recorded over
+every hour (12 * 300 seconds = 1 hour), for 100 days (2'400 hours). The
+third and the fourth RRA's do the same for the maximum and
+average temperature, respectively.
+
+=head1 EXAMPLE 2
+
+ rrdtool create monitor.rrd --step 300 \
+ DS:ifOutOctets:COUNTER:1800:0:4294967295 \
+ RRA:AVERAGE:0.5:1:2016 \
+ RRA:HWPREDICT:1440:0.1:0.0035:288
+
+This example is a monitor of a router interface. The first B<RRA> tracks the
+traffic flow in octets; the second B<RRA> generates the specialized
+functions B<RRAs> for aberrant behavior detection. Note that the I<rra-num>
+argument of HWPREDICT is missing, so the other B<RRAs> will implicitly be
+created with default parameter values. In this example, the forecasting
+algorithm baseline adapts quickly; in fact the most recent one hour of
+observations (each at 5 minute intervals) accounts for 75% of the baseline
+prediction. The linear trend forecast adapts much more slowly. Observations
+made during the last day (at 288 observations per day) account for only
+65% of the predicted linear trend. Note: these computations rely on an
+exponential smoothing formula described in the LISA 2000 paper.
+
+The seasonal cycle is one day (288 data points at 300 second intervals), and
+the seasonal adaption parameter will be set to 0.1. The RRD file will store 5
+days (1'440 data points) of forecasts and deviation predictions before wrap
+around. The file will store 1 day (a seasonal cycle) of 0-1 indicators in
+the FAILURES B<RRA>.
+
+The same RRD file and B<RRAs> are created with the following command,
+which explicitly creates all specialized function B<RRAs>.
+
+ rrdtool create monitor.rrd --step 300 \
+ DS:ifOutOctets:COUNTER:1800:0:4294967295 \
+ RRA:AVERAGE:0.5:1:2016 \
+ RRA:HWPREDICT:1440:0.1:0.0035:288:3 \
+ RRA:SEASONAL:288:0.1:2 \
+ RRA:DEVPREDICT:1440:5 \
+ RRA:DEVSEASONAL:288:0.1:2 \
+ RRA:FAILURES:288:7:9:5
+
+Of course, explicit creation need not replicate implicit create, a
+number of arguments could be changed.
+
+=head1 EXAMPLE 3
+
+ rrdtool create proxy.rrd --step 300 \
+ DS:Total:DERIVE:1800:0:U \
+ DS:Duration:DERIVE:1800:0:U \
+ DS:AvgReqDur:COMPUTE:Duration,Requests,0,EQ,1,Requests,IF,/ \
+ RRA:AVERAGE:0.5:1:2016
+
+This example is monitoring the average request duration during each 300 sec
+interval for requests processed by a web proxy during the interval.
+In this case, the proxy exposes two counters, the number of requests
+processed since boot and the total cumulative duration of all processed
+requests. Clearly these counters both have some rollover point, but using the
+DERIVE data source also handles the reset that occurs when the web proxy is
+stopped and restarted.
+
+In the B<RRD>, the first data source stores the requests per second rate
+during the interval. The second data source stores the total duration of all
+requests processed during the interval divided by 300. The COMPUTE data source
+divides each PDP of the AccumDuration by the corresponding PDP of
+TotalRequests and stores the average request duration. The remainder of the
+RPN expression handles the divide by zero case.
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
diff --git a/program/doc/rrddump.pod b/program/doc/rrddump.pod
--- /dev/null
+++ b/program/doc/rrddump.pod
@@ -0,0 +1,62 @@
+=head1 NAME
+
+rrddump - dump the contents of an RRD to XML format
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<dump> I<filename.rrd> E<gt> I<filename.xml>
+
+or
+
+B<rrdtool> B<dump> I<filename.rrd> I<filename.xml>
+
+=head1 DESCRIPTION
+
+The B<dump> function writes the contents of an B<RRD> in human
+readable (?) XML format to a file or to stdout. This format can
+be read by rrdrestore. Together they allow you to transfer your
+files from one computer architecture to another as well to
+manipulate the contents of an B<RRD> file in a somewhat more
+convenient manner.
+
+
+
+=over 8
+
+=item I<filename.rrd>
+
+The name of the B<RRD> you want to dump.
+
+=item I<filename.xml>
+
+The (optional) filename that you want to write the XML output to.
+If not specified, the XML will be printed to stdout.
+
+=back
+
+=head1 EXAMPLES
+
+To transfer an RRD between architectures, follow these steps:
+
+=over 4
+
+=item 1.
+
+On the same system where the RRD was created, use B<rrdtool> B<dump>
+to export the data to XML format.
+
+=item 2.
+
+Transfer the XML dump to the target system.
+
+=item 3.
+
+Run B<rrdtool> B<restore> to create a new RRD from the XML dump. See
+B<rrdrestore> for details.
+
+=back
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
diff --git a/program/doc/rrdfetch.pod b/program/doc/rrdfetch.pod
--- /dev/null
+++ b/program/doc/rrdfetch.pod
@@ -0,0 +1,262 @@
+=head1 NAME
+
+rrdfetch - Fetch data from an RRD.
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<fetch> I<filename> I<CF>
+S<[B<--resolution>|B<-r> I<resolution>]>
+S<[B<--start>|B<-s> I<start>]>
+S<[B<--end>|B<-e> I<end>]>
+
+=head1 DESCRIPTION
+
+The B<fetch> function is normally used internally by the graph
+function to get data from B<RRD>s. B<fetch> will analyze the B<RRD>
+and try to retrieve the data in the resolution requested.
+The data fetched is printed to stdout. I<*UNKNOWN*> data is often
+represented by the string "NaN" depending on your OS's printf
+function.
+
+=over 8
+
+=item I<filename>
+
+the name of the B<RRD> you want to fetch the data from.
+
+=item I<CF>
+
+the consolidation function that is applied to the data you
+want to fetch (AVERAGE,MIN,MAX,LAST)
+
+=item B<--resolution>|B<-r> I<resolution> (default is the highest resolution)
+
+the interval you want the values to have (seconds per
+value). B<rrdfetch> will try to match your request, but it will return
+data even if no absolute match is possible. B<NB.> See note below.
+
+=item B<--start>|B<-s> I<start> (default end-1day)
+
+start of the time series. A time in seconds since epoch (1970-01-01)
+is required. Negative numbers are relative to the current time. By default,
+one day worth of data will be fetched. See also AT-STYLE TIME SPECIFICATION
+section for a detailed explanation on ways to specify the start time.
+
+=item B<--end>|B<-e> I<end> (default now)
+
+the end of the time series in seconds since epoch. See also AT-STYLE
+TIME SPECIFICATION section for a detailed explanation of how to
+specify the end time.
+
+=back
+
+=head2 RESOLUTION INTERVAL
+
+In order to get RRDtool to fetch anything other than the finest resolution RRA
+B<both> the start and end time must be specified on boundaries that are
+multiples of the desired resolution. Consider the following example:
+
+ rrdtool create subdata.rrd -s 10 DS:ds0:GAUGE:300:0:U \
+ RRA:AVERAGE:0.5:30:3600 \
+ RRA:AVERAGE:0.5:90:1200 \
+ RRA:AVERAGE:0.5:360:1200 \
+ RRA:MAX:0.5:360:1200 \
+ RRA:AVERAGE:0.5:8640:600 \
+ RRA:MAX:0.5:8640:600
+
+This RRD collects data every 10 seconds and stores its averages over 5
+minutes, 15 minutes, 1 hour, and 1 day, as well as the maxima for 1 hour
+and 1 day.
+
+Consider now that you want to fetch the 15 minute average data for the
+last hour. You might try
+
+ rrdtool fetch subdata.rrd AVERAGE -r 900 -s -1h
+
+However, this will almost always result in a time series that is
+B<NOT> in the 15 minute RRA. Therefore, the highest resolution RRA,
+i.e. 5 minute averages, will be chosen which in this case is not
+what you want.
+
+Hence, make sure that
+
+=over 3
+
+=item 1.
+
+both start and end time are a multiple of 900
+
+=item 2.
+
+both start and end time are within the desired RRA
+
+=back
+
+So, if time now is called "t", do
+
+ end time == int(t/900)*900,
+ start time == end time - 1hour,
+ resolution == 900.
+
+Using the bash shell, this could look be:
+
+ TIME=$(date +%s)
+ RRDRES=900
+ rrdtool fetch subdata.rrd AVERAGE -r $RRDRES \
+ -e $(($TIME/$RRDRES*$RRDRES)) -s e-1h
+
+Or in Perl:
+
+ perl -e '$ctime = time; $rrdres = 900; \
+ system "rrdtool fetch subdata.rrd AVERAGE \
+ -r $rrdres -e @{[int($ctime/$rrdres)*$rrdres]} -s e-1h"'
+
+
+=head2 AT-STYLE TIME SPECIFICATION
+
+Apart from the traditional I<Seconds since epoch>, RRDtool does also
+understand at-style time specification. The specification is called
+"at-style" after the Unix command at(1) that has moderately complex
+ways to specify time to run your job at a certain date and time. The
+at-style specification consists of two parts: the B<TIME REFERENCE>
+specification and the B<TIME OFFSET> specification.
+
+=head2 TIME REFERENCE SPECIFICATION
+
+The time reference specification is used, well, to establish a reference
+moment in time (to which the time offset is then applied to). When present,
+it should come first, when omitted, it defaults to B<now>. On its own part,
+time reference consists of a I<time-of-day> reference (which should come
+first, if present) and a I<day> reference.
+
+The I<time-of-day> can be specified as B<HH:MM>, B<HH.MM>,
+or just B<HH>. You can suffix it with B<am> or B<pm> or use
+24-hours clock. Some special times of day are understood as well,
+including B<midnight> (00:00), B<noon> (12:00) and British
+B<teatime> (16:00).
+
+The I<day> can be specified as I<month-name> I<day-of-the-month> and
+optional a 2- or 4-digit I<year> number (e.g. March 8 1999). Alternatively,
+you can use I<day-of-week-name> (e.g. Monday), or one of the words:
+B<yesterday>, B<today>, B<tomorrow>. You can also specify the I<day> as a
+full date in several numerical formats, including B<MM/DD/[YY]YY>,
+B<DD.MM.[YY]YY>, or B<YYYYMMDD>.
+
+I<NOTE1>: this is different from the original at(1) behavior, where a
+single-number date is interpreted as MMDD[YY]YY.
+
+I<NOTE2>: if you specify the I<day> in this way, the I<time-of-day> is
+REQUIRED as well.
+
+Finally, you can use the words B<now>, B<start>, or B<end> as your time
+reference. B<Now> refers to the current moment (and is also the default
+time reference). B<Start> (B<end>) can be used to specify a time
+relative to the start (end) time for those tools that use these
+categories (B<rrdfetch>, L<rrdgraph>).
+
+Month and day of the week names can be used in their naturally
+abbreviated form (e.g., Dec for December, Sun for Sunday, etc.). The
+words B<now>, B<start>, B<end> can be abbreviated as B<n>, B<s>, B<e>.
+
+=head2 TIME OFFSET SPECIFICATION
+
+The time offset specification is used to add/subtract certain time
+intervals to/from the time reference moment. It consists of a I<sign>
+(S<B<+> or B<->>) and an I<amount>. The following time units can be
+used to specify the I<amount>: B<years>, B<months>, B<weeks>, B<days>,
+B<hours>, B<minutes>, or B<seconds>. These units can be used in
+singular or plural form, and abbreviated naturally or to a single
+letter (e.g. +3days, -1wk, -3y). Several time units can be combined
+(e.g., -5mon1w2d) or concatenated (e.g., -5h45min = -5h-45min =
+-6h+15min = -7h+1h30m-15min, etc.)
+
+I<NOTE3>: If you specify time offset in days, weeks, months, or years,
+you will end with the time offset that may vary depending on your time
+reference, because all those time units have no single well defined
+time interval value (S<1 year> contains either 365 or 366 days, S<1 month>
+is 28 to 31 days long, and even S<1 day> may be not equal to 24 hours
+twice a year, when DST-related clock adjustments take place).
+To cope with this, when you use days, weeks, months, or years
+as your time offset units your time reference date is adjusted
+accordingly without too much further effort to ensure anything
+about it (in the hope that mktime(3) will take care of this later).
+This may lead to some surprising (or even invalid!) results,
+e.g. S<'May 31 -1month'> = S<'Apr 31'> (meaningless) = S<'May 1'>
+(after mktime(3) normalization); in the EET timezone
+'3:30am Mar 29 1999 -1 day' yields '3:30am Mar 28 1999' (Sunday)
+which is an invalid time/date combination (because of 3am -> 4am DST
+forward clock adjustment, see the below example).
+
+In contrast, hours, minutes, and seconds are well defined time
+intervals, and these are guaranteed to always produce time offsets
+exactly as specified (e.g. for EET timezone, S<'8:00 Mar 27 1999 +2
+days'> = S<'8:00 Mar 29 1999'>, but since there is 1-hour DST forward
+clock adjustment that occurs around S<3:00 Mar 28 1999>, the actual
+time interval between S<8:00 Mar 27 1999> and S<8:00 Mar 29 1999>
+equals 47 hours; on the other hand, S<'8:00 Mar 27 1999 +48 hours'> =
+S<'9:00 Mar 29 1999'>, as expected)
+
+I<NOTE4>: The single-letter abbreviation for both B<months> and B<minutes>
+is B<m>. To disambiguate them, the parser tries to read your S<mind :)>
+by applying the following two heuristics:
+
+=over 3
+
+=item 1
+
+If B<m> is used in context of (i.e. right after the) years,
+months, weeks, or days it is assumed to mean B<months>, while
+in the context of hours, minutes, and seconds it means minutes.
+(e.g., in -1y6m or +3w1m B<m> is interpreted as B<months>, while in
+-3h20m or +5s2m B<m> the parser decides for B<minutes>).
+
+=item 2
+
+Out of context (i.e. right after the B<+> or B<-> sign) the
+meaning of B<m> is guessed from the number it directly follows.
+Currently, if the number's absolute value is below 25 it is assumed
+that B<m> means B<months>, otherwise it is treated as B<minutes>.
+(e.g., -25m == -25 minutes, while +24m == +24 months)
+
+=back
+
+I<Final NOTES>: Time specification is case-insensitive.
+Whitespace can be inserted freely or omitted altogether.
+There are, however, cases when whitespace is required
+(e.g., S<'midnight Thu'>). In this case you should either quote the
+whole phrase to prevent it from being taken apart by your shell or use
+'_' (underscore) or ',' (comma) which also count as whitespace
+(e.g., midnight_Thu or midnight,Thu).
+
+
+=head2 TIME SPECIFICATION EXAMPLES
+
+I<Oct 12> -- October 12 this year
+
+I<-1month> or I<-1m> -- current time of day, only a month before
+(may yield surprises, see NOTE3 above).
+
+I<noon yesterday -3hours> -- yesterday morning; can also be specified
+as I<9am-1day>.
+
+I<23:59 31.12.1999> -- 1 minute to the year 2000.
+
+I<12/31/99 11:59pm> -- 1 minute to the year 2000 for imperialists.
+
+I<12am 01/01/01> -- start of the new millennium
+
+I<end-3weeks> or I<e-3w> -- 3 weeks before end time
+(may be used as start time specification).
+
+I<start+6hours> or I<s+6h> -- 6 hours after start time
+(may be used as end time specification).
+
+I<931225537> -- 18:45 July 5th, 1999
+(yes, seconds since 1970 are valid as well).
+
+I<19970703 12:45> -- 12:45 July 3th, 1997
+(my favorite, and its even got an ISO number (8601)).
+
+=head1 AUTHOR
+
+Tobias Oetiker <tobi@oetiker.ch>
diff --git a/program/doc/rrdfirst.pod b/program/doc/rrdfirst.pod
--- /dev/null
+++ b/program/doc/rrdfirst.pod
@@ -0,0 +1,32 @@
+=head1 NAME
+
+rrdfirst - Return the date of the first data sample in an RRA within an RRD
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<first> I<filename> [I<--rraindex number>]
+
+=head1 DESCRIPTION
+
+The B<first> function returns the UNIX timestamp of the first data
+sample entered into the specified RRA of the RRD file.
+
+=over 8
+
+=item I<filename>
+
+The name of the B<RRD> that contains the data.
+
+=item I<--rraindex number>
+
+The index number of the B<RRA> that is to be examined. If not specified, the
+index defaults to zero. B<RRA> index numbers can be determined through
+B<rrdtool info>.
+
+=back
+
+=head1 AUTHOR
+
+Burton Strauss <Burton@ntopSupport.com>
+
+
diff --git a/program/doc/rrdgraph-old.pod b/program/doc/rrdgraph-old.pod
--- /dev/null
@@ -0,0 +1,664 @@
+=head1 NAME
+
+rrdtool graph - Create a graph based on data from one or several RRD
+
+=for html <div align="right"><a href="rrdgraph.pdf">PDF</a> version.</div>
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<graph> I<filename>
+S<[B<-s>|B<--start> I<seconds>]>
+S<[B<-e>|B<--end> I<seconds>]>
+S<[B<-x>|B<--x-grid> I<x-axis grid and label>]>
+S<[B<-y>|B<--y-grid> I<y-axis grid and label>]>
+S<[B<-Y>|B<--alt-y-grid>]>
+S<[B<-A>|B<--alt-autoscale>]>
+S<[B<-M>|B<--alt-autoscale-max>]>
+S<[B<-X>|B<--units-exponent>]> I<value>]>
+S<[B<-v>|B<--vertical-label> I<text>]>
+S<[B<-w>|B<--width> I<pixels>]>
+S<[B<-h>|B<--height> I<pixels>]>
+S<[B<-i>|B<--interlaced>]>
+S<[B<-f>|B<--imginfo> I<formatstring>]>
+S<[B<-a>|B<--imgformat> B<SVG>|B<PNG>]>
+S<[B<-z>|B<--lazy>]>
+S<[B<-o>|B<--logarithmic>]>
+S<[B<-u>|B<--upper-limit> I<value>]>
+S<[B<-l>|B<--lower-limit> I<value>]>
+S<[B<-g>|B<--no-legend>]>
+S<[B<-r>|B<--rigid>]>
+S<[B<-S>|B<--step> I<value>]>
+S<[B<-b>|B<--base> I<value>]>
+S<[B<-c>|B<--color> I<COLORTAG>B<#>I<rrggbb>]>
+S<[B<-t>|B<--title> I<title>]>
+S<[B<DEF:>I<vname>B<=>I<rrd>B<:>I<ds-name>B<:>I<CF>]>
+S<[B<CDEF:>I<vname>B<=>I<rpn-expression>]>
+S<[B<PRINT:>I<vname>B<:>I<CF>B<:>I<format>]>
+S<[B<GPRINT:>I<vname>B<:>I<CF>B<:>I<format>]>
+S<[B<COMMENT:>I<text>]>
+S<[B<HRULE:>I<value>B<#>I<rrggbb>[B<:>I<legend>]]>
+S<[B<VRULE:>I<time>B<#>I<rrggbb>[B<:>I<legend>]]>
+S<[B<LINE>{B<1>|B<2>|B<3>}B<:>I<vname>[B<#>I<rrggbb>[B<:>I<legend>]]]>
+S<[B<AREA:>I<vname>[B<#>I<rrggbb>[B<:>I<legend>]]]>
+S<[B<STACK:>I<vname>[B<#>I<rrggbb>[B<:>I<legend>]]]>
+S<[B<TICK:>I<vname>B<#>I<rrggbb>[B<:>I<axis-fraction>[B<:>I<legend>]]]>
+
+=head1 DESCRIPTION
+
+The B<graph> functions main purpose is to create graphical
+representations of the data stored in one or several B<RRD>s. Apart
+from generating graphs, it can also extract numerical reports.
+
+=over
+
+=item I<filename>
+
+The name of the graph to generate. Since B<RRDtool> outputs
+SVGs and PNGs, it's recommended that the filename end in either
+F<.svg> or F<.png>. B<RRDtool> does not enforce this, however.
+If the I<filename> is set to '-' the image file will be written
+to standard out. All other output will get suppressed.
+
+If no graph functions are called, the graph will not be created.
+
+=item B<-s>|B<--start> I<seconds> (default end-1day)
+
+The time when the graph should begin. Time in seconds since
+epoch (1970-01-01) is required. Negative numbers are relative to the
+current time. By default one day worth of data will be graphed.
+See also AT-STYLE TIME SPECIFICATION section in the I<rrdfetch>
+documentation for a detailed explanation on how to specify time.
+
+=item B<-e>|B<--end> I<seconds> (default now)
+
+The time when the graph should end. Time in seconds since epoch.
+See also AT-STYLE TIME SPECIFICATION section in the I<rrdfetch>
+documentation for a detailed explanation of ways to specify time.
+
+=item B<-x>|B<--x-grid> I<x-axis grid and label> (default autoconfigure)
+
+The x-axis label is quite complex to configure. So if you don't have
+very special needs, you can rely on the autoconfiguration to get this
+right.
+
+If you want no x-grid at all, use the magic setting B<none>.
+
+The x-axis label and grid can be configured, using the following format:
+
+I<GTM>B<:>I<GST>B<:>I<MTM>B<:>I<MST>B<:>I<LTM>:I<LST>B<:>I<LPR>B<:>I<LFM>
+
+You have to configure three elements making up the x-axis labels and
+grid. The base grid (I<G??>), the major grid (I<M??>) and the labels
+(I<L??>). The configuration is based on the idea that you first
+specify a well known amount of time (I<?TM>) and then say how many
+times it has to pass between each grid line or label (I<?ST>). For the
+label you have to define two additional items: The precision of the
+label in seconds (I<LPR>) and the strftime format used to generate the
+text of the label (I<LFM>).
+
+The I<?TM> elements must be one of the following keywords: B<SECOND>,
+B<MINUTE>, B<HOUR>, B<DAY>, B<WEEK>, B<MONTH> or B<YEAR>.
+
+If you wanted a graph with a base grid every 10 minutes and a major
+one every hour, with labels every hour you would use the following
+x-axis definition.
+
+C<MINUTE:10:HOUR:1:HOUR:1:0:%X>
+
+The precision in this example is 0 because the %X format is exact. If
+the label was the name of the day, we would have had a precision of 24
+hours, because when you say something like 'Monday' you mean the whole
+day and not Monday morning 00:00. Thus the label should be positioned
+at noon. By defining a precision of 24 hours or rather 86400 seconds,
+you make sure that this happens.
+
+=item B<-y>|B<--y-grid> I<grid step>:I<label factor> (default autoconfigure)
+
+Makes vertical grid lines appear at I<grid step> interval. Every
+I<label factor> gridstep, a major grid line is printed, along with
+label showing the value of the grid line.
+
+If you want no y-grid at all set specify the magic word B<none>.
+
+=item B<--alt-y-grid>
+
+Place Y grid dynamically based on graph Y range. Algorithm ensures
+that you always have grid, that there are enough but not too many
+grid lines and the grid is metric. That is grid lines are placed
+every 1, 2, 5 or 10 units. (contributed by Sasha Mikheev)
+
+
+=item B<--alt-autoscale>
+
+Compute Y range based on function absolute minimum and
+maximum values. Default algorithm uses predefined set of ranges.
+This is good in many cases but it fails miserably when you need
+to graph something like 260 + 0.001 * sin(x). Default algorithm
+will use Y range from 250 to 300 and on the graph you will see
+almost straight line. With --alt-autoscale Y range will be
+from slightly less the 260 - 0.001 to slightly more then 260 + 0.001
+and periodic behavior will be seen. (contributed by Sasha Mikheev)
+
+=item B<--alt-autoscale-max>
+
+Where --alt-autoscale will modify both the absolute maximum AND minimum
+values, this option will only affect the maximum value. The minimum
+value, if not defined on the command line, will be 0. This option can
+be useful when graphing router traffic when the WAN line uses compression,
+and thus the throughput may be higher than the WAN line speed.
+
+=item B<--units-exponent> I<value> (default autoconfigure)
+
+This sets the 10**exponent scaling of the y-axis values. Normally
+values will be scaled to the appropriate units (k, M, etc.). However
+you may wish to display units always in k (Kilo, 10e3) even if the data
+is in the M (Mega, 10e6) range for instance. Value should be an
+integer which is a multiple of 3 between -18 and 18 inclusive. It is
+the exponent on the units you which to use. For example, use 3 to
+display the y-axis values in k (Kilo, 10e3, thousands), use -6 to
+display the y-axis values in u (Micro, 10e-6, millionths). Use a value
+of 0 to prevent any scaling of the y-axis values.
+
+=item B<-v>|B<--vertical-label> I<text>
+
+vertical label on the left side of the graph. This is normally used to
+specify the units used.
+
+=item B<-w>|B<--width> I<pixels> (default 400 pixel)
+
+Width of the drawing area within the graph. This affects the size of the
+image.
+
+=item B<-h>|B<--height> I<pixels> (default 100 pixel)
+
+Width of the drawing area within the graph. This affects the size of the
+image.
+
+=item B<-i>|B<--interlaced> (default: false)
+
+If you set this option, then the resulting image will be interlaced.
+Most web browsers display these incrementally as they load. If
+you do not use this option, the image defaults to being progressive
+scanned. The only effect of this option is to control the format
+of the image on disk. It makes no changes to the layout or contents
+of the graph.
+
+=item B<-f>|B<--imginfo> I<formatstring>
+
+After the image has been created, the graph function uses printf
+together with this format string to create output similar to the PRINT
+function, only that the printf is supplied with the parameters
+I<filename>, I<xsize> and I<ysize>. In order to generate an B<IMG> tag
+suitable for including the graph into a web page, the command line
+would look like this:
+
+ --imginfo '<IMG SRC="/img/%s" WIDTH="%lu" HEIGHT="%lu" ALT="Demo">'
+
+=item B<-a>|B<--imgformat> B<SVG>|B<PNG> (default: PNG)
+
+Allows you to produce PNG output from RRDtool.
+
+=item B<-z>|B<--lazy> (default: false)
+
+Only generate the graph, if the current image is out of date or not
+existent.
+
+=item B<-u>|B<--upper-limit> I<value> (default autoconfigure)
+
+Defines the value normally located at the upper border of the
+graph. If the graph contains higher values, the upper border will
+move upward to accommodate these values as well.
+
+If you want to define an upper-limit which will not move in any
+event you have to set the B<--rigid> option as well.
+
+=item B<-l>|B<--lower-limit> I<value> (default autoconfigure)
+
+This is not the lower limit of a graph. But rather, this is the
+maximum lower bound of a graph. For example, the value -100 will
+result in a graph that has a lower limit of -100 or less. Use this
+keyword to expand graphs down.
+
+=item B<-r>|B<--rigid>
+
+rigid boundaries mode. Normally rrdgraph will automatically expand the
+lower and upper limit if the graph contains a value outside the valid
+range. With the r option you can disable this behavior
+
+=item B<-b>|B<--base> I<value>
+
+if you are graphing memory (and NOT network traffic) this switch
+should be set to 1024 so that one Kb is 1024 byte. For traffic
+measurement, 1 kb/s is 1000 b/s.
+
+=item B<-o>|B<--logarithmic>
+
+logarithmic y-axis scaling
+
+=item B<-c>|B<--color> I<COLORTAG>B<#>I<rrggbb> (default colors)
+
+override the colors for the standard elements of the graph. The I<COLORTAG>
+must be one of the following symbolic names: B<BACK> ground, B<CANVAS>,
+B<SHADEA> left/top border, B<SHADEB> right/bottom border, B<GRID>, B<MGRID>
+major grid, B<FONT>, B<FRAME> and axis of the graph or B<ARROW>. This option
+can be called multiple times to set several colors.
+
+=item B<-g>|B<--no-legend>
+
+Suppress generation of legend; only render the graph.
+
+=item B<-t>|B<--title> I<text> (default no title)
+
+Define a title to be written into the graph
+
+=item B<--step> I<value> (default automatic)
+
+By default rrdgraph calculates the width of one pixel in the time domain and
+tries to get data at that resolution from the RRD. With this switch you can
+override this behaviour. If you want rrdgraph to get data at 1 hour
+resolution from the RRD, then you can set the step to 3600 seconds. Note,
+that a step smaller than 1 pixel will be silently ignored.
+
+=item B<DEF:>I<vname>B<=>I<rrd>B<:>I<ds-name>B<:>I<CF>
+
+Define virtual name for a data source. This name can then be used
+in the functions explained below. The
+DEF call automatically chooses an B<RRA> which contains I<CF> consolidated data in a
+resolution appropriate for the size of the graph to be drawn. Ideally
+this means that one data point from the B<RRA> should be represented
+by one pixel in the graph. If the resolution of the B<RRA> is higher
+than the resolution of the graph, the data in the RRA will be further
+consolidated according to the consolidation function (I<CF>) chosen.
+
+=item B<CDEF:>I<vname>B<=>I<rpn-expression>
+
+Create a new virtual data source by evaluating a mathematical expression,
+specified in Reverse Polish Notation (RPN). If you have ever used a traditional
+HP calculator you already know RPN. The idea behind RPN notation is,
+that you have a stack and push your data onto this stack. When ever
+you execute an operation, it takes as many data values from the stack
+as needed. The pushing of data is implicit, so when ever you specify a number
+or a variable, it gets pushed automatically.
+
+If this is all a big load of incomprehensible words for you, maybe an
+example helps (a more complete explanation is given in [1]): The
+expression I<vname+3/2> becomes C<vname,3,2,/,+> in RPN. First the three
+values get pushed onto the stack (which now contains (the current
+value of) vname, a 3 and a 2). Then the / operator pops two values
+from the stack (3 and 2), divides the first argument by the second
+(3/2) and pushes the result (1.5) back onto the stack. Then the +
+operator pops two values (vname and 1.5) from the stack; both values
+are added up and the result gets pushes back onto the stack. In the
+end there is only one value left on the stack: The result of the
+expression.
+
+The I<rpn-expression> in the B<CDEF> function takes both, constant values
+as well as I<vname> variables. The following operators can be used on these
+values:
+
+=over
+
+=item +, -, *, /, %
+
+pops two values from the stack applies the selected operator and pushes
+the result back onto the stack. The % operator stands for the modulo
+operation.
+
+=item SIN, COS, LOG, EXP, FLOOR, CEIL
+
+pops one value from the stack, applies the selected function and pushes
+the result back onto the stack.
+
+=item LT, LE, GT, GE, EQ
+
+pops two values from the stack, compares them according to the selected
+condition and pushes either 1 back onto the stack if the condition is true
+and 0 if the condition was not true.
+
+=item IF
+
+pops three values from the stack. If the last value is not 0, the
+second value will be pushed back onto the stack, otherwise the
+first value is pushed back.
+
+If the stack contains the values A, B, C, D, E are presently on the
+stack, the IF operator will pop the values E D and C of the stack. It will
+look at C and if it is not 0 it will push D back onto the stack, otherwise
+E will be sent back to the stack.
+
+=item MIN, MAX
+
+selects the lesser or larger of the two top stack values respectively
+
+=item LIMIT
+
+replaces the value with I<*UNKNOWN*> if it is outside the limits specified
+by the two values above it on the stack.
+
+ CDEF:a=alpha,0,100,LIMIT
+
+=item DUP, EXC, POP
+
+These manipulate the stack directly. DUP will duplicate the top of the
+stack, pushing the result back onto the stack. EXC will exchange the top
+two elements of the stack, and POP will pop off the top element of the
+stack. Having insufficient elements on the stack for these operations is
+an error.
+
+=item UN
+
+Pops one value off the stack, if it is I<*UNKNOWN*>, 1 will be pushed
+back otherwise 0.
+
+=item UNKN
+
+Push an I<*UNKNOWN*> value onto the stack.
+
+=item PREV
+
+Push I<*UNKNOWN*> if its at the first value of a data set or otherwise
+the value of this CDEF at the previous time step. This allows you to
+perform calculations across the data.
+
+=item COUNT
+
+Pushes the number 1 if it is at the first value of the data set, the
+number 2 if it is at the second, and so on. This special value, allows
+you to make calculations based on the position of the value within
+the data set.
+
+=item INF, NEGINF
+
+Push a positive or negative infinite (oo) value onto the stack. When
+drawing an infinite number it appears right at the top or bottom edge of the
+graph, depending whether you have a positive or negative infinite number.
+
+=item NOW
+
+Push the current (real world) time onto the stack.
+
+=item TIME
+
+Push the time the current sample was taken onto the stack. This is the
+number of non-skip seconds since 0:00:00 January 1, 1970.
+
+=item LTIME
+
+This is like TIME B<+ current timezone offset in seconds>. The current
+offset takes daylight saving time into account, given your OS supports
+this. If you were looking at a sample, in Zurich, in summer, the
+offset would be 2*3600 seconds, as Zurich at that time of year is 2
+hours ahead of UTC.
+
+Note that the timezone offset is always calculated for the time the
+current sample was taken at. It has nothing to do with the time you are
+doing the calculation.
+
+=back
+
+Please note that you may only use I<vname> variables that you
+previously defined by either B<DEF> or B<CDEF>. Furthermore, as of
+this writing (version 0.99.25), you must use at least one I<vname>
+per expression, that is "CDEF:fourtytwo=2,40,+" will yield an error
+message but not a I<vname> fourtytwo that's always equal to 42.
+
+=item B<PRINT:>I<vname>B<:>I<CF>B<:>I<format>
+
+Calculate the chosen consolidation function I<CF> over the data-source
+variable I<vname> and C<printf> the result to stdout using I<format>.
+In the I<format> string there should be a '%lf', '%le' or'%lg' marker in the
+place where the number should be printed.
+
+If an additional '%s' is found AFTER the marker, the value will be scaled
+and an appropriate SI magnitude unit will be printed in place of the '%s'
+marker. The scaling will take the '--base' argument into consideration!
+
+If a '%S' is used instead of a '%s', then instead of calculating the
+appropriate SI magnitude unit for this value, the previously calculated
+SI magnitude unit will be used. This is useful if you want all the values
+in a PRINT statement to have the same SI magnitude unit. If there was
+no previous SI magnitude calculation made, then '%S' behaves like a '%s',
+unless the value is 0, in which case it does not remember a SI magnitude
+unit and a SI magnitude unit will only be calculated when the next '%s' is
+seen or the next '%S' for a non-zero value.
+
+If you want to put a '%' into your PRINT string, use '%%' instead.
+
+=item B<GPRINT:>I<vname>B<:>I<CF>B<:>I<format>
+
+Same as B<PRINT> but the result is printed into the graph below the legend.
+
+=back
+
+B<Caveat:> When using the B<PRINT> and B<GRPRINT> functions to
+calculate data summaries over time periods bounded by the current
+time, it is important to note that the last sample will almost always
+yield a value of UNKNOWN as it lies after the last update time. This
+can result in slight data skewing, particularly with the B<AVERAGE>
+function. In order to avoid this, make sure that your end time is at
+least one heartbeat prior to the current time.
+
+=over
+
+
+=item B<COMMENT:>I<text>
+
+Like B<GPRINT> but the I<text> is simply printed into the graph.
+
+=item B<HRULE:>I<value>B<#>I<rrggbb>[B<:>I<legend>]
+
+Draw a horizontal rule into the graph and optionally add a legend
+
+=item B<VRULE:>I<time>B<#>I<rrggbb>[B<:>I<legend>]
+
+Draw a vertical rule into the graph and optionally add a legend
+
+=item B<LINE>{B<1>|B<2>|B<3>}B<:>I<vname>[B<#>I<rrggbb>[B<:>I<legend>]]
+
+Plot for the requested data, using the color specified. Write a legend
+into the graph. The 3 possible keywords B<LINE1>, B<LINE2>, and B<LINE3>
+generate increasingly wide lines. If no color is defined,
+the drawing is done 'blind' this is useful in connection with the
+B<STACK> function when you want to ADD the values of two
+data-sources without showing it in the graph.
+
+=item B<AREA>:I<vname>[B<#>I<rrggbb>[B<:>I<legend>]]
+
+Does the same as B<LINE?>, but the area between 0 and
+the graph will be filled with the color specified.
+
+=item B<STACK>:I<vname>[B<#>I<rrggbb>[B<:>I<legend>]]
+
+Does the same as B<LINE?>, but the graph gets stacked on top of the previous
+B<LINE?>, B<AREA> or B<STACK> graph. Depending on the type of the
+previous graph, the B<STACK> will be either a B<LINE?> or an B<AREA>.
+This obviously implies that the first B<STACK> must be preceded by an
+B<AREA> or B<LINE?> -- you need something to stack something onto in
+the first place ;)
+
+Note, that when you STACK onto *UNKNOWN* data, RRDtool will not draw
+any graphics ... *UNKNOWN* is not zero ... if you want it to be zero
+then you might want to use a CDEF argument with IF and UN functions to
+turn *UNKNOWN* into zero ...
+
+=item B<TICK:>I<vname>B<#>I<rrggbb>[B<:>I<axis-fraction>[B<:>I<legend>]]
+
+Plot a tick mark (a vertical line) for each value of I<vname> that is
+non-zero and not *UNKNOWN*. The I<axis-fraction> argument specifies the
+length of the tick mark as a fraction of the y-axis; the default value
+is 0.1 (10% of the axis). Note that the color specification is not
+optional.
+
+=back
+
+=head1 NOTES on legend arguments
+
+=head2 Escaping the colon
+
+In a ':' in a I<legend> argument will mark the end of the legend. To
+enter a ':' into a legend, the colon must be escaped with a backslash '\:'.
+Beware, that many environments look for backslashes themselves, so it may
+be necessary to write two backslashes so that one is passed onto rrd_graph.
+
+=head2 String Formatting
+
+The text printed below the actual graph can be formated by appending special
+escaped characters at the end of a text. When ever such a character occurs,
+all pending text is pushed onto the graph according to the character
+specified.
+
+Valid markers are: B<\j> for justified, B<\l> for left aligned, B<\r> for
+right aligned and B<\c> for centered. In the next section there is an
+example showing how to use centered formating.
+
+Normally there are two space characters inserted between every two items
+printed into the graph. The space following a string can be suppressed by
+putting a B<\g> at the end of the string. The B<\g> also ignores any space
+inside the string if it is at the very end of the string. This can be used
+in connection with B<%s> to suppress empty unit strings.
+
+ GPRINT:a:MAX:%lf%s\g
+
+A special case is COMMENT:B<\s> this inserts some additional vertical space
+before placing the next row of legends.
+
+=head1 NOTE on Return Values
+
+Whenever rrd_graph gets called, it prints a line telling the size of
+the image it has just created to stdout. This line looks like this: XSIZExYSIZE.
+
+=head1 EXAMPLE 1
+
+ rrdtool graph demo.png --title="Demo Graph" \
+ DEF:cel=demo.rrd:exhaust:AVERAGE \
+ "CDEF:far=cel,1.8,*,32,+"" \
+ LINE2:cel#00a000:"D. Celsius" \
+ LINE2:far#ff0000:"D. Fahrenheit\c"
+
+=head1 EXAMPLE 2
+
+This example demonstrates the syntax for using IF and UN to set
+I<*UNKNOWN*> values to 0. This technique is useful if you are
+aggregating interface data where the start dates of the data sets
+doesn't match.
+
+ rrdtool graph demo.png --title="Demo Graph" \
+ DEF:idat1=interface1.rrd:ds0:AVERAGE \
+ DEF:idat2=interface2.rrd:ds0:AVERAGE \
+ DEF:odat1=interface1.rrd:ds1:AVERAGE \
+ DEF:odat2=interface2.rrd:ds1:AVERAGE \
+ CDEF:agginput=idat1,UN,0,idat1,IF,idat2,UN,0,idat2,IF,+,8,* \
+ CDEF:aggoutput=odat1,UN,0,odat1,IF,odat2,UN,0,odat2,IF,+,8,* \
+ AREA:agginput#00cc00:Input Aggregate \
+ LINE1:agginput#0000FF:Output Aggregate
+
+Assuming that idat1 has a data value of I<*UNKNOWN*>, the CDEF expression
+
+ idat1,UN,0,idat1,IF
+
+leaves us with a stack with contents of 1,0,NaN and the IF function
+will pop off the 3 values and replace them with 0. If idat1 had a
+real value like 7942099, then the stack would have 0,0,7942099 and the
+real value would be the replacement.
+
+=head1 EXAMPLE 3
+
+This example shows two ways to use the INF function. First it makes
+the background change color during half of the hours. Then, it uses
+AREA and STACK to draw a picture. If one of the inputs was UNKNOWN,
+all inputs are overlaid with another AREA.
+
+ rrdtool graph example.png --title="INF demo" \
+ DEF:val1=some.rrd:ds0:AVERAGE \
+ DEF:val2=some.rrd:ds1:AVERAGE \
+ DEF:val3=some.rrd:ds2:AVERAGE \
+ DEF:val4=other.rrd:ds0:AVERAGE \
+ CDEF:background=val4,POP,TIME,7200,%,3600,LE,INF,UNKN,IF \
+ CDEF:wipeout=val1,val2,val3,val4,+,+,+,UN,INF,UNKN,IF \
+ AREA:background#F0F0F0 \
+ AREA:val1#0000FF:Value1 \
+ STACK:val2#00C000:Value2 \
+ STACK:val3#FFFF00:Value3 \
+ STACK:val4#FFC000:Value4 \
+ AREA:wipeout#FF0000:Unknown
+
+The first CDEF uses val4 as a dummy value. It's value is removed immediately
+from the stack. Then a decision is made based on the time that a sample was
+taken. If it is an even hour (UTC time !) then the area will be filled. If
+it is not, the value is set to UNKN and is not plotted.
+
+The second CDEF looks if any of val1,val2,val3,val4 is unknown. It does so by
+checking the outcome of sum(val1,val2,val3,val4). Again, INF is returned when
+the condition is true, UNKN is used to not plot the data.
+
+The different items are plotted in a particular order. First do the background, then use a
+normal area to overlay it with data. Stack the other data until they are all plotted. Last but
+not least, overlay everything with eye-hurting red
+to signal any unknown data.
+
+Note that this example assumes that your data is in the positive half of the y-axis
+otherwise you would would have to add NEGINF in order to extend the coverage
+of the area to whole graph.
+
+=head1 EXAMPLE 4
+
+If the specialized function B<RRAs> exist for aberrant behavior detection, they
+can be used to generate the graph of a time series with confidence bands and
+failures.
+
+ rrdtool graph example.png \
+ DEF:obs=monitor.rrd:ifOutOctets:AVERAGE \
+ DEF:pred=monitor.rrd:ifOutOctets:HWPREDICT \
+ DEF:dev=monitor.rrd:ifOutOctets:DEVPREDICT \
+ DEF:fail=monitor.rrd:ifOutOctets:FAILURES \
+ TICK:fail#ffffa0:1.0:"Failures\: Average bits out" \
+ CDEF:scaledobs=obs,8,* \
+ CDEF:upper=pred,dev,2,*,+ \
+ CDEF:lower=pred,dev,2,*,- \
+ CDEF:scaledupper=upper,8,* \
+ CDEF:scaledlower=lower,8,* \
+ LINE2:scaledobs#0000ff:"Average bits out" \
+ LINE1:scaledupper#ff0000:"Upper Confidence Bound: Average bits out" \
+ LINE1:scaledlower#ff0000:"Lower Confidence Bound: Average bits out"
+
+This example generates a graph of the data series in blue (LINE2 with the scaledobs
+virtual data source), confidence bounds in red (scaledupper and scaledlower virtual
+data sources), and potential failures (i.e. potential aberrant aberrant behavior)
+marked by vertical yellow lines (the fail data source).
+
+The raw data comes from an AVERAGE B<RRA>, the finest resolution of the observed
+time series (one consolidated data point per primary data point). The predicted
+(or smoothed) values are stored in the HWPREDICT B<RRA>. The predicted deviations
+(think standard deviation) values are stored in the DEVPREDICT B<RRA>. Finally,
+the FAILURES B<RRA> contains indicators, with 1 denoting a potential failure.
+
+All of the data is rescaled to bits (instead of Octets) by multiplying by 8.
+The confidence bounds are computed by an offset of 2 deviations both above
+and below the predicted values (the CDEFs upper and lower). Vertical lines
+indicated potential failures are graphed via the TICK graph element, which
+converts non-zero values in an B<RRA> into tick marks. Here an axis-fraction
+argument of 1.0 means the tick marks span the entire y-axis, and hence become
+vertical lines on the graph.
+
+The choice of 2 deviations (a scaling factor) matches the default used internally
+by the FAILURES B<RRA>. If the internal value is changed (see L<rrdtune>), this
+graphing command should be changed to be consistent.
+
+=head2 A note on data reduction:
+
+The B<rrdtool> I<graph> command is designed to plot data at a specified temporal
+resolution, regardless of the actually resolution of the data in the RRD file.
+This can present a problem for the specialized consolidation functions which
+maintain a one-to-one mapping between primary data points and consolidated
+data points. If a graph insists on viewing the contents of these B<RRAs> on a
+coarser temporal scale, the I<graph> command tries to do something intelligent,
+but the confidence bands and failures no longer have the same meaning and may
+be misleading.
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
+=head1 REFERENCES
+
+[1] http://www.dotpoint.com/xnumber/rpn_or_adl.htm
diff --git a/program/doc/rrdgraph.pod b/program/doc/rrdgraph.pod
--- /dev/null
+++ b/program/doc/rrdgraph.pod
@@ -0,0 +1,398 @@
+=head1 NAME
+
+rrdgraph - Round Robin Database tool grapher functions
+
+=head1 SYNOPSIS
+
+B<rrdtool graph> I<filename>
+[I<L<option|rrdgraph/OPTIONS>> ...]
+[I<L<data definition|rrdgraph_data/DEF>> ...]
+[I<L<data calculation|rrdgraph_data/CDEF>> ...]
+[I<L<variable definition|rrdgraph_data/VDEF>> ...]
+[I<L<graph element|rrdgraph_graph/GRAPH>> ...]
+[I<L<print element|rrdgraph_graph/PRINT>> ...]
+
+=head1 DESCRIPTION
+
+The B<graph> function of B<RRDtool> is used to present the
+data from an B<RRD> to a human viewer. Its main purpose is to
+create a nice graphical representation, but it can also generate
+a numerical report.
+
+=head1 OVERVIEW
+
+B<rrdtool graph> needs data to work with, so you must use one or more
+B<L<data definition|rrdgraph_data/DEF>> statements to collect this
+data. You are not limited to one database, it's perfectly legal to
+collect data from two or more databases (one per statement, though).
+
+If you want to display averages, maxima, percentiles, etcetera
+it is best to collect them now using the
+B<L<variable definition|rrdgraph_data/VDEF>> statement.
+Currently this makes no difference, but in a future version
+of rrdtool you may want to collect these values before consolidation.
+
+The data fetched from the B<RRA> is then B<consolidated> so that
+there is exactly one datapoint per pixel in the graph. If you do
+not take care yourself, B<RRDtool> will expand the range slightly
+if necessary. Note, in that case the first and/or last pixel may very
+well become unknown!
+
+Sometimes data is not exactly in the format you would like to display
+it. For instance, you might be collecting B<bytes> per second, but
+want to display B<bits> per second. This is what the B<L<data
+calculation|rrdgraph_data/CDEF>> command is designed for. After
+B<consolidating> the data, a copy is made and this copy is modified
+using a rather powerful B<L<RPN|rrdgraph_rpn/>> command set.
+
+When you are done fetching and processing the data, it is time to
+graph it (or print it). This ends the B<rrdtool graph> sequence.
+
+=head1 OPTIONS
+
+=over 4
+
+=item filename
+
+The name and path of the graph to generate. It is recommended to
+end this in C<.png>, C<.svg> or C<.eps>, but B<RRDtool> does not enforce this.
+
+I<filename> can be 'C<->' to send the image to C<stdout>. In
+this case, no other output is generated.
+
+=item Time range
+
+[B<-s>|B<--start> I<time>]
+[B<-e>|B<--end> I<time>]
+[B<-S>|B<--step> I<seconds>]
+
+The start and end of the time series you would like to display, and which
+B<RRA> the data should come from. Defaults are: 1 day ago until
+now, with the best possible resolution. B<Start> and B<end> can
+be specified in several formats, see
+L<AT-STYLE TIME SPECIFICATION|rrdfetch/> and L<rrdgraph_examples>.
+By default, B<rrdtool graph> calculates the width of one pixel in
+the time domain and tries to get data from an B<RRA> with that
+resolution. With the B<step> option you can alter this behaviour.
+If you want B<rrdtool graph> to get data at a one-hour resolution
+from the B<RRD>, set B<step> to 3'600. Note: a step smaller than
+one pixel will silently be ignored.
+
+=item Labels
+
+[B<-t>|B<--title> I<string>]
+[B<-v>|B<--vertical-label> I<string>]
+
+A horizontal string at the top of the graph and/or a vertically
+placed string at the left hand side of the graph.
+
+=item Size
+
+[B<-w>|B<--width> I<pixels>]
+[B<-h>|B<--height> I<pixels>]
+[B<-j>|B<--only-graph>]
+
+The width and height of the B<canvas> (the part of the graph with
+the actual data and such). This defaults to 400 pixels by 100 pixels.
+
+If you specify the B<--only-graph> option and set the height E<lt> 32
+pixels you will get a tiny graph image (thumbnail) to use as an icon
+for use in an overview, for example. All labeling will be stripped off
+the graph.
+
+=item Limits
+
+[B<-u>|B<--upper-limit> I<value>]
+[B<-l>|B<--lower-limit> I<value>]
+[B<-r>|B<--rigid>]
+
+By default the graph will be autoscaling so that it will adjust the
+y-axis to the range of the data. You can change this behaviour by
+explicitly setting the limits. The displayed y-axis will then range at
+least from B<lower-limit> to B<upper-limit>. Autoscaling will still
+permit those boundaries to be stretched unless the B<rigid> option is
+set.
+
+[B<-A>|B<--alt-autoscale>]
+
+Sometimes the default algorithm for selecting the y-axis scale is not
+satisfactory. Normally the scale is selected from a predefined
+set of ranges and this fails miserably when you need to graph something
+like C<260 + 0.001 * sin(x)>. This option calculates the minimum and
+maximum y-axis from the actual minimum and maximum data values. Our example
+would display slightly less than C<260-0.001> to slightly more than
+C<260+0.001> (this feature was contributed by Sasha Mikheev).
+
+[B<-J>|B<--alt-autoscale-min>]
+
+Where C<--alt-autoscale> will modify both the absolute maximum AND minimum
+values, this option will only affect the minimum value. The maximum
+value, if not defined on the command line, will be 0. This option can
+be useful when graphing router traffic when the WAN line uses compression,
+and thus the throughput may be higher than the WAN line speed.
+
+[B<-M>|B<--alt-autoscale-max>]
+
+Where C<--alt-autoscale> will modify both the absolute maximum AND minimum
+values, this option will only affect the maximum value. The minimum
+value, if not defined on the command line, will be 0. This option can
+be useful when graphing router traffic when the WAN line uses compression,
+and thus the throughput may be higher than the WAN line speed.
+
+[B<-N>|B<--no-gridfit>]
+
+In order to avoid anti-aliasing effects gridlines are placed on
+integer pixel values. This is by default done by extending
+the scale so that gridlines happens to be spaced using an
+integer number of pixels and also start on an integer pixel value.
+This might extend the scale too much for some logarithmic scales
+and for linear scales where B<--alt-autoscale> is needed.
+Using B<--no-gridfit> disables modification of the scale.
+
+=item Grid
+
+=over 4
+
+=item X-Axis
+
+[B<-x>|B<--x-grid> I<GTM>B<:>I<GST>B<:>I<MTM>B<:>I<MST>B<:>I<LTM>B<:>I<LST>B<:>I<LPR>B<:>I<LFM>]
+
+[B<-x>|B<--x-grid> B<none>]
+
+The x-axis label is quite complex to configure. If you don't have
+very special needs it is probably best to rely on the autoconfiguration
+to get this right. You can specify the string C<none> to suppress the grid
+and labels altogether.
+
+The grid is defined by specifying a certain amount of time in the I<?TM>
+positions. You can choose from C<SECOND>, C<MINUTE>, C<HOUR>, C<DAY>,
+C<WEEK>, C<MONTH> or C<YEAR>. Then you define how many of these should
+pass between each line or label. This pair (I<?TM:?ST>) needs to be
+specified for the base grid (I<G??>), the major grid (I<M??>) and the
+labels (I<L??>). For the labels you also must define a precision
+in I<LPR> and a I<strftime> format string in I<LFM>. I<LPR> defines
+where each label will be placed. If it is zero, the label will be
+placed right under the corresponding line (useful for hours, dates
+etcetera). If you specify a number of seconds here the label is
+centered on this interval (useful for Monday, January etcetera).
+
+ --x-grid MINUTE:10:HOUR:1:HOUR:4:0:%X
+
+This places grid lines every 10 minutes, major grid lines every hour,
+and labels every 4 hours. The labels are placed under the major grid
+lines as they specify exactly that time.
+
+ --x-grid HOUR:8:DAY:1:DAY:1:0:%A
+
+This places grid lines every 8 hours, major grid lines and labels
+each day. The labels are placed exactly between two major grid lines
+as they specify the complete day and not just midnight.
+
+=item Y-Axis
+
+[B<-y>|B<--y-grid> I<grid step>B<:>I<label factor>]
+
+[B<-y>|B<--y-grid> B<none>]
+
+Y-axis grid lines appear at each I<grid step> interval. Labels are
+placed every I<label factor> lines. You can specify C<-y none> to
+suppress the grid and labels altogether. The default for this option is
+to automatically select sensible values.
+
+If you have set --y-grid to 'none' not only the labels get supressed, also
+the space reserved for the labels is removed. You can still add space
+manually if you use the --units-length command to explicitly reserve space.
+
+[B<-Y>|B<--alt-y-grid>]
+
+Place the Y grid dynamically based on the graph's Y range. The algorithm
+ensures that you always have a grid, that there are enough but not too many
+grid lines, and that the grid is metric. That is the grid lines are placed
+every 1, 2, 5 or 10 units. This parameter will also ensure that you get
+enough decimals displayed even if your graph goes from 69.998 to 70.001.
+(contributed by Sasha Mikheev).
+
+[B<-o>|B<--logarithmic>]
+
+Logarithmic y-axis scaling.
+
+[B<-X>|B<--units-exponent> I<value>]
+
+This sets the 10**exponent scaling of the y-axis values. Normally,
+values will be scaled to the appropriate units (k, M, etc.). However,
+you may wish to display units always in k (Kilo, 10e3) even if the data
+is in the M (Mega, 10e6) range, for instance. Value should be an
+integer which is a multiple of 3 between -18 and 18 inclusively. It is
+the exponent on the units you wish to use. For example, use 3 to
+display the y-axis values in k (Kilo, 10e3, thousands), use -6 to
+display the y-axis values in u (Micro, 10e-6, millionths). Use a value
+of 0 to prevent any scaling of the y-axis values.
+
+This option is very effective at confusing the heck out of the default
+rrdtool autoscaler and grid painter. If rrdtool detects that it is not
+successful in labeling the graph under the given circumstances, it will switch
+to the more robust B<--alt-y-grid> mode.
+
+[B<-L>|B<--units-length> I<value>]
+
+How many digits should rrdtool assume the y-axis labels to be? You
+may have to use this option to make enough space once you start
+fideling with the y-axis labeling.
+
+[B<--units=si>]
+
+With this option y-axis values on logarithmic graphs will be scaled to
+the appropriate units (k, M, etc.) instead of using exponential notation.
+Note that for linear graphs, SI notation is used by default.
+
+=back
+
+=item Miscellaneous
+
+[B<-z>|B<--lazy>]
+
+Only generate the graph if the current graph is out of date or not
+existent.
+
+[B<-f>|B<--imginfo> I<printfstr>]
+
+After the image has been created, the graph function uses printf
+together with this format string to create output similar to the PRINT
+function, only that the printf function is supplied with the parameters
+I<filename>, I<xsize> and I<ysize>. In order to generate an B<IMG> tag
+suitable for including the graph into a web page, the command line
+would look like this:
+
+ --imginfo '<IMG SRC="/img/%s" WIDTH="%lu" HEIGHT="%lu" ALT="Demo">'
+
+[B<-c>|B<--color> I<COLORTAG>#I<rrggbb>[I<aa>]]
+
+Override the default colors for the standard elements of the graph. The
+I<COLORTAG> is one of C<BACK> background, C<CANVAS> for the background of
+the actual graph, C<SHADEA> for the left and top border, C<SHADEB> for the
+right and bottom border, C<GRID>, C<MGRID> for the major grid, C<FONT> for
+the color of the font, C<AXIS> for the axis of the graph, C<FRAME> for the
+line around the color spots and finally C<ARROW> for the arrow head pointing
+up and forward. Each color is composed out of three hexadecimal numbers
+specifying its rgb color component (00 is off, FF is maximum) of red, green
+and blue. Optionally you may add another hexadecimal number specifying the
+transparency (FF is solid). You may set this option several times to alter
+multiple defaults.
+
+A green arrow is made by: C<--color ARROW#00FF00>
+
+[B<--zoom> I<factor>]
+
+Zoom the graphics by the given amount. The factor must be E<gt> 0
+
+[B<-n>|B<--font> I<FONTTAG>B<:>I<size>B<:>[I<font>]]
+
+This lets you customize which font to use for the various text
+elements on the RRD graphs. C<DEFAULT> sets the default value for all
+elements, C<TITLE> for the title, C<AXIS> for the axis labels, C<UNIT>
+for the vertical unit label, C<LEGEND> for the graph legend.
+
+Use Times for the title: C<--font TITLE:13:/usr/lib/fonts/times.ttf>
+
+If you do not give a font string you can modify just the sice of the default font:
+C<--font TITLE:13:>.
+
+If you specify the size 0 then you can modify just the font without touching
+the size. This is especially usefull for altering the default font without
+resetting the default fontsizes: C<--font DEFAULT:0:/usr/lib/fonts/times.ttf>.
+
+RRDtool comes with a preset default font. You can set the environment
+variable C<RRD_DEFAULT_FONT> if you want to change this.
+
+Truetype fonts are only supported for PNG output. See below.
+
+[B<-R>|B<--font-render-mode> {I<normal>,I<light>,I<mono>}]
+
+This lets you customize the strength of the font smoothing,
+or disable it entirely using I<mono>. By default, I<normal>
+font smoothing is used.
+
+[B<-B>|B<--font-smoothing-threshold> I<size>]
+
+This specifies the largest font size which will be rendered
+bitmapped, that is, without any font smoothing. By default,
+no text is rendered bitmapped.
+
+[B<-E>|B<--slope-mode>]
+
+RRDtool graphs are composed of stair case curves by default. This is in line with
+the way RRDtool calculates its data. Some people favor a more 'organic' look
+for their graphs even though it is not all that true.
+
+[B<-a>|B<--imgformat> B<PNG>|B<SVG>|B<EPS>|B<PDF>]
+
+Image format for the generated graph. For the vector formats you can
+choose among the standard Postscript fonts Courier-Bold,
+Courier-BoldOblique, Courier-Oblique, Courier, Helvetica-Bold,
+Helvetica-BoldOblique, Helvetica-Oblique, Helvetica, Symbol,
+Times-Bold, Times-BoldItalic, Times-Italic, Times-Roman, and ZapfDingbats.
+
+[B<-i>|B<--interlaced>]
+
+If images are interlaced they become visible on browsers more quickly.
+
+[B<-g>|B<--no-legend>]
+
+Suppress generation of the legend; only render the graph.
+
+[B<-F>|B<--force-rules-legend>]
+
+Force the generation of HRULE and VRULE legends even if those HRULE or
+VRULE will not be drawn because out of graph boundaries (mimics
+behaviour of pre 1.0.42 versions).
+
+[B<-T>|B<--tabwidth> I<value>]
+
+By default the tab-width is 40 pixels, use this option to change it.
+
+[B<-b>|B<--base> I<value>]
+
+If you are graphing memory (and NOT network traffic) this switch
+should be set to 1024 so that one Kb is 1024 byte. For traffic
+measurement, 1 kb/s is 1000 b/s.
+
+[B<-W>|B<--watermark> I<string>]
+
+Adds the given string as a watermark, horizontally centred, at the bottom
+of the graph.
+
+=item Data and variables
+
+B<DEF:>I<vname>B<=>I<rrdfile>B<:>I<ds-name>B<:>I<CF>[B<:step=>I<step>][B<:start=>I<time>][B<:end=>I<time>]
+
+B<CDEF:>I<vname>B<=>I<RPN expression>
+
+B<VDEF:>I<vname>B<=>I<RPN expression>
+
+You need at least one B<DEF> statement to generate anything. The
+other statements are useful but optional.
+See L<rrdgraph_data> and L<rrdgraph_rpn> for the exact format.
+
+=item Graph and print elements
+
+You need at least one graph element to generate an image and/or
+at least one print statement to generate a report.
+See L<rrdgraph_graph> for the exact format.
+
+=back
+
+=head1 SEE ALSO
+
+L<rrdgraph> gives an overview of how B<rrdtool graph> works.
+L<rrdgraph_data> describes B<DEF>,B<CDEF> and B<VDEF> in detail.
+L<rrdgraph_rpn> describes the B<RPN> language used in the B<?DEF> statements.
+L<rrdgraph_graph> page describes all of the graph and print functions.
+
+Make sure to read L<rrdgraph_examples> for tipsE<amp>tricks.
+
+=head1 AUTHOR
+
+Program by Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
+This manual page by Alex van den Bogaerdt E<lt>alex@ergens.op.het.netE<gt>
+
diff --git a/program/doc/rrdgraph_data.pod b/program/doc/rrdgraph_data.pod
--- /dev/null
@@ -0,0 +1,106 @@
+=head1 NAME
+
+rrdgraph_data - preparing data for graphing in rrdtool graph
+
+=head1 SYNOPSIS
+
+B<DEF:>I<E<lt>vnameE<gt>>=I<E<lt>rrdfileE<gt>>:I<E<lt>ds-nameE<gt>>:I<E<lt>CFE<gt>>[:step=I<E<lt>stepE<gt>>][:start=I<E<lt>timeE<gt>>][:end=I<E<lt>timeE<gt>>][:reduce=I<E<lt>B<CF>E<gt>>]
+
+B<VDEF>:I<vname>=I<RPN expression>
+
+B<CDEF>:I<vname>=I<RPN expression>
+
+=head1 DESCRIPTION
+
+These three instructions extract data values out of the B<RRD> files,
+optionally altering them (think, for example, of a bytes to bits
+conversion). If so desired, you can also define variables containing
+useful information such as maximum, minimum etcetera. Two of the
+instructions use a language called B<RPN> which is described in its
+own manual page.
+
+Variable names (I<vname>) must be made up strings of the following characters
+C<A-Z, a-z, 0-9, -,_> and a maximum length of 255 characters.
+
+When picking variable names, make sure you do not choose a name that is
+already taken by an RPN operator. A save bet it to use lowercase or
+mixedcase names for variables since operators will always be in uppercase.
+
+=head1 DEF
+
+B<DEF:>I<E<lt>vnameE<gt>>=I<E<lt>rrdfileE<gt>>:I<E<lt>ds-nameE<gt>>:I<E<lt>CFE<gt>>[:step=I<E<lt>stepE<gt>>][:start=I<E<lt>timeE<gt>>][:end=I<E<lt>timeE<gt>>][:reduce=I<E<lt>B<CF>E<gt>>]
+
+This command fetches data from an B<RRD> file. The virtual name
+I<vname> can then be used throughout the rest of the script. By
+default, an B<RRA> which contains the correct consolidated data
+at an appropriate resolution will be chosen. The resolution can
+be overridden with the L<--step|rrdgraph/item_Time> option.
+The resolution can again be overridden by specifying the B<step size>.
+The time span of this data is the same as for the graph by default,
+you can override this by specifying B<start and end>. Remember to
+escape colons in the time specification!
+
+If the resolution of the data is higher than the resolution of the
+graph, the data will be further consolidated. This may result in
+a graph that spans slightly more time than requested.
+Ideally each point in the graph should correspond with one B<CDP>
+from an B<RRA>. For instance, if your B<RRD> has an B<RRA> with
+a resolution of 1800 seconds per B<CDP>, you should create an
+image with width 400 and time span 400*1800 seconds (use appropriate
+start and end times, such as C<--start end-8days8hours>).
+
+If consolidation needs to be done, the B<CF> of the B<RRA> specified in the
+B<DEF> itself will be used to reduce the data density. This behaviour can
+be changed using C<:reduce=I<E<lt>B<CF>E<gt>>>. This optional parameter
+specifies the B<CF> to use during the data reduction phase.
+
+Example:
+
+ DEF:ds0=router.rrd:ds0:AVERAGE
+ DEF:ds0weekly=router.rrd:ds0:AVERAGE:step=7200
+ DEF:ds0weekly=router.rrd:ds0:AVERAGE:start=end-1h
+ DEF:ds0weekly=router.rrd:ds0:AVERAGE:start=11\:00:end=start+1h
+
+=head1 VDEF
+
+B<VDEF>:I<vname>=I<RPN expression>
+
+This command returns a value and/or a time according to the B<RPN>
+statements used. The resulting I<vname> will, depending on the
+functions used, have a value and a time component. When you use
+this I<vname> in another B<RPN> expression, you are effectively
+inserting its value just as if you had put a number at that place.
+The variable can also be used in the various graph and print
+elements.
+
+Example: C<VDEF:avg=mydata,AVERAGE>
+
+Note that currently only agregation functions work in VDEF rpn expressions.
+Patches to change this are welcome.
+
+=head1 CDEF
+
+B<CDEF>:I<vname>=I<RPN expression>
+
+This command creates a new set of data points (in memory only, not
+in the B<RRD> file) out of one or more other data series. The B<RPN>
+instructions are used to evaluate a mathematical function on each
+data point. The resulting I<vname> can then be used further on in
+the script, just as if it were generated by a B<DEF> instruction.
+
+Example: C<CDEF:mydatabits=mydata,8,*>
+
+=head1 SEE ALSO
+
+L<rrdgraph> gives an overview of how B<rrdtool graph> works.
+L<rrdgraph_data> describes B<DEF>,B<CDEF> and B<VDEF> in detail.
+L<rrdgraph_rpn> describes the B<RPN> language used in the B<?DEF> statements.
+L<rrdgraph_graph> page describes all of the graph and print functions.
+
+Make sure to read L<rrdgraph_examples> for tipsE<amp>tricks.
+
+=head1 AUTHOR
+
+Program by Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
+This manual page by Alex van den Bogaerdt E<lt>alex@ergens.op.het.netE<gt>
diff --git a/program/doc/rrdgraph_examples.pod b/program/doc/rrdgraph_examples.pod
--- /dev/null
@@ -0,0 +1,137 @@
+=head1 NAME
+
+rrdgraph_examples - Examples for rrdtool graph
+
+=head1 SYNOPSIS
+
+B<rrdtool graph /home/httpd/html/test.png --img-format PNG>
+
+followed by any of the examples below
+
+=head1 DESCRIPTION
+
+For your convenience some of the commands are explained here
+by using detailed examples. They are not always cut-and-paste
+ready because comments are intermixed with the examples.
+
+=head1 EXAMPLES
+
+=head2 Data with multiple resolutions
+
+ --end now --start end-120000s --width 400
+ DEF:ds0a=/home/rrdtool/data/router1.rrd:ds0:AVERAGE
+ DEF:ds0b=/home/rrdtool/data/router1.rrd:ds0:AVERAGE:step=1800
+ DEF:ds0c=/home/rrdtool/data/router1.rrd:ds0:AVERAGE:step=7200
+ LINE1:ds0a#0000FF:"default resolution\l"
+ LINE1:ds0b#00CCFF:"resolution 1800 seconds per interval\l"
+ LINE1:ds0c#FF00FF:"resolution 7200 seconds per interval\l"
+
+=head2 Nicely formatted legend section
+
+ DEF:ds0=/home/rrdtool/data/router1.rrd:ds0:AVERAGE
+ DEF:ds1=/home/rrdtool/data/router1.rrd:ds1:AVERAGE
+ VDEF:ds0max=ds0,MAXIMUM
+ VDEF:ds0avg=ds0,AVERAGE
+ VDEF:ds0min=ds0,MINIMUM
+ VDEF:ds0pct=ds0,95,PERCENT
+ VDEF:ds1max=ds1,MAXIMUM
+ VDEF:ds1avg=ds1,AVERAGE
+ VDEF:ds1min=ds1,MINIMUM
+ VDEF:ds1pct=ds1,95,PERCENT
+
+Note: consolidation occurs here.
+
+ CDEF:ds0bits=ds0,8,*
+ CDEF:ds1bits=ds1,8,*
+
+Note: 10 spaces to move text to the right
+
+ COMMENT:" "
+
+Note: the column titles have to be as wide as the columns
+
+ COMMENT:"Maximum "
+ COMMENT:"Average "
+ COMMENT:"Minimum "
+
+ COMMENT:"95th percentile\l"
+ AREA:ds0bits#00C000:"Inbound "
+ GPRINT:ds0max:"%6.2lf %Sbps"
+ GPRINT:ds0avg:"%6.2lf %Sbps"
+ GPRINT:ds0min:"%6.2lf %Sbps"
+ GPRINT:ds0pct:"%6.2lf %Sbps\l"
+ LINE1:ds1bits#0000FF:"Outbound"
+ GPRINT:ds1max:"%6.2lf %Sbps"
+ GPRINT:ds1avg:"%6.2lf %Sbps"
+ GPRINT:ds1min:"%6.2lf %Sbps"
+ GPRINT:ds1pct:"%6.2lf %Sbps\l"
+
+=head2 Offsetting a line on the y-axis
+
+Depending on your needs you can do this in two ways:
+
+=over 4
+
+=item *
+
+Offset the data, then graph this
+
+ DEF:mydata=my.rrd:ds:AVERAGE
+
+Note: this will also influence any other command that uses "data"
+
+ CDEF:data=mydata,100,+
+ LINE1:data#FF0000:"Data with offset"
+
+=item *
+
+Graph the original data, with an offset
+
+ DEF:mydata=my.rrd:ds:AVERAGE
+
+Note: no color in the first line so it is not visible
+
+ LINE1:100
+
+Note: the second line gets stacked on top of the first one
+
+ LINE1:data#FF0000:"Data with offset":STACK
+
+=back
+
+=head2 Time ranges
+
+ Last four weeks: --start end-4w --end 00:00
+ January 2001: --start 20010101 --end start+31d
+ January 2001: --start 20010101 --end 20010201
+ Last hour: --start end-1h
+ Last 24 hours: <nothing at all>
+ Yesterday: --end 00:00
+
+=head2 Viewing the current and previous week together
+
+ --end now --start end-1w
+ DEF:thisweek=router.rrd:ds0:AVERAGE
+ DEF:lastweek=router.rrd:ds0:AVERAGE:end=now-1w:start=end-1w
+
+Shift the data forward by one week (604800 seconds)
+
+ SHIFT:lastweek:604800
+ [ more of the usual VDEF and CDEF stuff if you like ]
+ AREA:lastweek#0000FF:Last\ week
+ LINE1:thisweek#FF0000:This\ week
+
+=head1 SEE ALSO
+
+L<rrdgraph> gives an overview of how B<rrdtool graph> works.
+L<rrdgraph_data> describes B<DEF>,B<CDEF> and B<VDEF> in detail.
+L<rrdgraph_rpn> describes the B<RPN> language used in the B<xDEF> statements.
+L<rrdgraph_graph> page describes all the graph and print functions.
+
+Make sure to read L<rrdgraph_examples> for tipsE<amp>tricks.
+
+=head1 AUTHOR
+
+Program by Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
+This manual page by Alex van den Bogaerdt E<lt>alex@ergens.op.het.netE<gt>
diff --git a/program/doc/rrdgraph_graph.pod b/program/doc/rrdgraph_graph.pod
--- /dev/null
@@ -0,0 +1,379 @@
+=pod
+
+=head1 NAME
+
+rrdgraph_graph - rrdtool graph command reference
+
+=head1 SYNOPSIS
+
+B<PRINT>B<:>I<vname>B<:>I<format>
+
+B<GPRINT>B<:>I<vname>B<:>I<format>
+
+B<COMMENT>B<:>I<text>
+
+B<VRULE>B<:>I<time>B<#>I<color>[B<:>I<legend>]
+
+B<HRULE>B<:>I<value>B<#>I<color>[B<:>I<legend>]
+
+B<LINE>[I<width>]B<:>I<value>[B<#>I<color>][B<:>[I<legend>][B<:STACK>]]
+
+B<AREA>B<:>I<value>[B<#>I<color>][B<:>[I<legend>][B<:STACK>]]
+
+B<TICK>B<:>I<vname>B<#>I<rrggbb>[I<aa>][B<:>I<fraction>[B<:>I<legend>]]
+
+B<SHIFT>B<:>I<vname>B<:>I<offset>
+
+=cut
+
+#
+#B<PART>B<:>I<vname>B<#>I<rrggbb>[I<aa>][B<:>I<legend>]
+#
+
+=pod
+
+B<PRINT>B<:>I<vname>B<:>I<CF>B<:>I<format> (deprecated)
+
+B<GPRINT>B<:>I<vname>B<:>I<CF>B<:>I<format> (deprecated)
+
+
+B<STACK>B<:>I<vname>B<#>I<color>[B<:>I<legend>] (deprecated)
+
+=head1 DESCRIPTION
+
+These instructions allow you to generate your image or report.
+If you don't use any graph elements, no graph is generated.
+Similarly, no report is generated if you don't use print options.
+
+=head1 PRINT
+
+=over 4
+
+
+=item B<PRINT:>I<vname>B<:>I<format>[B<:strftime>]
+
+Depending on the context, either the value component or the time
+component of a B<VDEF> is printed using I<format>. It is an error
+to specify a I<vname> generated by a B<DEF> or B<CDEF>.
+
+Any text in I<format> is printed literally with one exception:
+The percent character introduces a formatter string. This string
+can be:
+
+For printing values:
+
+=over 4
+
+=item B<%%>
+
+just prints a literal '%' character
+
+=item B<%#.#le>
+
+prints numbers like 1.2346e+04. The optional integers # denote field
+width and decimal precision.
+
+=item B<%#.#lf>
+
+prints numbers like 12345.6789, with optional field width
+and precision.
+
+=item B<%s>
+
+place this after B<%le>, B<%lf> or B<%lg>. This will be replaced by the
+appropriate SI magnitude unit and the value will be scaled
+accordingly (123456 -> 123.456 k).
+
+=item B<%S>
+
+is similar to B<%s>. It does, however, use a previously defined
+magnitude unit. If there is no such unit yet, it tries to define
+one (just like B<%s>) unless the value is zero, in which case the magnitude
+unit stays undefined. Thus, formatter strings using B<%S> and no B<%s>
+will all use the same magnitude unit except for zero values.
+
+=back
+
+If you PRINT a VDEF value, you can also print the time associated with it by appending the string
+B<:strftime> to the format. Note that rrdtool uses the strftime function of your OSs clibrary. This means that
+the conversion specifier may vary. Check the manual page if you are uncertain. The following is a list of
+conversion specifiers usually supported across the board.
+
+=over 4
+
+=item B<%a>
+
+The abbreviated weekday name according to the current locale.
+
+=item B<%A>
+
+The full weekday name according to the current locale.
+
+=item B<%b>
+
+The abbreviated month name according to the current locale.
+
+=item B<%B>
+
+The full month name according to the current locale.
+
+=item B<%c>
+
+The preferred date and time representation for the current locale.
+
+=item B<%d>
+
+The day of the month as a decimal number (range 01 to 31).
+
+=item B<%H>
+
+The hour as a decimal number using a 24-hour clock (range 00 to 23).
+
+=item B<%I>
+
+The hour as a decimal number using a 12-hour clock (range 01 to 12).
+
+=item B<%j>
+
+The day of the year as a decimal number (range 001 to 366).
+
+=item B<%m>
+
+The month as a decimal number (range 01 to 12).
+
+=item B<%M>
+
+The minute as a decimal number (range 00 to 59).
+
+=item B<%p>
+
+Either `AM' or `PM' according to the given time value, or the corresponding
+strings for the current locale. Noon is treated as `pm' and midnight as
+`am'. Note that in many locales and `pm' notation is unsupported and in
+such cases %p will return an empty string.
+
+=item B<%S>
+
+The second as a decimal number (range 00 to 61).
+
+=item B<%U>
+
+The week number of the current year as a decimal number, range 00 to 53, starting with the
+first Sunday as the first day of week 01. See also %V and %W.
+
+=item B<%V>
+
+The ISO 8601:1988 week number of the current year as a decimal number, range 01 to 53, where
+week 1 is the first week that has at least 4 days in the current year, and with Monday as the
+first day of the week. See also %U and %W.
+
+=item B<%w>
+
+The day of the week as a decimal, range 0 to 6, Sunday being 0. See also %u.
+
+=item B<%W>
+
+The week number of the current year as a decimal number, range 00 to 53, starting with the
+first Monday as the first day of week 01.
+
+=item B<%x>
+
+The preferred date representation for the current locale without the time.
+
+=item B<%X>
+
+The preferred time representation for the current locale without the date.
+
+=item B<%y>
+
+The year as a decimal number without a century (range 00 to 99).
+
+=item B<%Y>
+
+The year as a decimal number including the century.
+
+=item B<%Z>
+
+The time zone or name or abbreviation.
+
+=item B<%%>
+
+A literal `%' character.
+
+=back
+
+=item B<PRINT:>I<vname>B<:>I<CF>B<:>I<format>
+
+I<Deprecated. Use the new form of this command in new scripts.>
+The first form of this command is to be used with B<CDEF> I<vname>s.
+
+=back
+
+=head1 GRAPH
+
+=over 4
+
+=item B<GPRINT>B<:>I<vname>B<:>I<format>
+
+This is the same as C<PRINT>, but printed inside the graph.
+
+=item B<GPRINT>B<:>I<vname>B<:>I<CF>B<:>I<format>
+
+I<Deprecated. Use the new form of this command in new scripts.>
+This is the same as C<PRINT>, but printed inside the graph.
+
+=item B<COMMENT>B<:>I<text>
+
+Text is printed literally in the legend section of the graph. Note that in
+RRDtool 1.2 you have to escape colons in COMMENT text in the same way you
+have to escape them in B<*PRINT> commands by writing B<'\:'>.
+
+=item B<VRULE>B<:>I<time>B<#>I<color> [B<:>I<legend> ]
+
+Draw a vertical line at I<time>. Its color is composed from three
+hexadecimal numbers specifying the rgb color components (00 is off, FF is
+maximum) red, green and blue followed by an optional alpha. Optionally, a legend box and string is
+printed in the legend section. I<time> may be a number or a variable
+from a B<VDEF>. It is an error to use I<vname>s from B<DEF> or B<CDEF> here.
+
+=item B<HRULE>B<:>I<value>B<#>I<color> [ :I<legend> ]
+
+Draw a horyzontal line at I<value>. HRULE acts much like LINE except that
+will have no effect on the scale of the graph. If a HRULE is outside the
+graphing area it will just not be visible.
+
+=item B<LINE>[I<width>]B<:>I<value>[B<#>I<color>][B<:>[I<legend>][B<:STACK>]]
+
+Draw a line of the specified width onto the graph. I<width> can be a
+floating point number. If the color is not specified, the drawing is done
+'invisibly'. This is useful when stacking something else on top of this
+line. Also optional is the legend box and string which will be printed in
+the legend section if specified. The B<value> can be generated by B<DEF>,
+B<VDEF>, and B<CDEF>. If the optional B<STACK> modifier is used, this line
+is stacked on top of the previous element which can be a B<LINE> or an
+B<AREA>.
+
+When you do not specify a color, you cannot specify a legend. Should
+you want to use STACK, use the "LINEx:<value>::STACK" form.
+
+=item B<AREA>B<:>I<value>[B<#>I<color>][B<:>[I<legend>][B<:STACK>]]
+
+See B<LINE>, however the area between the x-axis and the line will
+be filled.
+
+=item B<TICK>B<:>I<vname>B<#>I<rrggbb>[I<aa>][B<:>I<fraction>[B<:>I<legend>]]
+
+Plot a tick mark (a vertical line) for each value of I<vname> that is
+non-zero and not *UNKNOWN*. The I<fraction> argument specifies the length of
+the tick mark as a fraction of the y-axis; the default value is 0.1 (10% of
+the axis). Note that the color specification is not optional. The TICK marks normaly
+start at the lower edge of the graphing area. If the fraction is negative they start
+at the upper border of the graphing area.
+
+=item B<SHIFT>B<:>I<vname>B<:>I<offset>
+
+Using this command B<RRDtool> will graph the following elements
+with the specified offset. For instance, you can specify an
+offset of S<( 7*24*60*60 = ) 604'800 seconds> to "look back" one
+week. Make sure to tell the viewer of your graph you did this ...
+As with the other graphing elements, you can specify a number or
+a variable here.
+
+=cut
+
+# This section describes the curruently defunct
+# PieChart code.
+#
+# =item B<PART>B<:>I<vname>B<#>I<rrggbb>[I<aa>][B<:>I<legend>]
+#
+# B<RRDtool> has now support for B<pie charts>. If you include the
+# B<PART> command, the canvas is extended to make room for a chart.
+# The size of the canvas is determined by the lesser of
+# L<width and height|rrdgraph/item_Size>.
+#
+# Pie parts will be concatenated, the first one will start at the
+# top and parts will be created clockwise. The size of the part
+# is defined by the value part of the L<VDEF|rrdgraph_data/VDEF>
+# function. It should return a number between 0 and 100, being a
+# percentage. Providing wrong input will produce undefined results.
+#
+#
+
+=pod
+
+=item B<STACK>B<:>I<vname>B<#>I<color>[B<:>I<legend>]
+
+I<Deprecated. Use the B<STACK> modifiers on the other commands.>
+
+=back
+
+B<Some notes on stacking>
+
+When stacking, an element is not placed above the X-axis but rather
+on top of the previous element. There must be something to stack
+upon.
+
+You can use an B<invisible> LINE or AREA to stacked upon.
+
+An B<unknown> value makes the entire stack unknown from that moment on.
+You don't know where to begin (the unknown value) and therefore do
+not know where to end.
+
+If you want to make sure you will be displaying a certain variable,
+make sure never to stack upon the unknown value. Use a CDEF instruction
+with B<IF> and B<UN> to do so.
+
+=head1 NOTES on legend arguments
+
+=head2 Escaping the colon
+
+A colon ':' in a I<legend> argument will mark the end of the
+legend. To enter a ':' as part of a legend, the colon must be escaped
+with a backslash '\:'. Beware that many environments process
+backslashes themselves, so it may be necessary to write two
+backslashes in order to one being passed onto rrd_graph.
+
+=head2 String Formatting
+
+The text printed below the actual graph can be formatted by appending special
+escape characters at the end of a text. When ever such a character occurs,
+all pending text is pushed onto the graph according to the character
+specified.
+
+Valid markers are: B<\j> for justified, B<\l> for left aligned, B<\r> for
+right aligned, and B<\c> for centered. In the next section there is an
+example showing how to use centered formatting.
+
+B<\n> is a valid alias for B<\l> since incomplete parsing in earlier
+versions of rrdtool lead to this behaviour and a number of people has been using it.
+
+Normally there are two space characters inserted between every two items
+printed into the graph. The space following a string can be suppressed by
+putting a B<\g> at the end of the string. The B<\g> also ignores any space
+inside the string if it is at the very end of the string. This can be used
+in connection with B<%s> to suppress empty unit strings.
+
+ GPRINT:a:MAX:%lf%s\g
+
+A special case is COMMENT:B<\s> which inserts some additional vertical space
+before placing the next row of legends.
+
+If you are using the proportional font in your graph, you can use tab
+characters or the sequence B<\t> to line-up legend elements. Note that
+the tabs inserted are relative to the start of the current legend
+element!
+
+=head1 SEE ALSO
+
+L<rrdgraph> gives an overview of how B<rrdtool graph> works.
+L<rrdgraph_data> describes B<DEF>,B<CDEF> and B<VDEF> in detail.
+L<rrdgraph_rpn> describes the B<RPN> language used in the B<?DEF> statements.
+L<rrdgraph_graph> page describes all of the graph and print functions.
+
+Make sure to read L<rrdgraph_examples> for tipsE<amp>tricks.
+
+=head1 AUTHOR
+
+Program by Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
+This manual page by Alex van den Bogaerdt E<lt>alex@ergens.op.het.netE<gt>
diff --git a/program/doc/rrdgraph_rpn.pod b/program/doc/rrdgraph_rpn.pod
--- /dev/null
@@ -0,0 +1,312 @@
+=head1 NAME
+
+rrdgraph_rpn - About RPN Math in rrdtool graph
+
+=head1 SYNOPSIS
+
+I<RPN expression>:=I<vname>|I<operator>|I<value>[,I<RPN expression>]
+
+=head1 DESCRIPTION
+
+If you have ever used a traditional HP calculator you already know
+B<RPN>. The idea behind B<RPN> is that you have a stack and push
+your data onto this stack. Whenever you execute an operation, it
+takes as many elements from the stack as needed. Pushing is done
+implicitly, so whenever you specify a number or a variable, it gets
+pushed onto the stack automatically.
+
+At the end of the calculation there should be one and only one value left on
+the stack. This is the outcome of the function and this is what is put into
+the I<vname>. For B<CDEF> instructions, the stack is processed for each
+data point on the graph. B<VDEF> instructions work on an entire data set in
+one run. Note, that currently B<VDEF> instructions only support a limited
+list of functions.
+
+Example: C<VDEF:maximum=mydata,MAXIMUM>
+
+This will set variable "maximum" which you now can use in the rest
+of your RRD script.
+
+Example: C<CDEF:mydatabits=mydata,8,*>
+
+This means: push variable I<mydata>, push the number 8, execute
+the operator I<*>. The operator needs two elements and uses those
+to return one value. This value is then stored in I<mydatabits>.
+As you may have guessed, this instruction means nothing more than
+I<mydatabits = mydata * 8>. The real power of B<RPN> lies in the
+fact that it is always clear in which order to process the input.
+For expressions like C<a = b + 3 * 5> you need to multiply 3 with
+5 first before you add I<b> to get I<a>. However, with parentheses
+you could change this order: C<a = (b + 3) * 5>. In B<RPN>, you
+would do C<a = b, 3, +, 5, *> without the need for parentheses.
+
+=head1 OPERATORS
+
+=over 4
+
+=item Boolean operators
+
+B<LT, LE, GT, GE, EQ, NE>
+
+Pop two elements from the stack, compare them for the selected condition
+and return 1 for true or 0 for false. Comparing an I<unknown> or an
+I<infinite> value will always result in 0 (false).
+
+B<UN, ISINF>
+
+Pop one element from the stack, compare this to I<unknown> respectively
+to I<positive or negative infinity>. Returns 1 for true or 0 for false.
+
+B<IF>
+
+Pops three elements from the stack. If the element popped last is 0
+(false), the value popped first is pushed back onto the stack,
+otherwise the value popped second is pushed back. This does, indeed,
+mean that any value other than 0 is considered to be true.
+
+Example: C<A,B,C,IF> should be read as C<if (A) then (B) else (C)>
+
+Z<>
+
+=item Comparing values
+
+B<MIN, MAX>
+
+Pops two elements from the stack and returns the smaller or larger,
+respectively. Note that I<infinite> is larger than anything else.
+If one of the input numbers is I<unknown> then the result of the operation will be
+I<unknown> too.
+
+B<LIMIT>
+
+Pops two elements from the stack and uses them to define a range.
+Then it pops another element and if it falls inside the range, it
+is pushed back. If not, an I<unknown> is pushed.
+
+The range defined includes the two boundaries (so: a number equal
+to one of the boundaries will be pushed back). If any of the three
+numbers involved is either I<unknown> or I<infinite> this function
+will always return an I<unknown>
+
+Example: C<CDEF:a=alpha,0,100,LIMIT> will return I<unknown> if
+alpha is lower than 0 or if it is higher than 100.
+
+Z<>
+
+=item Arithmetics
+
+B<+, -, *, /, %>
+
+Add, subtract, multiply, divide, modulo
+
+B<SIN, COS, LOG, EXP, SQRT>
+
+Sine and cosine (input in radians), log and exp (natural logarithm),
+square root.
+
+B<ATAN>
+
+Arctangent (output in radians).
+
+B<ATAN2>
+
+Arctangent of y,x components (output in radians).
+This pops one element from the stack, the x (cosine) component, and then
+a second, which is the y (sine) component.
+It then pushes the arctangent of their ratio, resolving the ambiguity between
+quadrants.
+
+Example: C<CDEF:angle=Y,X,ATAN2,RAD2DEG> will convert C<X,Y>
+components into an angle in degrees.
+
+B<FLOOR, CEIL>
+
+Round down or up to the nearest integer.
+
+B<DEG2RAD, RAD2DEG>
+
+Convert angle in degrees to radians, or radians to degrees.
+
+B<ABS>
+
+Take the absolute value.
+
+=item Set Operations
+
+B<SORT, REV>
+
+Pop one element from the stack. This is the I<count> of items to be sorted
+(or reversed). The top I<count> of the remaining elements are then sorted
+(or reversed) in place on the stack.
+
+Example: C<CDEF:x=v1,v2,v3,v4,v5,v6,6,SORT,POP,5,REV,POP,+,+,+,4,/> will
+compute the average of the values v1 to v6 after removing the smallest and
+largest.
+
+B<AVG>
+
+Pop one element (I<count>) from the stack. Now pop I<count> elements and build the
+average, ignoring all UNKNOWN values in the process.
+
+Example: C<CDEF:x=a,b,c,d,4,AVG>
+
+B<TREND>
+
+Create a "sliding window" average of another data series.
+
+Usage:
+CDEF:smoothed=x,1800,TREND
+
+This will create a half-hour (1800 second) sliding window average of x. The
+average is essentially computed as shown here:
+
+ +---!---!---!---!---!---!---!---!--->
+ now
+ delay t0
+ <--------------->
+ delay t1
+ <--------------->
+ delay t2
+ <--------------->
+
+
+ Value at sample (t0) will be the average between (t0-delay) and (t0)
+ Value at sample (t1) will be the average between (t1-delay) and (t1)
+ Value at sample (t2) will be the average between (t2-delay) and (t2)
+
+=item Special values
+
+B<UNKN>
+
+Pushes an unknown value on the stack
+
+B<INF, NEGINF>
+
+Pushes a positive or negative infinite value on the stack. When
+such a value is graphed, it appears at the top or bottom of the
+graph, no matter what the actual value on the y-axis is.
+
+B<PREV>
+
+Pushes an I<unknown> value if this is the first value of a data
+set or otherwise the result of this B<CDEF> at the previous time
+step. This allows you to do calculations across the data. This
+function cannot be used in B<VDEF> instructions.
+
+B<PREV(vname)>
+
+Pushes an I<unknown> value if this is the first value of a data
+set or otherwise the result of the vname variable at the previous time
+step. This allows you to do calculations across the data. This
+function cannot be used in B<VDEF> instructions.
+
+B<COUNT>
+
+Pushes the number 1 if this is the first value of the data set, the
+number 2 if it is the second, and so on. This special value allows
+you to make calculations based on the position of the value within
+the data set. This function cannot be used in B<VDEF> instructions.
+
+=item Time
+
+Time inside RRDtool is measured in seconds since the epoch. The
+epoch is defined to be S<C<Thu Jan 1 00:00:00 UTC 1970>>.
+
+B<NOW>
+
+Pushes the current time on the stack.
+
+B<TIME>
+
+Pushes the time the currently processed value was taken at onto the stack.
+
+B<LTIME>
+
+Takes the time as defined by B<TIME>, applies the time zone offset
+valid at that time including daylight saving time if your OS supports
+it, and pushes the result on the stack. There is an elaborate example
+in the examples section below on how to use this.
+
+=item Processing the stack directly
+
+B<DUP, POP, EXC>
+
+Duplicate the top element, remove the top element, exchange the two
+top elements.
+
+Z<>
+
+=back
+
+=head1 VARIABLES
+
+These operators work only on B<VDEF> statements. Note that currently ONLY these work for B<VDEF>.
+
+=over 4
+
+=item MAXIMUM, MINIMUM, AVERAGE
+
+Return the corresponding value, MAXIMUM and MINIMUM also return
+the first occurrence of that value in the time component.
+
+Example: C<VDEF:avg=mydata,AVERAGE>
+
+=item LAST, FIRST
+
+Return the last/first value including its time. The time for
+FIRST is actually the start of the corresponding interval, whereas
+LAST returns the end of the corresponding interval.
+
+Example: C<VDEF:first=mydata,FIRST>
+
+=item TOTAL
+
+Returns the rate from each defined time slot multiplied with the
+step size. This can, for instance, return total bytes transfered
+when you have logged bytes per second. The time component returns
+the number of seconds.
+
+Example: C<VDEF:total=mydata,TOTAL>
+
+=item PERCENT
+
+This should follow a B<DEF> or B<CDEF> I<vname>. The I<vname> is popped,
+another number is popped which is a certain percentage (0..100). The
+data set is then sorted and the value returned is chosen such that
+I<percentage> percent of the values is lower or equal than the result.
+I<Unknown> values are considered lower than any finite number for this
+purpose so if this operator returns an I<unknown> you have quite a lot
+of them in your data. B<Inf>inite numbers are lesser, or more, than the
+finite numbers and are always more than the I<Unknown> numbers.
+(NaN E<lt> -INF E<lt> finite values E<lt> INF)
+
+Example: C<VDEF:perc95=mydata,95,PERCENT>
+
+=item LSLSLOPE, LSLINT, LSLCORREL
+
+Return the parameters for a B<L>east B<S>quares B<L>ine I<(y = mx +b)>
+which approximate the provided dataset. LSLSLOPE is the slope I<(m)> of
+the line related to the COUNT position of the data. LSLINT is the
+y-intercept I<(b)>, which happens also to be the first data point on the
+graph. LSLCORREL is the Correlation Coefficient (also know as Pearson's
+Product Moment Correlation Coefficient). It will range from 0 to +/-1
+and represents the quality of fit for the approximation.
+
+Example: C<VDEF:slope=mydata,LSLSLOPE>
+
+=back
+
+=head1 SEE ALSO
+
+L<rrdgraph> gives an overview of how B<rrdtool graph> works.
+L<rrdgraph_data> describes B<DEF>,B<CDEF> and B<VDEF> in detail.
+L<rrdgraph_rpn> describes the B<RPN> language used in the B<?DEF> statements.
+L<rrdgraph_graph> page describes all of the graph and print functions.
+
+Make sure to read L<rrdgraph_examples> for tipsE<amp>tricks.
+
+=head1 AUTHOR
+
+Program by Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
+This manual page by Alex van den Bogaerdt E<lt>alex@ergens.op.het.netE<gt>
diff --git a/program/doc/rrdinfo.pod b/program/doc/rrdinfo.pod
--- /dev/null
+++ b/program/doc/rrdinfo.pod
@@ -0,0 +1,62 @@
+=head1 NAME
+
+rrdinfo - extract header information from an RRD
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<info> I<filename.rrd>
+
+=head1 DESCRIPTION
+
+The B<info> function prints the header information from an RRD in
+a parsing friendly format.
+
+Check L<rrdcreate> if you are uncertain about the meaning of the
+individual keys.
+
+=head1 EXAMPLE
+
+This is the output generated by running B<info> on a simple RRD which
+contains two data sources and one RRA. Note that the number after the
+I<last_update> keyword is in seconds since 1970. The string B<NaN>
+stands for I<*UNKNOWN*> data. In the example it means that this RRD
+has neither minimum nor maximum values defined for either of its
+data sources.
+
+ filename = "random.rrd"
+ rrd_version = "0001"
+ step = 300
+ last_update = 955892996
+ ds[a].type = "GAUGE"
+ ds[a].minimal_heartbeat = 600
+ ds[a].min = NaN
+ ds[a].max = NaN
+ ds[a].last_ds = "UNKN"
+ ds[a].value = 2.1824421548e+04
+ ds[a].unknown_sec = 0
+ ds[b].type = "GAUGE"
+ ds[b].minimal_heartbeat = 600
+ ds[b].min = NaN
+ ds[b].max = NaN
+ ds[b].last_ds = "UNKN"
+ ds[b].value = 3.9620838224e+03
+ ds[b].unknown_sec = 0
+ rra[0].cf = "AVERAGE"
+ rra[0].pdp_per_row = 1
+ rra[0].cdp_prep[0].value = nan
+ rra[0].cdp_prep[0].unknown_datapoints = 0
+ rra[0].cdp_prep[1].value = nan
+ rra[0].cdp_prep[1].unknown_datapoints = 0
+
+=over 8
+
+=item I<filename.rrd>
+
+The name of the B<RRD> you want to examine.
+
+=back
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
diff --git a/program/doc/rrdlast.pod b/program/doc/rrdlast.pod
--- /dev/null
+++ b/program/doc/rrdlast.pod
@@ -0,0 +1,27 @@
+=head1 NAME
+
+rrdlast - Return the date of the last data sample in an RRD
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<last> I<filename>
+
+=head1 DESCRIPTION
+
+The B<last> function returns the UNIX timestamp of the most recent
+update of the RRD.
+
+=over 8
+
+=item I<filename>
+
+The name of the B<RRD> that contains the data.
+
+=back
+
+=head1 AUTHOR
+
+Russ Wright <rwwright@home.com>
+
+
+
diff --git a/program/doc/rrdlastupdate.pod b/program/doc/rrdlastupdate.pod
--- /dev/null
@@ -0,0 +1,27 @@
+=head1 NAME
+
+rrdlastupdate - Return the most recent update to an RRD
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<lastupdate> I<filename>
+
+=head1 DESCRIPTION
+
+The B<lastupdate> function returns the UNIX timestamp and the
+value stored for each datum in the most recent update of an RRD.
+
+=over 8
+
+=item I<filename>
+
+The name of the B<RRD> that contains the data.
+
+=back
+
+=head1 AUTHOR
+
+Andy Riebs <andy.riebs@hp.com>
+
+
+
diff --git a/program/doc/rrdpython.pod b/program/doc/rrdpython.pod
--- /dev/null
@@ -0,0 +1,59 @@
+=head1 NAME
+
+rrdpython - About the RRD Python bindings
+
+=head1 SYNOPSIS
+
+ import rrdtool
+ rrdtool.create('/tmp/test.rrd', 'DS:foo:GUAGE:20:0:U')
+
+=head1 DESCRIPTION
+
+The B<rrdtool> functions are directly callable via the Python programming
+language. This wrapper implementation has been written from the scratch
+(without SWIG)
+
+The API's simply expects string parameters to the functions. Please refer
+to the other B<rrdtool> documentation for functions and valid arguments.
+
+=head1 EXAMPLE
+
+ sys.path.append('/path/to/rrdtool/lib/python2.3/site-packages/')
+ import rrdtool, tempfile
+
+ DAY = 86400
+ YEAR = 365 * DAY
+ fd,path = tempfile.mkstemp('.png')
+
+ rrdtool.graph(path,
+ '--imgformat', 'PNG',
+ '--width', '540',
+ '--height', '100',
+ '--start', "-%i" % YEAR,
+ '--end', "-1",
+ '--vertical-label', 'Downloads/Day',
+ '--title', 'Annual downloads',
+ '--lower-limit', '0',
+ 'DEF:downloads=downloads.rrd:downloads:AVERAGE',
+ 'AREA:downloads#990033:Downloads')
+
+ info = rrdtool.info('downloads.rrd')
+ print info['last_update']
+ print info['ds']['downloads']['minimal_heartbeat']
+
+If you use the B<site-python-install> make target you can drop to first sys.path.append
+line since the rrdtool module will be available everywhere.
+
+If rrdtool runs into trouble, it will throw an exception which you might want to catch.
+
+=head1 SEE ALSO
+
+rrdcreate, rrdupdate, rrdgraph, rrddump, rrdfetch, rrdtune, rrdlast,
+rrdxport, rrdinfo
+
+=head1 AUTHOR
+
+Hye-Shik Chang E<lt>perky@i18n.orgE<gt>
+
+Alan Milligan E<lt>alan.milligan@last-bastion.netE<gt>
+
diff --git a/program/doc/rrdresize.pod b/program/doc/rrdresize.pod
--- /dev/null
@@ -0,0 +1,54 @@
+=head1 NAME
+
+rrdresize - alters the size of an RRA and creates a new .rrd file
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<resize> I<filename> I<rra-num> B<GROW>I<|>B<SHRINK> I<rows>
+
+=head1 DESCRIPTION
+
+The B<resize> function is used to modify the number of rows in
+an B<RRA>.
+
+=over 8
+
+=item I<filename>
+
+the name of the B<RRD> you want to alter.
+
+=item I<rra-num>
+
+the B<RRA> you want to alter. You can find the number using B<rrdtool info>.
+
+=item B<GROW>
+
+used if you want to add extra rows to an RRA. The extra rows will be inserted
+as the rows that are oldest.
+
+=item B<SHRINK>
+
+used if you want to remove rows from an RRA. The rows that will be removed
+are the oldest rows.
+
+=item I<rows>
+
+the number of rows you want to add or remove.
+
+=back
+
+=head1 NOTES
+
+The new .rrd file, with the modified RRAs, is written to the file
+B<resize.rrd> in the current directory. B<The original .rrd file is not
+modified>.
+
+It is possible to abuse this tool and get strange results
+by first removing some rows and then reinserting the same amount (effectively
+clearing them to be Unknown). You may thus end up with unknown data in one
+RRA while at the same timestamp this data is available in another RRA.
+
+=head1 AUTHOR
+
+Alex van den Bogaerdt <alex@ergens.op.het.net>
+
diff --git a/program/doc/rrdrestore.pod b/program/doc/rrdrestore.pod
--- /dev/null
@@ -0,0 +1,38 @@
+=head1 NAME
+
+rrdrestore - Restore the contents of an RRD from its XML dump format
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<restore> I<filename.xml> I<filename.rrd>
+S<[B<--range-check>|B<-r>]>
+
+=head1 DESCRIPTION
+
+The B<restore> function reads the XML representation of an RRD and converts
+it to the native B<RRD> format.
+
+=over 8
+
+=item I<filename.xml>
+
+The name of the B<XML> file you want to restore.
+
+=item I<filename.rrd>
+
+The name of the B<RRD> to restore.
+
+=item B<--range-check>|B<-r>
+
+Make sure the values in the RRAs do not exceed the limits defined for
+the various data sources.
+
+=item B<--force-overwrite>|B<-f>
+
+Allows B<RRDtool> to overwrite the destination B<RRD>.
+
+=back
+
+=head1 AUTHOR
+
+Tobias Oetiker <tobi@oetiker.ch>
diff --git a/program/doc/rrdruby.pod b/program/doc/rrdruby.pod
--- /dev/null
+++ b/program/doc/rrdruby.pod
@@ -0,0 +1,86 @@
+=head1 NAME
+
+rrdruby - About the RRD Ruby bindings
+
+=head1 SYNOPSIS
+
+ require "RRD"
+ RRD.create(
+ rrd,
+ "--step", "300",
+ "DS:a:GAUGE:600:U:U",
+ "DS:b:GAUGE:600:U:U",
+ "RRA:AVERAGE:0.5:1:300")
+
+=head1 DESCRIPTION
+
+The B<rrdtool> functions are directly callable via the Ruby programming
+language. This wrapper implementation has been written from the scratch
+(without SWIG)
+
+The API's simply expects string parameters to the functions. Please refer
+to the other B<rrdtool> documentation for functions and valid arguments.
+
+=head1 EXAMPLE
+
+ $: << '/path/to/rrdtool/lib/ruby/1.8/i386-linux'
+ require "RRD"
+
+ name = "test"
+ rrd = "#{name}.rrd"
+ start = Time.now.to_i
+
+ RRD.create(
+ rrd,
+ "--start", "#{start - 1}",
+ "--step", "300",
+ "DS:a:GAUGE:600:U:U",
+ "DS:b:GAUGE:600:U:U",
+ "RRA:AVERAGE:0.5:1:300")
+ puts
+
+ puts "updating #{rrd}"
+ start.to_i.step(start.to_i + 300 * 300, 300) { |i|
+ RRD.update(rrd, "#{i}:#{rand(100)}:#{Math.sin(i / 800) * 50 + 50}")
+ }
+ puts
+
+ puts "fetching data from #{rrd}"
+ (fstart, fend, data) = RRD.fetch(rrd, "--start", start.to_s, "--end",
+ (start + 300 * 300).to_s, "AVERAGE")
+ puts "got #{data.length} data points from #{fstart} to #{fend}"
+ puts
+
+ puts "generating graph #{name}.png"
+ RRD.graph(
+ "#{name}.png",
+ "--title", " RubyRRD Demo",
+ "--start", "#{start+3600}",
+ "--end", "start + 1000 min",
+ "--interlace",
+ "--imgformat", "PNG",
+ "--width=450",
+ "DEF:a=#{rrd}:a:AVERAGE",
+ "DEF:b=#{rrd}:b:AVERAGE",
+ "CDEF:line=TIME,2400,%,300,LT,a,UNKN,IF",
+ "AREA:b#00b6e4:beta",
+ "AREA:line#0022e9:alpha",
+ "LINE3:line#ff0000")
+ puts
+
+If you use the B<--ruby-site-install> configure option you can drop the $:
+line since the rrdtool module will be found automatically.
+
+If rrdtool runs into trouble, it will throw an exception which you might
+want to catch.
+
+=head1 SEE ALSO
+
+rrdcreate, rrdupdate, rrdgraph, rrddump, rrdfetch, rrdtune, rrdlast,
+rrdxport, rrdinfo
+
+=head1 AUTHOR
+
+Loïs Lherbier E<lt>lois.lherbier@covadis.chE<gt>
+
+Miles Egan E<lt>miles@caddr.comE<gt>
diff --git a/program/doc/rrdthreads.pod b/program/doc/rrdthreads.pod
--- /dev/null
@@ -0,0 +1,144 @@
+=head1 NAME
+
+rrdthreads - Provisions for linking the RRD library to use in multi-threaded programs
+
+=head1 SYNOPSIS
+
+Using librrd in multi-threaded programs requires some extra
+precautions, as the RRD library in its original form was not
+thread-safe at all. This document describes requirements and pitfalls
+on the way to use the multi-threaded version of librrd in your own
+programs. It also gives hints for future RRD development to keep the
+library thread-safe.
+
+Currently only some RRD operations are implemented in a thread-safe
+way. They all end in the usual "C<_r>" suffix.
+
+=head1 DESCRIPTION
+
+In order to use librrd in multi-threaded programs you must:
+
+=over
+
+=item *
+
+Link with F<librrd_th> instead of F<librrd> (use C<-lrrd_th> when
+linking)
+
+=item *
+
+Use the "C<_r>" functions instead of the normal API-functions
+
+=item *
+
+Do not use any at-style time specifications. Parsing of such time
+specifications is terribly non-thread-safe.
+
+=item *
+
+Never use non *C<_r> functions unless it is explicitly documented that
+the function is tread-safe.
+
+=item *
+
+Every thread SHOULD call C<rrd_get_context()> before its first call to
+any C<librrd_th> function in order to set up thread specific data. This
+is not strictly required, but it is the only way to test if memory
+allocation can be done by this function. Otherwise the program may die
+with a SIGSEGV in a low-memory situation.
+
+=item *
+
+Always call C<rrd_error_clear()> before any call to the
+library. Otherwise the call might fail due to some earlier error.
+
+=back
+
+=head2 NOTES FOR RRD CONTRIBUTORS
+
+Some precautions must be followed when developing RRD from now on:
+
+=over
+
+=item *
+
+Only use thread-safe functions in library code. Many often used libc
+functions aren't thread-safe. Take care in the following
+situations or when using the following library functions:
+
+=over
+
+=item *
+
+Direct calls to C<strerror()> must be avoided: use C<rrd_strerror()>
+instead, it provides a per-thread error message.
+
+=item *
+
+The C<getpw*>, C<getgr*>, C<gethost*> function families (and some more
+C<get*> functions) are not thread-safe: use the *C<_r> variants
+
+=item *
+
+Time functions: C<asctime>, C<ctime>, C<gmtime>, C<localtime>: use
+*C<_r> variants
+
+=item *
+
+C<strtok>: use C<strtok_r>
+
+=item *
+
+C<tmpnam>: use C<tmpnam_r>
+
+=item *
+
+Many others (lookup documentation)
+
+=back
+
+=item *
+
+A header file named F<rrd_is_thread_safe.h> is provided
+that works with the GNU C-preprocessor to "poison" some of the most
+common non-thread-safe functions using the C<#pragma GCC poison>
+directive. Just include this header in source files you want to keep
+thread-safe.
+
+=item *
+
+Do not introduce global variables!
+
+If you really, really have to use a global variable you may add a new
+field to the C<rrd_context> structure and modify F<rrd_error.c>,
+F<rrd_thread_safe.c> and F<rrd_non_thread_safe.c>
+
+=item *
+
+Do not use C<getopt> or C<getopt_long> in *C<_r> (neither directly nor
+indirectly).
+
+C<getopt> uses global variables and behaves badly in a multi-threaded
+application when called concurrently. Instead provide a *_r function
+taking all options as function parameters. You may provide argc and
+**argv arguments for variable length argument lists. See
+C<rrd_update_r> as an example.
+
+=item *
+
+Do not use the C<parsetime> function!
+
+It uses lots of global variables. You may use it in functions not designed
+to be thread-safe, like in functions wrapping the C<_r> version of some
+operation (e.g., C<rrd_create>, but not in C<rrd_create_r>)
+
+=back
+
+=head2 CURRENTLY IMPLEMENTED THREAD SAFE FUNCTIONS
+
+Currently there exist thread-safe variants of C<rrd_update>,
+C<rrd_create>, C<rrd_dump>, C<rrd_info>, C<rrd_last>, and C<rrd_fetch>.
+
+=head1 AUTHOR
+
+Peter Stamfest E<lt>peter@stamfest.atE<gt>
diff --git a/program/doc/rrdtool-dump.dtd b/program/doc/rrdtool-dump.dtd
--- /dev/null
@@ -0,0 +1,39 @@
+<!-- rrdtool-dump.dtd -->
+<!-- wolfgang{dot}schrimm{at}urz{dot}uni-heidelberg{dot}de -->
+
+<!-- root element -->
+<!ELEMENT rrd (version, step, lastupdate, ds+, rra+)>
+
+<!-- rrd's children -->
+<!ELEMENT version (#PCDATA)>
+<!ELEMENT step (#PCDATA)>
+<!ELEMENT lastupdate (#PCDATA)>
+<!-- There are two different elements with the same name -->
+<!-- /rrd/ds and /rrd/rra/cdp_prep/ds -->
+<!ELEMENT ds ((name, type, minimal_heartbeat, min, max, last_ds, value,
+unknown_sec)|(value, unknown_datapoints))>
+<!ELEMENT rra (cf, pdp_per_row, xff, cdp_prep, database)>
+
+<!-- ds's children -->
+<!ELEMENT name (#PCDATA)>
+<!ELEMENT type (#PCDATA)>
+<!ELEMENT minimal_heartbeat (#PCDATA)>
+<!ELEMENT min (#PCDATA)>
+<!ELEMENT max (#PCDATA)>
+<!ELEMENT last_ds (#PCDATA)>
+<!ELEMENT unknown_sec (#PCDATA)>
+<!ELEMENT unknown_datapoints (#PCDATA)>
+<!-- There are two different elements with the same name -->
+<!-- /rrd/ds/value and /rrd/rra/cdp_prep/ds/value -->
+<!ELEMENT value (#PCDATA)>
+
+<!-- rra's children -->
+<!ELEMENT cf (#PCDATA)>
+<!ELEMENT pdp_per_row (#PCDATA)>
+<!ELEMENT xff (#PCDATA)>
+<!ELEMENT cdp_prep (ds+)>
+<!ELEMENT database (row+)>
+
+<!-- database's children -->
+<!ELEMENT row (v+)>
+<!ELEMENT v (#PCDATA)>
diff --git a/program/doc/rrdtool-xport.dtd b/program/doc/rrdtool-xport.dtd
--- /dev/null
@@ -0,0 +1,32 @@
+<!-- rrdtool-xport.dtd -->
+<!-- the attributes of the row and the t elements are used -->
+<!-- in the examples/shared-demo.pl, but not in the output -->
+<!-- of the native xport command. -->
+<!-- wolfgang{dot}schrimm{at}urz{dot}uni-heidelberg{dot}de -->
+
+<!-- root element -->
+<!ELEMENT xport (meta, data)>
+
+<!-- root's children -->
+<!ELEMENT meta (start, step, end, rows, columns, legend)>
+<!ELEMENT data (row+)>
+
+<!-- meta's children -->
+<!ELEMENT start (#PCDATA)>
+<!ELEMENT step (#PCDATA)>
+<!ELEMENT end (#PCDATA)>
+<!ELEMENT rows (#PCDATA)>
+<!ELEMENT columns (#PCDATA)>
+<!ELEMENT legend (entry+)>
+
+<!-- legend's children -->
+<!ELEMENT entry (#PCDATA)>
+
+<!-- data's children -->
+<!ELEMENT row (t, v+)>
+<!ATTLIST row id CDATA #IMPLIED>
+
+<!-- row's children -->
+<!ELEMENT t (#PCDATA)>
+<!ATTLIST t is CDATA #IMPLIED>
+<!ELEMENT v (#PCDATA)>
diff --git a/program/doc/rrdtool.pod b/program/doc/rrdtool.pod
--- /dev/null
+++ b/program/doc/rrdtool.pod
@@ -0,0 +1,312 @@
+=head1 NAME
+
+rrdtool - Round Robin Database Tool
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<-> [workdir]| I<function>
+
+=head1 DESCRIPTION
+
+=head2 OVERVIEW
+
+It is pretty easy to gather status information from all sorts of
+things, ranging from the temperature in your office to the number of
+octets which have passed through the FDDI interface of your
+router. But it is not so trivial to store this data in an efficient and
+systematic manner. This is where B<RRDtool> comes in handy. It lets you
+I<log and analyze> the data you gather from all kinds of data-sources
+(B<DS>). The data analysis part of RRDtool is based on the ability to
+quickly generate graphical representations of the data values
+collected over a definable time period.
+
+In this man page you will find general information on the design and
+functionality of the Round Robin Database Tool (RRDtool). For a more
+detailed description of how to use the individual functions of
+B<RRDtool> check the corresponding man page.
+
+For an introduction to the usage of RRDtool make sure you consult the
+L<rrdtutorial>.
+
+=head2 FUNCTIONS
+
+While the man pages talk of command line switches you have to set in
+order to make B<RRDtool> work it is important to note that
+B<RRDtool> can be remotely controlled through a set of pipes. This
+saves a considerable amount of startup time when you plan to make
+B<RRDtool> do a lot of things quickly. Check the section on L<"Remote
+Control"> further down. There is also a number of language bindings
+for RRDtool which allow you to use it directly from perl, python, tcl,
+php, etc.
+
+=over 8
+
+=item B<create>
+
+Set up a new Round Robin Database (RRD). Check L<rrdcreate>.
+
+=item B<update>
+
+Store new data values into an RRD. Check L<rrdupdate>.
+
+=item B<updatev>
+
+Operationally equivalent to B<update> except for output. Check L<rrdupdate>.
+
+=item B<graph>
+
+Create a graph from data stored in one or several RRDs. Apart from
+generating graphs, data can also be extracted to stdout. Check L<rrdgraph>.
+
+=item B<dump>
+
+Dump the contents of an RRD in plain ASCII. In connection with restore
+you can use this to move an RRD from one computer architecture to
+another. Check L<rrddump>.
+
+=item B<restore>
+
+Restore an RRD in XML format to a binary RRD. Check L<rrdrestore>
+
+=item B<fetch>
+
+Get data for a certain time period from a RRD. The graph function
+uses fetch to retrieve its data from an RRD. Check L<rrdfetch>.
+
+=item B<tune>
+
+Alter setup of an RRD. Check L<rrdtune>.
+
+=item B<last>
+
+Find the last update time of an RRD. Check L<rrdlast>.
+
+=item B<info>
+
+Get information about an RRD. Check L<rrdinfo>.
+
+=item B<rrdresize>
+
+Change the size of individual RRAs. This is dangerous! Check L<rrdresize>.
+
+=item B<xport>
+
+Export data retrieved from one or several RRDs. Check L<rrdxport>
+
+=item B<rrdcgi>
+
+This is a standalone tool for producing RRD graphs on the fly. Check
+L<rrdcgi>.
+
+=back
+
+=head2 HOW DOES RRDTOOL WORK?
+
+=over 8
+
+=item Data Acquisition
+
+When monitoring the state of a system, it is convenient to have the
+data available at a constant time interval. Unfortunately, you may not
+always be able to fetch data at exactly the time you want
+to. Therefore B<RRDtool> lets you update the logfile at any time you
+want. It will automatically interpolate the value of the data-source
+(B<DS>) at the latest official time-slot (intervall) and write this
+interpolated value to the log. The original value you have supplied is
+stored as well and is also taken into account when interpolating the
+next log entry.
+
+=item Consolidation
+
+You may log data at a 1 minute interval, but you might also be
+interested to know the development of the data over the last year. You
+could do this by simply storing the data in 1 minute intervals for the
+whole year. While this would take considerable disk space it would
+also take a lot of time to analyze the data when you wanted to create
+a graph covering the whole year. B<RRDtool> offers a solution to this
+problem through its data consolidation feature. When setting up an
+Round Robin Database (B<RRD>), you can define at which interval this
+consolidation should occur, and what consolidation function (B<CF>)
+(average, minimum, maximum, total, last) should be used to build the
+consolidated values (see rrdcreate). You can define any number of
+different consolidation setups within one B<RRD>. They will all be
+maintained on the fly when new data is loaded into the B<RRD>.
+
+=item Round Robin Archives
+
+Data values of the same consolidation setup are stored into Round
+Robin Archives (B<RRA>). This is a very efficient manner to store data
+for a certain amount of time, while using a known and constant amount
+of storage space.
+
+It works like this: If you want to store 1'000 values in 5 minute
+interval, B<RRDtool> will allocate space for 1'000 data values and a
+header area. In the header it will store a pointer telling which slots
+(value) in the storage area was last written to. New values are
+written to the Round Robin Archive in, you guessed it, a round robin
+manner. This automatically limits the history to the last 1'000 values
+(in our example). Because you can define several B<RRA>s within a
+single B<RRD>, you can setup another one, for storing 750 data values
+at a 2 hour interval, for example, and thus keep a log for the last
+two months at a lower resolution.
+
+The use of B<RRA>s guarantees that the B<RRD> does not grow over
+time and that old data is automatically eliminated. By using the
+consolidation feature, you can still keep data for a very long time,
+while gradually reducing the resolution of the data along the time
+axis.
+
+Using different consolidation functions (B<CF>) allows you to store
+exactly the type of information that actually interests you: the maximum
+one minute traffic on the LAN, the minimum temperature of your wine cellar,
+the total minutes of down time, etc.
+
+=item Unknown Data
+
+As mentioned earlier, the B<RRD> stores data at a constant
+interval. Sometimes it may happen that no new data is available when a
+value has to be written to the B<RRD>. Data acquisition may not be
+possible for one reason or other. With B<RRDtool> you can handle these
+situations by storing an I<*UNKNOWN*> value into the database. The
+value 'I<*UNKNOWN*>' is supported through all the functions of the
+tool. When consolidating a data set, the amount of I<*UNKNOWN*> data
+values is accounted for and when a new consolidated value is ready to
+be written to its Round Robin Archive (B<RRA>), a validity check is
+performed to make sure that the percentage of unknown values in the
+data point is above a configurable level. If not, an I<*UNKNOWN*> value
+will be written to the B<RRA>.
+
+=item Graphing
+
+B<RRDtool> allows you to generate reports in numerical and
+graphical form based on the data stored in one or several
+B<RRD>s. The graphing feature is fully configurable. Size, color and
+contents of the graph can be defined freely. Check L<rrdgraph>
+for more information on this.
+
+=item Aberrant Behavior Detection
+
+by Jake Brutlag
+
+B<RRDtool> provides the building blocks for near real-time aberrant
+behavior detection. These components include:
+
+=over
+
+=item *
+
+An algorithm for predicting the value of a time series one time step
+into the future.
+
+=item *
+
+A measure of deviation between predicted and observed values.
+
+=item *
+
+A mechanism to decide if and when an observed value or sequence of
+observed values is I<too deviant> from the predicted value(s).
+
+=back
+
+Here is a brief explanation of these components:
+
+The Holt-Winters time series forecasting algorithm is an on-line (or
+incremental) algorithm that adaptively predicts future observations in
+a time series. Its forecast is the sum of three components: a baseline
+(or intercept), a linear trend over time (or slope), and a seasonal
+coefficient (a periodic effect, such as a daily cycle). There is one
+seasonal coefficient for each time point in the period (cycle). After
+a value is observed, each of these components is updated via
+exponential smoothing. This means that the algorithm "learns" from
+past values and uses them to predict the future. The rate of
+adaptation is governed by 3 parameters, alpha (intercept), beta
+(slope), and gamma (seasonal). The prediction can also be viewed as a
+smoothed value for the time series.
+
+The measure of deviation is a seasonal weighted absolute
+deviation. The term I<seasonal> means deviation is measured separately
+for each time point in the seasonal cycle. As with Holt-Winters
+forecasting, deviation is predicted using the measure computed from
+past values (but only at that point in the seasonal cycle). After the
+value is observed, the algorithm learns from the observed value via
+exponential smoothing. Confidence bands for the observed time series
+are generated by scaling the sequence of predicted deviation values
+(we usually think of the sequence as a continuous line rather than a
+set of discrete points).
+
+Aberrant behavior (a potential failure) is reported whenever the
+number of times the observed value violates the confidence bands meets
+or exceeds a specified threshold within a specified temporal window
+(e.g. 5 violations during the past 45 minutes with a value observed
+every 5 minutes).
+
+This functionality is embedded in a set of related B<RRAs>. In
+particular, a FAILURES B<RRA> logs potential failures. With these data
+you could, for example, use a front-end application to B<RRDtool> to
+initiate real-time alerts.
+
+For a detailed description on how to set this up, see L<rrdcreate>.
+
+=back
+
+=head2 REMOTE CONTROL
+
+When you start B<RRDtool> with the command line option 'B<->' it waits
+for input via standard input (STDIN). With this feature you can
+improve performance by attaching B<RRDtool> to another process (MRTG
+is one example) through a set of pipes. Over these pipes B<RRDtool>
+accepts the same arguments as on the command line and some special
+commands like B<quit, cd, mkdir> and B<ls>. For detailed help on the
+server commands type:
+
+ rrdtool help cd|mkdir|pwd|ls|quit
+
+When a command is completed, RRDtool will print the string 'C<OK>',
+followed by timing information of the form B<u:>I<usertime>
+B<s:>I<systemtime>. Both values are the running totals of seconds since
+RRDtool was started. If an error occurs, a line of the form 'C<ERROR:>
+I<Description of error>' will be printed instead. B<RRDtool> will not abort,
+unless something realy serious happens. If
+a B<workdir> is specified and the UID is 0, RRDtool will do a chroot to that
+workdir. If the UID is not 0, RRDtool only changes the current directory to
+B<workdir>.
+
+=head2 RRD Server
+
+If you want to create a RRD-Server, you must choose a TCP/IP Service
+number and add them to I</etc/services> like this:
+
+ rrdsrv 13900/tcp # RRD server
+
+Attention: the TCP port 13900 isn't officially registered for
+rrdsrv. You can use any unused port in your services file, but the
+server and the client system must use the same port, of course.
+
+With this configuration you can add RRDtool as meta-server to
+I</etc/inetd.conf>. For example:
+
+ rrdsrv stream tcp nowait root /opt/rrd/bin/rrdtool rrdtool - /var/rrd
+
+Don't forget to create the database directory /var/rrd and
+reinitialize your inetd.
+
+If all was setup correctly, you can access the server with perl
+sockets, tools like netcat, or in a quick interactive test by using
+'telnet localhost rrdsrv'.
+
+B<NOTE:> that there is no authentication with this feature! Do not setup
+such a port unless you are sure what you are doing.
+
+=head1 SEE ALSO
+
+rrdcreate, rrdupdate, rrdgraph, rrddump, rrdfetch, rrdtune, rrdlast, rrdxport
+
+=head1 BUGS
+
+Bugs? Features!
+
+=head1 AUTHOR
+
+Tobias Oetiker <tobi@oetiker.ch>
+
diff --git a/program/doc/rrdtune.pod b/program/doc/rrdtune.pod
--- /dev/null
+++ b/program/doc/rrdtune.pod
@@ -0,0 +1,170 @@
+=head1 NAME
+
+rrdtune - Modify some basic properties of a Round Robin Database
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<tune> I<filename>
+S<[B<--heartbeat>|B<-h> I<ds-name>:I<heartbeat>]>
+S<[B<--minimum>|B<-i> I<ds-name>:I<min>]>
+S<[B<--maximum>|B<-a> I<ds-name>:I<max>]>
+S<[B<--data-source-type>|B<-d> I<ds-name>:I<DST>]>
+S<[B<--data-source-rename>|B<-r> I<old-name>:I<new-name>]>
+S<[B<--deltapos> I<scale-value>]>
+S<[B<--deltaneg> I<scale-value>]>
+S<[B<--failure-threshold> I<failure-threshold>]>
+S<[B<--window-length> I<window-length>]>
+S<[B<--alpha> I<adaption-parameter>]>
+S<[B<--beta> I<adaption-parameter>]>
+S<[B<--gamma> I<adaption-parameter>]>
+S<[B<--gamma-deviation> I<adaption-parameter>]>
+S<[B<--aberrant-reset> I<ds-name>]>
+
+=head1 DESCRIPTION
+
+The tune option allows you to alter some of the basic configuration
+values stored in the header area of a Round Robin Database (B<RRD>).
+
+One application of the B<tune> function is to relax the
+validation rules on an B<RRD>. This allows to fill a new B<RRD> with
+data available in larger intervals than what you would normally want
+to permit. Be very careful with tune operations for COMPUTE data sources.
+Setting the I<min>, I<max>, and I<heartbeat> for a COMPUTE data source
+without changing the data source type to a non-COMPUTE B<DST> WILL corrupt
+the data source header in the B<RRD>.
+
+A second application of the B<tune> function is to set or alter parameters
+used by the specialized function B<RRAs> for aberrant behavior detection.
+
+=over 8
+
+=item I<filename>
+
+The name of the B<RRD> you want to tune.
+
+=item S<B<--heartbeat>|B<-h> I<ds-name>:I<heartbeat>>
+
+modify the I<heartbeat> of a data source. By setting this to a high
+value the RRD will accept things like one value per day.
+
+=item S<B<--minimum>|B<-i> I<ds-name>:I<min>>
+
+alter the minimum value acceptable as input from the data source.
+Setting I<min> to 'U' will disable this limit.
+
+=item S<B<--maximum>|B<-a> I<ds-name>:I<max>>
+
+alter the maximum value acceptable as input from the data source.
+Setting I<max> to 'U' will disable this limit.
+
+=item S<B<--data-source-type>|B<-d> I<ds-name>:I<DST>>
+
+alter the type B<DST> of a data source.
+
+=item S<B<--data-source-rename>|B<-r> I<old-name>:I<new-name>>
+
+rename a data source.
+
+=item S<B<--deltapos> I<scale-value>>
+
+Alter the deviation scaling factor for the upper bound of the
+confidence band used internally to calculate violations for the
+FAILURES B<RRA>. The default value is 2. Note that this parameter is
+not related to graphing confidence bounds which must be specified as a
+CDEF argument to generate a graph with confidence bounds. The graph
+scale factor need not to agree with the value used internally by the
+FAILURES B<RRA>.
+
+=item S<B<--deltaneg> I<scale-value>>
+
+Alter the deviation scaling factor for the lower bound of the confidence band
+used internally to calculate violations for the FAILURES B<RRA>. The default
+value is 2. As with B<--deltapos>, this argument is unrelated to the scale
+factor chosen when graphing confidence bounds.
+
+=item S<B<--failure-threshold> I<failure-threshold>>
+
+Alter the number of confidence bound violations that constitute a failure for
+purposes of the FAILURES B<RRA>. This must be an integer less than or equal to
+the window length of the FAILURES B<RRA>. This restriction is not verified by
+the tune option, so one can reset failure-threshold and window-length
+simultaneously. Setting this option will reset the count of violations to 0.
+
+=item S<B<--window-length> I<window-length>>
+
+Alter the number of time points in the temporal window for determining
+failures. This must be an integer greater than or equal to the window
+length of the FAILURES B<RRA> and less than or equal to 28. Setting
+this option will reset the count of violations to 0.
+
+=item S<B<--alpha> I<adaption-parameter>>
+
+Alter the intercept adaptation parameter for the Holt-Winters
+forecasting algorithm. This parameter must be between 0 and 1.
+
+=item S<B<--beta> I<adaption-parameter>>
+
+Alter the slope adaptation parameter for the Holt-Winters forecasting
+algorithm. This parameter must be between 0 and 1.
+
+=item S<B<--gamma> I<adaption-parameter>>
+
+Alter the seasonal coefficient adaptation parameter for the SEASONAL
+B<RRA>. This parameter must be between 0 and 1.
+
+=item S<B<--gamma-deviation> I<adaption-parameter>>
+
+Alter the seasonal deviation adaptation parameter for the DEVSEASONAL
+B<RRA>. This parameter must be between 0 and 1.
+
+=item S<B<--aberrant-reset> I<ds-name>>
+
+This option causes the aberrant behavior detection algorithm to reset
+for the specified data source; that is, forget all it is has learnt so far.
+Specifically, for the HWPREDICT B<RRA>, it sets the intercept and slope
+coefficients to unknown. For the SEASONAL B<RRA>, it sets all seasonal
+coefficients to unknown. For the DEVSEASONAL B<RRA>, it sets all seasonal
+deviation coefficients to unknown. For the FAILURES B<RRA>, it erases
+the violation history. Note that reset does not erase past predictions
+(the values of the HWPREDICT B<RRA>), predicted deviations (the values of the
+DEVPREDICT B<RRA>), or failure history (the values of the FAILURES B<RRA>).
+This option will function even if not all the listed B<RRAs> are present.
+
+Due to the implementation of this option, there is an indirect impact on
+other data sources in the RRD. A smoothing algorithm is applied to
+SEASONAL and DEVSEASONAL values on a periodic basis. During bootstrap
+initialization this smoothing is deferred. For efficiency, the implementation
+of smoothing is not data source specific. This means that utilizing
+reset for one data source will delay running the smoothing algorithm
+for all data sources in the file. This is unlikely to have serious
+consequences, unless the data being collected for the non-reset data sources
+is unusually volatile during the reinitialization period of the reset
+data source.
+
+Use of this tuning option is advised when the behavior of the data source
+time series changes in a drastic and permanent manner.
+
+=back
+
+=head1 EXAMPLE 1
+
+C<rrdtool tune data.rrd -h in:100000 -h out:100000 -h through:100000>
+
+Set the minimum required heartbeat for data sources 'in', 'out'
+and 'through' to 10'000 seconds which is a little over one day in data.rrd.
+This would allow to feed old data from MRTG-2.0 right into
+RRDtool without generating *UNKNOWN* entries.
+
+=head1 EXAMPLE 2
+
+C<rrdtool tune monitor.rrd --window-length 5 --failure-threshold 3>
+
+If the FAILURES B<RRA> is implicitly created, the default
+window-length is 9 and the default failure-threshold is 7. This
+command now defines a failure as 3 or more violations in a temporal
+window of 5 time points.
+
+=head1 AUTHOR
+
+Tobias Oetiker <tobi@oetiker.ch>
+
diff --git a/program/doc/rrdtutorial.es.pod b/program/doc/rrdtutorial.es.pod
--- /dev/null
@@ -0,0 +1,1183 @@
+=head1 NAME
+
+rrdtutorial - Tutorial sobre RRDtool por Alex van den Bogaerdt
+(Traducido al castellano por Jesús Couto Fandiño)
+
+=for html <div align="right">Versión <a href="rrdtutorial.es.pdf">PDF</a></div>
+
+=for html <div align="right"><a href="rrdtutorial.html">English</a></div>
+
+=head1 DESCRIPTION / DESCRIPCIÓN
+
+RRDtool es un programa escrito por Tobias Oetiker con la
+colaboración de muchas personas en diversas partes del mundo. Alex van
+den Bogaerdt escribió este documento para ayudarte a entender que es
+RRDtool y que es lo que puede hacer por ti.
+
+La documentación que viene con RRDtool puede ser demasiado técnica
+para algunos. Este tutorial existe para ayudarte a entender las
+funciones básicas de RRdtool. Debe servirte de preparación para leer la
+documentación, y además explica algunas ideas generales sobre
+estadÃstica, con un enfoque particular hacia las redes.
+
+=head1 TUTORIAL
+
+=head2 Importante
+
+¡Por favor, no te adelantes en la lectura de este documento! Esta
+primera parte explica los fundamentos básicos. Puede ser aburrida,
+pero si te saltas los fundamentos, los ejemplos no te van a tener
+mucho sentido.
+
+=head2 ¿Qué es RRDtool?
+
+RRDtool significa "herramienta de bases de datos en round robin".
+"Round robin" es una técnica que implica un número fijo de datos, y un
+apuntador al elemento más reciente. Piensa en un circulo con unos
+cuantos puntos dibujados alrededor del borde; estos puntos son los
+lugares donde se pueden guardar los datos. Dibuja ahora una flecha
+desde el centro del cÃrculo a uno de los puntos; este es el apuntador.
+Cuando se lee o escribe el dato actualmente apuntado, la flecha se
+mueve al próximo elemento. Como estamos en un cÃrculo, no hay ni
+principio ni fin; siempre puedes seguir, eternamente. Al cabo de un
+tiempo ya se habrán usado todas las posiciones disponibles y el
+proceso empieza a reutilizar las antiguas. De esta forma, la base de datos
+no crece en tamaño y, por lo tanto, no requiere ningún mantenimiento.
+RRDtool trabaja con estas bases de datos en "round-robin", guardando y
+recuperando datos de ellas.
+
+=head2 ¿Qué datos pueden guardarse en una RRD?
+
+Lo que se te ocurra. Debes poder medir algún valor dado en distintos
+momentos en el tiempo y proveer a RRDtool de estos valores. Si puedes
+hacer esto, RRDtool puede guardar los datos. Los valores tienen que
+ser numéricos, pero no necesariamente enteros, como en MRTG.
+
+Muchos ejemplos mencionan SNMP, que es el acrónimo de
+"Simple Network Management Protocol" (Protocolo Simple de
+Administración de Redes). Lo de "simple" se refiere al protocolo - no
+se supone que sea fácil administrar o monitorizar una red. Cuando
+hayas terminado con este documento, deberás saber lo suficiente para
+entender cuando oigas a otros hablar sobre
+SNMP. Por ahora, simplemente considera a
+SNMP como una forma de preguntarle a los dispositivos
+por los valores de ciertos contadores que mantienen. Son estos valores
+de estos contadores los que vamos a almacenar en la RRD.
+
+=head2 ¿Qué puedo hacer con esta herramienta?
+
+RRDtool se deriva de MRTG (Multi Router
+Traffic Grapher, Graficador De Tráfico de Múltiples Enrutadores).
+MRTG empezó como un pequeño script para poder
+graficar el uso de una conexión a la Internet. Luego evolucionó,
+permitiendo graficar otras fuentes de datos, como temperatura,
+velocidad, voltajes, cantidad de páginas impresas, etc... Lo más
+probable es que empieces a usar RRDtool para guardar y procesar datos
+conseguidos a través de SNMP, y que los datos
+sean el número de bytes (o bits) transferidos desde y hacia una red u
+ordenador. RRDtool te permite crear una base de datos, guardar los
+datos en ellas, recuperarlos y crear gráficos en formato SVG o PNG,
+para mostrarlos en un navegador web. Esas imágenes dependen de los
+datos que hayas guardado y pueden, por ejemplo, ser un sumario del
+promedio de uso de la red, o los picos de tráfico que ocurrieron.
+También lo puedes usar para mostrar el nivel de las mareas, la
+radiación solar, el consumo de electricidad, el número de visitantes
+en una exposición en un momento dado, los niveles de ruido cerca del
+aeropuerto, la temperatura en tu lugar de vacaciones favorito, o en
+la nevera, o cualquier otra cosa que te puedas imaginar, mientras
+tengas algún sensor con el cual medir los datos y seas capaz de
+pasarle los números a RRDtool.
+
+=head2 ¿Y si aún tengo problemas después de leer este documento?
+
+Lo primero, ¡léelo otra vez!. Puede que te hayas perdido de algo.
+Si no puedes compilar el código fuente y usas un sistema operativo
+bastante común, casi seguro que no es la culpa de RRDtool.
+Probablemente consigas versiones pre-compiladas por la Internet. Si
+provienen de una fuente confiable, úsalas. Si, por otro lado, el
+programa funciona, pero no te da los resultados que tu esperabas,
+puede ser un problema con la configuración; revÃsala y
+compárala con los ejemplos.
+
+Hay una lista de correo electrónico y una archivo de la misma. Lee
+la lista durante unas cuantas semanas, y busca en el archivo. Es
+descortés hacer una pregunta sin haber revisado el archivo; ¡puede que
+tu problema ya haya sido resuelto antes! Normalmente ocurre asà en todas
+las listas de correo, no sólo esta. Examina la documentación que vino
+con RRDtool para ver donde está el archivo y como usarlo.
+
+Te sugiero que te tomes un momento y te subscribas a la lista ahora
+mismo, enviando un mensaje a rrd-users-request@lists.oetiker.ch
+con tÃtulo C<subscribe>. Si eventualmente deseas salirte de la lista,
+envÃa otro correo a la misma dirección, con tÃtulo C<unsubscribe>.
+
+=head2 ¿Cómo me vas a ayudar?
+
+Dándote descripciones y ejemplos detallados. Asumimos que el seguir
+las instrucciones en el orden en que se presentan aquà te dará
+suficiente conocimiento de RRDtool como para que experimentes por tu
+cuenta. Si no funciona a la primera, puede que te hallas saltado algo;
+siguiendo los ejemplos obtendrás algo de experiencia práctica y, lo
+que es más importante, un poco de información sobre como funciona el
+programa.
+
+Necesitarás saber algo sobre números hexadecimales. Si no, empieza
+por leer "bin_dec_hex" antes de continuar.
+
+=head2 Tu primera base de datos en round-robin
+
+En mi opinión, la mejor forma de aprender algo es haciéndolo. ¿Por
+qué no empezamos ya? Vamos a crear una base de datos, poner unos cuantos
+valores en ella y extraerlos después. La salida que obtengas debe ser
+igual a la que aparece en este documento.
+
+Empezaremos con algo fácil, comparando un coche con un enrutador, o
+por decirlo de otra forma, comparando kilómetros con bits y bytes. A
+nosotros nos da lo mismo; son unos números obtenidos en un espacio de tiempo.
+
+Asumamos que tenemos un dispositivo que transfiere bytes desde y
+hacia la Internet. Este dispositivo tiene un contador que empieza en 0
+al encenderse y se incrementa con cada byte transferido. Este contador
+tiene un valor máximo; si ese valor se alcanza y se cuenta un byte
+más, el contador vuelve a empezar desde cero. Esto es exactamente lo
+mismo que pasa con muchos contadores, como el cuentakilómetros del
+coche. En muchas de las disertaciones sobre redes se habla de bits por
+segundo, asà que empezaremos por acostumbrarnos a esto. Asumamos que un
+byte son 8 bits y empecemos a pensar en bits y no en bytes. ¡El
+contador, sin embargo, sigue contando en bytes! En el mundo
+SNMP, la mayorÃa de los contadores tienen una
+longitud de 32 bits. Esto significa que pueden contar desde 0 hasta
+4294967295. Usaremos estos valores en los ejemplos. El dispositivo, cuando
+le preguntamos, retorna el valor actual del contador. Como sabemos el
+tiempo transcurrido desde la última vez que le preguntamos, sabemos
+cuantos bytes se han transferido C<***en promedio***> por
+segundo. Esto no es muy difÃcil de calcular; primero en palabras,
+luego en operaciones:
+
+=over 4
+
+=item 1.
+
+Toma el valor actual del contador y réstale el valor anterior
+
+=item 2.
+
+Haz lo mismo con la fecha
+
+=item 3.
+
+Divide el resultado del paso (1) por el resultado del paso (2).
+El resultado es la cantidad de bytes por segundo. Si lo
+multiplicas por ocho obtienes la cantidad de bits por segundo
+
+=back
+
+ bps = (contador_actual - contador_anterior) / (fecha_actual - fecha_anterior) * 8
+
+Para algunos será de ayuda traducir esto a un ejemplo automotor.
+No prueben estas velocidades en la práctica, y si lo hacen, no me
+echen la culpa por los resultados.
+
+Usaremos las siguientes abreviaturas:
+
+ M: metros
+ KM: kilómetros (= 1000 metros).
+ H: horas
+ S: segundos
+ KM/H: kilómetros por hora
+ M/S: metros por segundo
+
+
+Vas conduciendo un coche. A las 12:05, miras el contador en el
+salpicadero y ves que el coche ha recorrido 12345
+KM. A las 12:10 vuelves a mirar otra vez, y dice
+12357 KM. Quiere decir, que has recorrido 12
+KM en cinco minutos. Un cientÃfico convertirÃa
+esto en metros por segundos; esto es bastante parecido al problema de
+pasar de bytes transferidos en 5 minutos a bits por segundo.
+
+Viajamos 12 kilómetros, que son 12000 metros. Tardamos 5 minutos, o
+sea 300 segundos. Nuestra velocidad es 12000M / 300S igual a 40 M/S.
+
+También podemos calcular la velocidad en KM/H: 12 veces 5 minutos
+es una hora, asà que multiplicando los 12 KM por 12 obtenemos 144
+KM/H. No intentes esto en casa, o por donde vivo :-)
+
+Recuerda que estos números son tan sólo promedios. No hay forma de
+deducir, viendo sólo los números, si fuiste a una velocidad constante.
+Hay un ejemplo más adelante en el tutorial que explica esto.
+
+Espero que entiendas que no hay diferencia entre calcular la
+velocidad en M/S o bps; sólo la forma en que
+recogemos los datos es distinta. Inclusive, la K de kilo en este
+caso es exactamente la misma, ya que en redes k es 1000
+
+Ahora vamos a crear una base de datos en la que guardar todos estos
+interesantes valores. El método a usar para arrancar el programa puede
+variar de un sistema de operación a otro, pero asumamos que lo puedes
+resolver tu mismo en caso que se diferente en el sistema que usas.
+Asegúrate de no sobreescribir ningún archivo en tu sistema al
+ejecutarlo y escribe todo como una sola lÃnea (tuve que partirlo para
+que fuera legible), saltándote todos los caracteres '\'
+
+ rrdtool create test.rrd \
+ --start 920804400 \
+ DS:speed:COUNTER:600:U:U \
+ RRA:AVERAGE:0.5:1:24 \
+ RRA:AVERAGE:0.5:6:10
+
+(o sea, escribe: C<rrdtool create test.rrd --start 920804400 DS ...>)
+
+=head2 ¿Qué hemos creado?
+
+Hemos creado una base de datos en round robin llamada test
+(test.rrd), que empieza desde el mediodÃa del dÃa en que empecé a
+escribir este documento (7 de marzo de 1999). En ella se guarda una
+fuente de datos (DS), llamada "speed", que se
+lee de un contador. En la misma base de datos se guardan dos archivos
+en round robin (RRAs), uno promedia los datos cada vez que se leen (o
+sea, no hay nada que promediar), y mantiene 24 muestras (24 por 5
+minutos = 2 horas de muestras). El otro promedia 6 muestras (media
+hora), y guarda 10 de estos promedios (o sea, 5 horas). Las opciones
+restantes las veremos más adelante.
+
+RRDtool usa un formato de "fecha" especial que viene del mundo de
+UNIX. Estas "fechas" son el número de segundos
+que han pasado desde el primero de enero de 1970, zona UTC. Este
+número de segundos se convierte luego en la fecha local, por lo que
+varia según la franja horaria.
+
+Lo más probable es que tu no vivas en la misma parte del mundo que
+yo, por lo que tu franja horaria será diferente. En los ejemplos,
+cuando mencione horas, puede que no sean las mismas para ti; esto no
+afecta mucho los resultados, sólo tienes que corregir las horas
+mientras lees. Por ejemplo, las 12:05 para mà son las 11:05 para los
+amigos en la Gran Bretaña.
+
+Ahora tenemos que llenar nuestra base de datos con valores. Vamos a
+suponer que leÃmos estos datos:
+
+ 12:05 12345 KM
+ 12:10 12357 KM
+ 12:15 12363 KM
+ 12:20 12363 KM
+ 12:25 12363 KM
+ 12:30 12373 KM
+ 12:35 12383 KM
+ 12:40 12393 KM
+ 12:45 12399 KM
+ 12:50 12405 KM
+ 12:55 12411 KM
+ 13:00 12415 KM
+ 13:05 12420 KM
+ 13:10 12422 KM
+ 13:15 12423 KM
+
+Llenaremos la base de datos asÃ:
+
+ rrdtool update test.rrd 920804700:12345 920805000:12357 920805300:12363
+ rrdtool update test.rrd 920805600:12363 920805900:12363 920806200:12373
+ rrdtool update test.rrd 920806500:12383 920806800:12393 920807100:12399
+ rrdtool update test.rrd 920807400:12405 920807700:12411 920808000:12415
+ rrdtool update test.rrd 920808300:12420 920808600:12422 920808900:12423
+
+Lo que significa: actualiza nuestra base de datos test con los
+siguientes valores:
+
+ fecha 920804700, valor 12345
+ fecha 920805000, valor 12357
+
+ etcétera.
+
+Como ves, pueden introducirse más de un valor en la base de datos
+por ejecución del comando. Yo los agrupo de tres en tres para hacerlo
+legible, pero en realidad el máximo depende del sistema de operación.
+
+Ahora podemos recuperar los datos usando ``rrdtool fetch'':
+
+ rrdtool fetch test.rrd AVERAGE --start 920804400 --end 920809200
+
+Debes obtener esto como salida:
+
+ speed
+
+ 920804400: NaN
+ 920804700: NaN
+ 920805000: 4.0000000000e-02
+ 920805300: 2.0000000000e-02
+ 920805600: 0.0000000000e+00
+ 920805900: 0.0000000000e+00
+ 920806200: 3.3333333333e-02
+ 920806500: 3.3333333333e-02
+ 920806800: 3.3333333333e-02
+ 920807100: 2.0000000000e-02
+ 920807400: 2.0000000000e-02
+ 920807700: 2.0000000000e-02
+ 920808000: 1.3333333333e-02
+ 920808300: 1.6666666667e-02
+ 920808600: 6.6666666667e-03
+ 920808900: 3.3333333333e-03
+ 920809200: NaN
+
+Si no, hay algo mal. Probablemente tu sistema de operación muestre ``NaN''
+de otra forma; representa "Not a Number", o sea "No es un número". Si
+aparece ``U'' o ``UNKN'' o algo parecido, es lo mismo. Si hay alguna otra
+diferencia, probablemente te equivocaste al introducir algún P valor
+(asumiendo que mi tutorial está bien, por supuesto :-). En ese caso, borra
+la base de datos y prueba de nuevo.
+
+Lo que representa exactamente esta salida lo vamos más adelante en el tutorial.
+
+=head2 Hora de hacer algunos gráficos
+
+Prueba este comando:
+
+ rrdtool graph speed.png \
+ --start 920804400 --end 920808000 \
+ DEF:myspeed=test.rrd:speed:AVERAGE \
+ LINE2:myspeed#FF0000
+
+Este comando crea speed.png, un gráfico de los datos desde las
+12:00 hasta las 13:00. Contiene una definición de la variable myspeed
+y define el color como rojo. Notarás que el gráfico no comienza
+exactamente a las 12:00 sino a las 12:05, y es porque no tenemos datos
+suficientes como para calcular el promedio de velocidad antes de ese
+momento. Esto sólo ocurre en caso de que se pierdan algún muestreo, lo
+que esperamos que no debe ocurrir muy a menudo.
+
+Si ha funcionado, ¡felicitaciones!. Si no, revisa qué puede estar mal.
+
+La definición de colores se construye a partir del rojo, verde y
+azul. Especificas cuanto de cada uno de estos componentes vas a usar
+en hexadecimal: 00 significa "nada de este color" y FF significa
+"este color a máxima intensidad". El "color" blanco es la mezcla
+del rojo, verde y azul a toda intensidad:
+FFFFFF; el negro es la ausencia de todos los colores: 000000.
+
+ rojo #FF0000
+ verde #00FF00
+ azul #0000FF
+ violeta #FF00FF (mezcla de rojo y azul)
+ gris #555555 (un tercio de cada uno de los colores)
+
+El archivo PNG que acabas de crear puede
+verse con tu visor de archivos de imagen favorito. Los navegadores lo
+mostrarán usando la URL
+``file://el/camino/de/directorios/hasta/speed.png''
+
+=head2 Gráficos con un poco de matemática
+
+Cuando veas la imagen, notarás que el eje horizontal tiene unas
+etiquetas marcando las 12:10, 12:20, 12:30, 12:40 y 12:50. Los otros
+dos momentos (12:00 y 13:00) no se pueden mostrar bien por falta de datos, asà que
+el programa se los salta. El eje vertical muestra el rango de los valores que
+entramos. Introdujimos los kilómetros y luego dividimos entre 300
+segundos, por lo que obtuvimos valores bastante bajos. Para ser
+exactos, el primer valor, 12 (12357-12345), dividido entre 300 da
+0.04, lo que RRDtool muestra como ``40m'', o sea ``40/1000''. ¡La
+``m''' no tiene nada que ver con metros, kilómetros o milÃmetros!.
+RRDtool no sabe nada de unidades, el sólo trabaja con números, no con
+metros.
+
+Donde nos equivocamos fue en que debimos medir en metros. AsÃ,
+(12357000-12345000)/300 = 12000/300 = 40.
+
+Vamos a corregirlo. PodrÃamos recrear la base de datos con los
+valores correctos, pero hay una forma mejor: ¡haciendo los cálculos
+mientras creamos el archivo png!
+
+ rrdtool graph speed2.png \
+ --start 920804400 --end 920808000 \
+ --vertical-label m/s \
+ DEF:myspeed=test.rrd:speed:AVERAGE \
+ CDEF:realspeed=myspeed,1000,* \
+ LINE2:realspeed#FF0000
+
+Cuando veas esta imagen, notarás que la ``m'' ha desaparecido, y
+ahora tienes los resultados correctos. Además hemos añadido una
+etiqueta a la imagen. Apartando esto, el archivo PNG es el mismo.
+
+Las operaciones están en la sección del CDEF
+y están escritas en Notación Polaca Inversa (Reverse Polish Notation o
+``RPN''). En palabras, dice: "toma la fuente de
+datos myspeed y el numero 1000, y multiplÃcalos". No te molestes en
+meterte con RPN todavÃa, la veremos con más
+detalle más adelante. Además, puede que quieras leer mi tutorial sobre
+los CDEF y el tutorial de Steve Rader sobre RPN, pero primero terminemos con este.
+
+¡Un momento! Si podemos multiplicar los valores por mil, entonces,
+¡también deberÃa ser posible el mostrar la velocidad en kilómetros por
+hora usando los mismos datos!
+
+Para cambiar el valor que medimos en metros por segundo, calculamos
+los metros por hora (valor * 3600) y dividimos entre 1000 para sacar
+los kilómetros por hora. Todo junto hace valor * (3600/1000) == valor
+* 3.6.
+
+Como en nuestra base de datos cometimos un error guardando los
+valores en kilómetros, debemos compensar por ello, multiplicando por
+100, por lo que al aplicar esta corrección nos queda valor * 3600.
+
+Ahora vamos a crear este png, agreándole un poco más de magia...
+
+ rrdtool graph speed3.png \
+ --start 920804400 --end 920808000 \
+ --vertical-label km/h \
+ DEF:myspeed=test.rrd:speed:AVERAGE \
+ "CDEF:kmh=myspeed,3600,*" \
+ CDEF:fast=kmh,100,GT,kmh,0,IF \
+ CDEF:good=kmh,100,GT,0,kmh,IF \
+ HRULE:100#0000FF:"Maximum allowed" \
+ AREA:good#00FF00:"Good speed" \
+ AREA:fast#FF0000:"Too fast"
+
+Esto luce mucho mejor. La velocidad en KM/H,
+y además tenemos una lÃnea extra mostrando la velocidad máxima
+permitida (en el camino por donde conduzco). También le cambie los
+colores de la velocidad, y ahora paso de ser una lÃnea a un área.
+
+Los cálculos son más complejos ahora. Para calcular la velocidad "aceptable":
+
+ Verifica si la velocidad en kmh es mayor que 100 ( kmh,100 ) GT
+ Si es asÃ, retorna 0, si no, retorna la velocidad ((( kmh,100 ) GT ), 0, kmh) IF
+
+Para calcular la parte de velocidad "excesiva":
+
+ Verifica si la velocidad en kmh es mayor que 100 ( kmh,100 ) GT
+ Si es asÃ, retorna la velocidad, si no, retorna 0 ((( kmh,100) GT ), kmh, 0) IF
+
+=head2 Magia gráfica
+
+Me gusta creer que virtualmente no hay limites para lo que RRDtool puede
+hacer con los datos. No voy a explicarlo en detalle, pero mira este PNG:
+
+ rrdtool graph speed4.png \
+ --start 920804400 --end 920808000 \
+ --vertical-label km/h \
+ DEF:myspeed=test.rrd:speed:AVERAGE \
+ "CDEF:kmh=myspeed,3600,*" \
+ CDEF:fast=kmh,100,GT,100,0,IF \
+ CDEF:over=kmh,100,GT,kmh,100,-,0,IF \
+ CDEF:good=kmh,100,GT,0,kmh,IF \
+ HRULE:100#0000FF:"Maximum allowed" \
+ AREA:good#00FF00:"Good speed" \
+ AREA:fast#550000:"Too fast" \
+ STACK:over#FF0000:"Over speed"
+
+Vamos a crear una página HTML simple para ver los tres archivos PNG:
+
+ <HTML><HEAD><TITLE>Velocidad</TITLE></HEAD><BODY>
+ <IMG src="speed2.png" alt="Speed in meters per second">
+ <BR>
+ <IMG src="speed3.png" alt="Speed in kilometers per hour">
+ <BR>
+ <IMG src="speed4.png" alt="Traveled too fast?">
+ </BODY></HTML>
+
+Guárdalo como ``speed.html'' o algo parecido, y examÃnalo con un navegador.
+
+Ahora, todo lo que tienes que hacer es medir los datos regularmente
+y actualizar la base de datos. Cuando quieras verlos, vuelve a crear
+los archivos PNG y asegúrate que se carguen de nuevo en tu navegador
+(Nota: presionar el botón de "refrescar" puede no ser suficiente; en
+particular, Netscape tiene un problema al respecto, por lo que
+necesitaras darle al botón mientras presionas la tecla de mayúsculas.
+
+=head2 Actualizaciones de verdad
+
+Ya hemos usado el comando ``update''; vimos que recibia uno o más
+parámetros en el formato: ``E<lt>fechaE<gt>:E<lt>valorE<gt>''. Para
+facilitarte las cosas, puedes obtener la fecha actual colocando
+``N'' en la fecha. También podrÃas usar la función
+``time'' de Perl para obtenerla. El ejemplo más corto de todo el
+tutorial :)
+
+ perl -e 'print time, "\n" '
+
+Ahora, la forma de poner a correr un programa a intervalos
+regulares de tiempo depende del sistema de operación. La
+actualización, en pseudo-código, serÃa:
+
+ Toma el valor, colócalo en la variable "$speed"
+ rrdtool update speed.rrd N:$speed
+
+(Pero no lo hagas sobre nuestra base de datos de pruebas, que aún
+la vamos a usar en otros ejemplos.
+
+Eso es todo. Ejecutando este script cada 5 minutos, lo único que
+tienes que hacer para ver los gráficos actuales es correr los ejemplos
+anteriores, que también puedes poner en un script. Luego de correrlo,
+basta con cargar index.html
+
+=head2 Unas palabras sobre SNMP
+
+Me imagino que muy pocas personas serán capaces de obtener en su
+ordenador datos reales de su coche cada 5 minutos; los demás nos
+tendremos que conformar con algún otro contador. Puedes, por ejemplo,
+medir la cantidad de páginas que ha hecho una impresora, cuanto café
+has hecho con la cafetera, el medidor del consumo de electricidad, o
+cualquier otra cosa. Cualquier contador incremental puede
+monitorizarse y graficarse con lo que has aprendido hasta ahora. Más
+adelante, veremos también como monitorizar otro tipo de valores, como
+la temperatura. La mayorÃa usaremos alguna vez un contador que lleve
+la cuenta de cuantos octetos (bytes) a transferido un dispositivo de
+red, asà que vamos a ver como hacer esto. Empezaremos describiendo
+como recoger los datos. Hay quien dirá que hay herramientas que pueden
+recoger estos datos por ti. ¡Es cierto! Pero, creo que es importante
+darse cuenta de que no son necesarias. Cuando tienes que determinar
+porqué algo no funciona, necesitas saber cómo funciona en primer lugar.
+
+Una herramienta que mencionamos brevemente al principio del
+documento es SNMP. SNMP es una forma de comunicarse con tus equipos.
+La herramienta particular que voy a usar más adelante se llama
+``snmpget'', y funciona asÃ:
+
+ snmpget dispositivo clave OID
+
+En "dispositivo" colocas el nombre o dirección IP del equipo a
+monitorizar. En clave, colocas la "cadena de caracteres de la
+comunidad de lectura", como se le denomina en el mundillo SNMP.
+Muchos dispositivos aceptarán "public" como
+cadena por defecto, pero por razones de privacidad y seguridad esta
+clave puede estar deshabilitada. Consulta la documentación
+correspondiente al dispositivo o programa.
+
+Luego esta el tercer parámetro, llamado OID
+(Object IDentifier, identificador de objeto).
+
+Al principio, cuando empiezas a aprender sobre SNMP, parece muy
+confuso. No lo es tanto cuando le hechas una ojeada a los
+``MIB'' (Manager Information Base, o Base de
+Información Administrativa). Es un árbol invertido que describe los
+datos, empezando en un nodo raÃz desde el que parten varias ramas.
+Cada rama termina en otro nodo y puede abrir nuevas sub-ramas. Cada
+rama tiene un nombre, y forman un camino que nos lleva hasta el fondo
+del árbol. En este ejemplo, las ramas que vamos a tomar se llaman iso,
+org, dod, internet, mgmt y mib-2. También pueden accederse por su
+número relativo; en este caso, estos números son 1, 3, 6, 1, 2 y 1:
+
+ iso.org.dod.internet.mgmt.mib-2 (1.3.6.1.2.1)
+
+En algunos programas se usa un punto al iniciar el OID. Esto puede
+ser confuso; no hay ningún punto inicial en la especificación de los
+OID... sin embargo, algunos programas usan por defecto un prefijo
+inicial. Para indicar la diferencia entre los OID abreviados (o sea, a
+los que se le pondrá el prefijo inicial) y los completos, estos
+programas necesitan que los OID completos empiecen por un punto. Para
+empeorar las cosas, se usan varios prefijos distintos...
+
+De acuerdo, sigamos con el inicio de nuestro OID: tenÃamos
+1.3.6.1.2.1 . Ahora, nos interesa la rama ``interfaces'', que tiene el
+número dos (o sea, 1.3.6.1.2.1.2, o 1.3.6.1.2.1.interfaces).
+
+Lo primero es hacernos con un programa SNMP. Busca algún
+paquete pre-compilado para tu plataforma, si no, puedes
+buscar el código fuente y compilarlo tu mismo. En Internet encontrarás
+muchos programas, búscalos con un motor de búsqueda o como prefieras.
+Mi sugerencia es que busques el paquete CMU-SNMP, que esta bastante difundido.
+
+Asumamos que ya tienes el programa. Empecemos por tomar ciertos
+datos que están disponibles en la mayorÃa de los sistemas. Recuerda:
+hay un nombre abreviado para la parte del árbol que más nos interesa.
+
+Voy a usar la versión corta, ya que creo que este documento ya es
+lo bastante largo. Si no te funciona, añádele el prefijo .1.3.6.1.2.1
+y prueba de nuevo. O prueba leyendo el manual; sáltate las partes que
+no entiendas aún, y busca las secciones que hablan de como arrancar y
+usar el programa.
+
+ snmpget myrouter public system.sysdescr.0
+
+El dispositivo deberá contestarte con una descripción, probablemente
+vacÃa, de sà mismo. Si no consigues una respuesta válida, prueba con
+otra "clave" u otro dispositivo; no podemos seguir hasta tener un
+resultado.
+
+ snmpget myrouter public interfaces.ifnumber.0
+
+Con suerte, usando este comando obtendrás un número como resultado:
+el número de interfaces del dispositivo. Si es asÃ, seguiremos
+adelante con otro programa, llamado "snmpwalk"
+
+ snmpwalk myrouter public interfaces.iftable.ifentry.ifdescr
+
+Si obtienes una lista de interfaces, ya casi hemos llegado. AquÃ
+tienes un ejemplo del resultado:
+
+ [user@host /home/alex]$ snmpwalk cisco public 2.2.1.2
+ interfaces.ifTable.ifEntry.ifDescr.1 = "BRI0: B-Channel 1"
+ interfaces.ifTable.ifEntry.ifDescr.2 = "BRI0: B-Channel 2"
+ interfaces.ifTable.ifEntry.ifDescr.3 = "BRI0" Hex: 42 52 49 30
+ interfaces.ifTable.ifEntry.ifDescr.4 = "Ethernet0"
+ interfaces.ifTable.ifEntry.ifDescr.5 = "Loopback0"
+
+En este equipo CISCO, quiero monitorizar la interfaz "Ethernet0".
+Viendo que es la cuarta, pruebo con:
+
+ [user@host /home/alex]$ snmpget cisco public 2.2.1.10.4 2.2.1.16.4
+
+ interfaces.ifTable.ifEntry.ifInOctets.4 = 2290729126
+ interfaces.ifTable.ifEntry.ifOutOctets.4 = 1256486519
+
+Entonces, tengo 2 OIDs que monitorizar, y son (en el formato largo, ahora):
+
+ 1.3.6.1.2.1.2.2.1.10
+
+ y
+
+ 1.3.6.1.2.1.2.2.1.16
+
+, ambas con el número de interfaz de 4
+
+No te engañes, esto no lo logre yo al primer intento. Me tomó un
+tiempo entender lo que significaban todos estos números; ayuda cuando
+se traducen en un texto descriptivo... por lo menos, cuando oigas
+hablar de MIBs y OIDs, ahora sabrás de qué se trata. No te olvides
+del número de interfaz (0 si el valor no depende de una interfaz), y
+prueba con snmpwalk si no obtienes una respuesta clara con snmpget.
+
+Si entendiste todo esto, y obtienes resultados del dispositivo con
+el que estás probando, sigue adelante con el tutorial. Si no, vuelve a
+leer esta sección; es importante
+
+=head2 Un ejemplo real
+
+Ok, empecemos con la diversión. Primero, crea una base de datos
+nueva. Vamos a guardar en ella 2 contadores, "input" y "ouput". Los
+datos los vamos a guardar en archivos que los promediarán, tomando
+grupos de 1, 6, 24 o 288 muestras. También archivaremos los valores
+máximos. Lo explicaremos con más detalle después. El intervalo de
+tiempo entre las muestras será de 300 segundos (5 minutos).
+
+ 1 muestra "promediada" sigue siendo 1 muestra cada 5 minutos
+ 6 muestras promediadas son un promedio de cada 30 minutos
+ 24 muestras promediadas son un promedio de cada 2 horas
+ 288 muestras promediadas son un promedio de cada dÃa
+
+Vamos a tratar de ser compatibles con MRTG, que guarda más o menos
+esta cantidad de datos:
+
+ 600 muestras de 5 minutos: 2 dÃas y 2 horas
+ 600 promedios de 30 minutos: 12.5 dÃas
+ 600 promedios de 2 horas: 50 dÃas
+ 600 promedios de 1 dÃa: 732 dÃas
+
+Uniendo todos estos rangos tenemos que en total guardamos datos de
+unos 797 dÃas. RRDtool guarda los datos de una forma distinta a MRTG;
+no empieza el archivo "semanal" donde acaba el "diario", sino que
+ambos archivos contienen la información más reciente, ¡por lo que con
+RRDtool archivamos más datos que con MRTG!
+
+Necesitaremos:
+
+ 600 muestras de 5 minutos (2 dÃas y 2 horas)
+ 700 entradas de 30 minutos (2 dÃas y 2 horas, más 12.5 dÃas)
+ 775 entradas de 2 horas (lo anterior + 50 dÃas)
+ 797 entradas de 1 dÃa (lo anterior + 732 dÃas, redondeando)
+
+ rrdtool create myrouter.rrd \
+ DS:input:COUNTER:600:U:U \
+ DS:output:COUNTER:600:U:U \
+ RRA:AVERAGE:0.5:1:600 \
+ RRA:AVERAGE:0.5:6:700 \
+ RRA:AVERAGE:0.5:24:775 \
+ RRA:AVERAGE:0.5:288:797 \
+ RRA:MAX:0.5:1:600 \
+ RRA:MAX:0.5:6:700 \
+ RRA:MAX:0.5:24:775 \
+ RRA:MAX:0.5:288:797
+
+Lo siguiente es recoger los datos y guardarlos, como en el ejemplo
+siguiente. Esta parcialmente en pseudo-código, por lo que tendrás que
+buscar exactamente como hacerlo funcionar en tu sistema operativo.
+
+ mientras no sea el fin del universo
+ hacer
+ tomar el resultado de
+ snmpget router community 2.2.1.10.4
+ en la variable $in
+ tomar el resultado de
+ snmpget router community 2.2.1.16.4
+ en la variable $out
+ rrdtool update myrouter.rrd N:$in:$out
+ esperar 5 minutos
+ hecho
+
+Luego, tras recoger datos por un dÃa, crea una imagen, usando:
+
+ rrdtool graph myrouter-day.png --start -86400 \
+ DEF:inoctets=myrouter.rrd:input:AVERAGE \
+ DEF:outoctets=myrouter.rrd:output:AVERAGE \
+ AREA:inoctets#00FF00:"In traffic" \
+ LINE1:outoctets#0000FF:"Out traffic"
+
+Este comando debe producir un gráfico del tráfico del dÃa. Un dÃa
+son 24 horas, de 60 minutos, de 60 segundos: 24*60*60=86400, o sea que
+empezamos a "ahora" menos 86400 segundos. Definimos (con los DEFs)
+"inoctets" y "outoctets" como los valores promedio de la base da datos
+myrouter.rrd, dibujando un área para el tráfico de entrada y una lÃnea
+para el tráfico de salida.
+
+Mira la imagen y sigue recogiendo datos por unos cuantos dÃas. Si
+lo deseas, puedes probar con los ejemplos de la base de datos de
+pruebas y ver si puedes hacer trabajar las diversas opciones y
+operaciones.
+
+Sugerencia:
+
+Haz un gráfico que muestre el tráfico en bytes por segundo y en
+bits por segundo. Colorea el tráfico Ethernet rojo si sobrepasa los
+cuatro megabits por segundo.
+
+=head2 Funciones de consolidación
+
+Unos cuantos párrafos atrás hablábamos sobre la posibilidad de
+guardar el valor máximo en vez del promedio. Profundicemos un poco en
+este tema.
+
+Recordemos lo que hablábamos sobre la velocidad de un coche.
+Supongamos que manejamos a 144 KM/H durante 5
+minutos y luego nos detiene la policÃa durante unos 25 minutos. Al
+finalizar el regaño, tomamos nuestro portátil y creamos una imagen
+desde nuestra base de datos. Si visualizamos la segunda RRA que
+creamos, tendremos el promedio de 6 muestreos. Las velocidades
+registradas serian 144+0+0+0+0+0=144, lo que en promedio nos da una
+velocidad de 24 KM/H., con lo que nos igual nos
+pondrÃan una multa, sólo que no por exceso de velocidad.
+
+Obviamente, en este caso, no deberÃamos tomar en cuenta los
+promedios. Estos son útiles en varios casos. Por ejemplo, si queremos
+ver cuantos KM hemos viajado, este serÃa el
+gráfico más indicado. Pero por otro lado, para ver la velocidad ha la
+que hemos viajado, los valores máximos son más adecuados.
+
+Es lo mismo con los datos que recogemos. Si quieres saber la
+cantidad total, mira los promedios. Si quieres ver la velocidad, mira
+los máximos. Con el tiempo, ambas cantidades se separan cada vez más.
+En la última base de datos que creamos, habÃa dos archivos que
+guardaban los datos de cada dÃa. El archivo que guarda los promedios
+mostrará valores bajos, mientras que el de máximos mostrará valores más
+altos. Para mi coche, mostrarÃa valores promedio de 96/24=4 KM/H
+(viajo unos 96 kilómetros por dÃa), y máximos de 1220 KM/H (la
+velocidad máxima que alcanzo cada dÃa)
+
+Como ves, una gran diferencia. No mires el segundo gráfico para
+estimar la distancia que recorro, ni al primero para estimar la
+velocidad a la que voy. Esto sólo funciona con muestras muy cercanas,
+pero no si sacas promedios.
+
+Algunas veces, hago un viaje largo. Si hago un recorrido por
+Europa, conduciendo por unas 12 horas, el primer gráfico subirá
+a unos 60 KM/H. El segundo mostrará unos 180 KM/H. Esto significa que
+recorrà unos 60 KM/H por 24 horas = 1440 KM. Muestra además que fui a
+una velocidad promedio mayor a la normal y a un máximo de 180 KM/H,
+¡no que fui 8 horas a una velocidad fija de 180 KM/H! Este es un
+ejemplo real: tengo que seguir la corriente en las autopistas de
+Alemania, detenerme por gasolina y café de vez en cuando, manejar más
+lentamente por Austria y Holanda, e ir con cuidado en las montañas y
+las villas. Si viéramos los gráficos de los promedios de cada 5
+minutos, la imagen serÃa completamente distinta; verÃamos los mismos
+valores de promedio y de máxima. (suponiendo que las mediciones fueran
+cada 300 segundos). Se podrÃa ver cuando paré, cuando iba en
+primera, cuando iba por las autopistas, etc. La granularidad de los
+datos es más alta, por lo que se tiene más información. Sin embargo,
+esto nos lleva unas 12 muestras por hora, o 288 al dÃa, lo cual es
+mucho para guardar por un periodo de tiempo largo. Por lo tanto,
+sacamos el promedio, guardando eventualmente un solo valor por dÃa.
+Con este único valor, no podemos ver mucho.
+
+Es importante comprender lo que expuesto en estos últimos párrafos.
+Unos ejes y unas lÃneas no tienen ningún valor por si mismos; hay que
+saber que representan e interpretar correctamente los valores
+obtenidos. Sean cuales sean los datos, esto siempre será cierto.
+
+El mayor error que puedes cometer es usar los datos recogidos para
+algo para lo cual no sirven. En ese caso, seria hasta mejor no tener
+gráfico alguno.
+
+=head2 Repasemos lo que sabemos
+
+Ahora ya sabes como crear una base de datos. Puedes guardar valores
+en ella, extraerlos creando un gráfico, hacer operaciones matemáticas
+con ellos desde la base de datos y visualizar los resultados de estas
+en vez de los datos originales. Vimos la diferencia entre los
+promedios y los máximos y cuando debemos usar cada uno (o al menos una
+idea de ello)
+
+RRDtool puede hacer más de lo que hemos visto hasta ahora. Pero
+antes de continuar, te recomiendo que releas el texto desde el
+principio y pruebes a hacerle algunas modificaciones a los ejemplos.
+Asegúrate de entenderlo todo. El esfuerzo valdrá la pena, y te ayudará,
+no sólo con el resto del documento, sino en tu trabajo diario de
+monitorización, mucho después de terminar con esta introducción.
+
+=head2 Tipos de fuentes de datos
+
+De acuerdo, quieres continuar. Bienvenido de vuelta otra vez y
+prepárate; voy a ir más rápido con los ejemplos y explicaciones.
+
+Ya vimos que, para ver el cambio de un contador a lo largo del
+tiempo, tenemos que tomar dos números y dividir la diferencia entre el
+tiempo transcurrido entre las mediciones. Para los ejemplos que hemos
+visto es lo lógico, pero hay otras posibilidades. Por ejemplo, mi
+enrutador me puede dar la temperatura actual en tres puntos distintos,
+la entrada de aire, el llamado "punto caliente" y la salida de
+ventilación. Estos valores no son contadores; si tomo los valores de
+dos muestreos y lo divido entre 300 segundos, obtendré el cambio de
+temperatura por segundo. ¡Esperemos que sea cero, o tendrÃamos un
+incendio en el cuarto de ordenadores! :)
+
+Entonces, ¿que hacemos? Podemos decirle a RRDtool que guarde los
+valores tal como los medimos (esto no es exactamente asÃ, pero se
+aproxima bastante a la verdad). AsÃ, los gráficos se verán mucho
+mejor. Puedo ver cuando el enrutador está trabajando más (en serio,
+funciona; como usa más electricidad, genera más calor y sube la
+temperatura), puedo saber cuando me he dejado las puertas abiertas (el
+cuarto de ordenadores tiene aire acondicionado; con las puertas
+abiertas el aire caliente del resto del edificion entra y sube la
+temperatura en la entrada de aire del enrutador), etc. Antes usamos un
+tipo de datos de "contador", ahora usaremos un tipo de datos
+diferente, con un nombre diferente, GAUGE.
+Tenemos otros tipos:
+
+ - COUNTER este ya lo conocemos
+ - GAUGE este acabamos de verlo
+ - DERIVE
+ - ABSOLUTE
+
+Los otros dos tipos son DERIVE y ABSOLUTE. ABSOLUTE puede usarse
+igual que COUNTER, con una diferencia; RRDtool asume que el contador
+se reinicia cada vez que se lee. O en otras palabras; el delta entre
+los valores no hay que calcularlo, mientras que con COUNTER RRDtool
+tiene que sacar él la cuenta. Por ejemplo, nuestro primer ejemplo,
+(12345, 12357, 12363, 12363), serÃa (unknown, 12, 6, 0) en ABSOLUTE.
+El otro tipo, DERIVE, es como COUNTER, pero al contrario de COUNTER,
+este valor también puede decrecer, por lo que puede tenerse un delta
+negativo.
+
+Vamos a probarlos todos:
+
+ rrdtool create all.rrd --start 978300900 \
+ DS:a:COUNTER:600:U:U \
+ DS:b:GAUGE:600:U:U \
+ DS:c:DERIVE:600:U:U \
+ DS:d:ABSOLUTE:600:U:U \
+ RRA:AVERAGE:0.5:1:10
+ rrdtool update all.rrd \
+ 978301200:300:1:600:300 \
+ 978301500:600:3:1200:600 \
+ 978301800:900:5:1800:900 \
+ 978302100:1200:3:2400:1200 \
+ 978302400:1500:1:2400:1500 \
+ 978302700:1800:2:1800:1800 \
+ 978303000:2100:4:0:2100 \
+ 978303300:2400:6:600:2400 \
+ 978303600:2700:4:600:2700 \
+ 978303900:3000:2:1200:3000
+ rrdtool graph all1.png -s 978300600 -e 978304200 -h 400 \
+ DEF:linea=all.rrd:a:AVERAGE LINE3:linea#FF0000:"Line A" \
+ DEF:lineb=all.rrd:b:AVERAGE LINE3:lineb#00FF00:"Line B" \
+ DEF:linec=all.rrd:c:AVERAGE LINE3:linec#0000FF:"Line C" \
+ DEF:lined=all.rrd:d:AVERAGE LINE3:lined#000000:"Line D"
+
+=head2 RRDtool bajo el microscopio
+
+=over 4
+
+=item *
+
+La lÃnea A es un contador, por lo que
+debe incrementarse continuamente y RRDtool tiene que calcular las
+diferencias. Además RRDtool tiene que dividir la diferencia entre
+el tiempo transcurrido. Esto deberÃa terminar con una lÃnea recta
+en 1 (los deltas son 300, y los intervalos son de 300)
+
+=item *
+
+La lÃnea B es de tipo GAUGE. Estos son
+los valores "reales", asà que el gráfico debe mostrar lo mismo que
+los valores que introducimos: una especie de onda
+Z<>
+
+=item *
+
+La lÃnea C es de tipo DERIVE. Es un
+contador, y puede decrecer. Va entre 2400 y 0, con 1800 en el medio.
+
+=item *
+
+La lÃnea D es de tipo ABSOLUTE. Esto es,
+es un contador pero no hay que calcular las diferencias. Los
+números son iguales a la lÃnea A, y espero
+que puedas ver la diferencia en los gráficos.
+
+=back
+
+Esto equivale a los valores siguientes, empezando a las 23:10 y
+terminando a las 00:10 (las U significan desconocido).
+
+
+ - LÃnea A: u u 1 1 1 1 1 1 1 1 1 u
+ - LÃnea B: u 1 3 5 3 1 2 4 6 4 2 u
+ - LÃnea C: u u 2 2 2 0 -2 -6 2 0 2 u
+ - LÃnea D: u 1 2 3 4 5 6 7 8 9 10 u
+
+Si tu archivo PNG muestra todo esto, has
+entrado los datos correctamente, tu programa RRDtool está funcionando
+bien, el visor de gráficos no te engaña y hemos entrado en el 2000 sin
+problemas :) Puedes probar el mismo ejemplo cuatro veces, una por cada lÃnea.
+
+Revisemos los datos otra vez:
+
+=over 4
+
+=item *
+
+LÃnea A: 300, 600, 900 , etc.
+La diferencia del contador es siempre 300, igual que el intervalo de
+tiempo transcurrido entre mediciones. Por lo tanto, el promedio
+siempre es 1. Pero, ¿por qué el primer punto tiene un valor de
+"desconocido"? ¿Acaso no era conocido el valor que pusimos en la
+base de datos? ¡Si! Pero no tenÃamos un valor inicial para
+calcular la diferencia. SerÃa un error asumir que el contador
+empezaba en 0, asà que no conocemos el valor de la diferencia
+
+=item *
+
+LÃnea B:
+No hay nada que calcular, los valores son los mismos que se
+introdujeron en la base de datos.
+
+=item *
+
+LÃnea C:
+De nuevo, no conocemos el valor
+inicial antes de la primera medición, asà que se aplica el mismo
+razonamiento que para la lÃnea A. En este
+caso las diferencias no son constantes, asà que la lÃnea no es
+recta. Si hubiésemos puesto los mismos valores que en la lÃnea
+A, el gráfico serÃa el mismo. Al contrario
+que COUNTER, el valor puede decrecer, y espero mostrarte más
+adelante el por que de la diferencia entre ambos tipos.
+
+=item *
+
+LÃnea D: En este caso, el dispositivo nos
+da las diferencias por sà mismo. Por lo tanto, conocemos la
+diferencia inicial, y podemos graficarla. Tenemos los mismos
+valores que en la lÃnea A, pero su
+significado es distinto, por lo que el gráfico también lo es. En
+este caso, las diferencias se incrementan en 300 cada vez,
+mientras que el intervalo de tiempo permanece constante en 300
+segundos, por lo que la división nos da resultados cada vez mayores.
+
+=back
+
+=head2 Reinicialización de los contadores
+
+TodavÃa nos quedan algunas cosas por ver. Nos quedan algunas
+opciones importantes por cubrir, y aun no hemos hablado de la
+reinicialización de contadores. Empecemos por ahÃ: Estamos en nuestro
+coche, vemos el contador y muestra 999987. Andamos unos 20 KM, asà que
+el contador debe subir a 1000007. Desafortunadamente, el contador
+sólo tiene 6 dÃgitos, asà que en realidad nos muestra 000007. Si
+estuviéramos guardando los valores en un tipo DERIVE, esto
+significarÃa que el contador retrocedió unos 999980 KM. Por supuesto
+esto no es cierto, por lo que necesitamos alguna protección contra estos
+casos. Esta protección sólo la tenemos para el tipo COUNTER, el cual
+de todas formas era el que tenÃamos que haber usado para este tipo de
+contador. ¿Cómo funciona? Los valores tipo COUNTER no deben decrecer
+nunca, ¡por lo que RRDtool asume en ese caso que el contador se ha
+reinicializado! Si la diferencia es negativa, esto se compensa sumando
+el valor máximo del contador + 1. Para nuestro coche, tendrÃamos:
+
+ Delta = 7 - 999987 = -999980 (en vez de 1000007-999987=20)
+
+ Delta real= -999980 + 999999 + 1 = 20
+
+Al momento de escribir este documento, RRDtool maneja contadores de
+32 o 64 bits de tamaño. Estos contadores pueden manejar los siguientes
+valores:
+
+ - 32 bits: 0 .. 4294967295
+ - 64 bits: 0 .. 18446744073709551615
+
+Si estos valores te parecen raros, podemos verlos en formato hexadecimal:
+
+ - 32 bits: 0 .. FFFFFFFF
+ - 64 bits: 0 .. FFFFFFFFFFFFFFFF
+
+RRDtool maneja ambos contadores de la misma manera. Si ocurre un
+desbordamiento y la diferencia es negativa, RRDtool le suma primero
+el máximo del contador "menor" (32 bits) + 1 a la diferencia. Si aún
+asà la diferencia es negativa, entonces el contador reinicializado era
+mayor (64 bits), por lo que se le suma el valor máximo del contador
+"largo" + 1 y se le resta el máximo del contador "pequeño" que sumamos
+erróneamente. Hay un problema con esto: supongamos que un contador
+largo se ha reinicializado al sumársele una diferencia muy grande;
+entonces es posible que al añadir el valor máximo del contador pequeño
+la diferencia nos dé positivo. En este caso poco probable, los valores
+resultantes no serian correctos. Para que ocurra esto, el incremento
+tiene que ser casi tan grande como el valor máximo del contador, por
+lo que de ocurrir es muy probable que halla varios problemas más en
+la configuración y no merezca la pena preocuparse sólo por este. Aún
+asÃ, he incluido un ejemplo de este caso para que lo puedas juzgar por
+ti mismo.
+
+A continuación, unos ejemplos de reinicialización de los
+contadores. Prueba de hacer los cálculos por ti mismo, o acepta mis
+resultados si tu calculadora no puede con los números :)
+
+Números de corrección:
+
+ - 32 bits: (4294967295+1) = 4294967296
+ - 64 bits: (18446744073709551615+1)-correction1 = 18446744069414584320
+
+ Antes: 4294967200
+ Incremento: 100
+ DeberÃa ser: 4294967300
+ Pero es: 4
+ Diferencia: -4294967196
+ Corrección #1: -4294967196 + 4294967296 = 100
+
+ Antes: 18446744073709551000
+ Incremento: 800
+ DeberÃa ser: 18446744073709551800
+ Pero es: 184
+ Diferencia: -18446744073709550816
+ Corrección #1: -18446744073709550816 +4294967296 = -18446744069414583520
+ Corrección #2: -18446744069414583520 +18446744069414584320 = 800
+
+ Antes: 18446744073709551615 ( valor máximo )
+ Incremento: 18446744069414584320 ( incremento absurdo,
+ DeberÃa ser: 36893488143124135935 mÃnimo para que
+ Pero es: 18446744069414584319 funcione el ejemplo)
+ Diferencia: -4294967296
+ Corrección #1: -4294967296 + 4294967296 = 0 (positivo,
+ por tanto no se hace
+ la segunda corrección)
+
+ Antes: 18446744073709551615 ( valor máximo )
+ Incremento: 18446744069414584319
+ DeberÃa ser: 36893488143124135934
+ Pero es: 18446744069414584318
+ Diferencia: -4294967297
+ Corrección #1: -4294967297 +4294967296 = -1
+ Corrección #2: -1 +18446744069414584320 = 18446744069414584319
+
+Como puede verse en los últimos ejemplos, necesitas unos valores
+bastante extraños para hacer que RRDtool falle (asumiendo que no tenga
+ningún error el programa, por supuesto), asà que esto no deberÃa
+ocurrir. Sin embargo, SNMP o cualquier otro
+método que uses de recogida de datos puede también reportar algún
+valor erróneo ocasionalmente. No podemos prevenir todos los errores,
+pero podemos tomar algunas medidas. El comando "create" de RRDtool
+tiene dos parámetros especialmente para esto, que definen los valores
+mÃnimo y máximo permitidos. Hasta ahora hemos usado "U",
+"desconocido". Si le pasas valores para uno o ambos parámetros y
+RRDtool recibe un valor fuera de esos lÃmites, los ignorará. Para un
+termómetro en grados Celsius, el mÃnimo absoluto es -273. Para mi
+enrutador, puedo asumir que ese mÃnimo es mucho mayor, digamos que 10.
+La temperatura máxima la pondrÃa en unos 80 grados; más alto y
+el aparato no funcionarÃa. Para mi coche, nunca esperarÃa obtener
+valores negativos, y tampoco esperarÃa valores mayores a 230.
+Cualquier otra cosa serÃa un error. Pero recuerda, lo contrario no es
+cierto: si los valores pasan este examen no quiere decir que sean los
+correctos. Siempre examina bien el gráfico si los valores parecen
+extraños.
+
+
+=head2 Remuestreo de los datos
+
+Hay una funcionalidad importante de RRDtool que no hemos explicado
+todavÃa: es virtualmente imposible recoger los datos y pasarselos a
+RRDtool a intervalos exactos de tiempo. Por tanto, RRDtool interpola
+los datos a los intervalos exactos. Si no sabes que significa esto o
+como se hace, he aquà la ayuda que necesitas:
+
+Supongamos un contador se incremente exactamente en 1 cada segundo.
+Queremos medirlo cada 300 segundos, por lo que deberÃamos tener
+valores separados exactamente en 300. Sin embargo, por varias
+circunstancias llegamos unos segundos tarde y el intervalo es 303. La
+diferencia será por tanto 303. Obviamente, RRDtool no debe colocar 303
+en la base de datos y dar asà la impresión de que el contador se
+incrementó 303 en 300 segundos. Aquà es donde RRDtool interpola:
+alterá el valor 303 al valor que tendrÃa 3 segundos antes y guarda 300
+en 300 segundos. Digamos que la próxima vez llegamos justo a tiempo;
+por tanto, el intervalo actual es 297 segundos, por lo que el contador
+deberÃa ser 297. De nuevo, RRDtool altera el valor y guarda 300, como
+debe ser.
+
+ en RRD en realidad
+ tiempo+000: 0 delta="U" tiempo+000: 0 delta="U"
+ tiempo+300: 300 delta=300 tiempo+300: 300 delta=300
+ tiempo+600: 600 delta=300 tiempo+603: 603 delta=303
+ tiempo+900: 900 delta=300 tiempo+900: 900 delta=297
+
+Creemos dos bases de datos idénticas. He escogido el rango de
+tiempo entre 920805000 y 920805900.
+
+ rrdtool create seconds1.rrd \
+ --start 920804700 \
+ DS:seconds:COUNTER:600:U:U \
+ RRA:AVERAGE:0.5:1:24
+
+ para Unix: cp seconds1.rrd seconds2.rrd
+ para DOS: copy seconds1.rrd seconds2.rrd
+ para VMS: y yo que sé :)
+
+ rrdtool update seconds1.rrd \
+ 920805000:000 920805300:300 920805600:600 920805900:900
+ rrdtool update seconds2.rrd \
+ 920805000:000 920805300:300 920805603:603 920805900:900
+
+ rrdtool graph seconds1.png \
+ --start 920804700 --end 920806200 \
+ --height 200 \
+ --upper-limit 1.05 --lower-limit 0.95 --rigid \
+ DEF:seconds=seconds1.rrd:seconds:AVERAGE \
+ CDEF:unknown=seconds,UN \
+ LINE2:seconds#0000FF \
+ AREA:unknown#FF0000
+ rrdtool graph seconds2.png \
+ --start 920804700 --end 920806200 \
+ --height 200 \
+ --upper-limit 1.05 --lower-limit 0.95 --rigid \
+ DEF:seconds=seconds2.rrd:seconds:AVERAGE \
+ CDEF:unknown=seconds,UN \
+ LINE2:seconds#0000FF \
+ AREA:unknown#FF0000
+
+Los dos gráficos debe ser iguales.
+
+=head1 RESUMEN
+
+Es hora de concluir este documento. Ahora debes conocer lo básico
+como para trabajar con RRDtool y leer la documentación. Aún hay mucho
+más por descubrir acerca de RRDtool, y le encontrarás; más y más usos
+para la herramienta. Con los ejemplos y la herramienta puedes crear
+fácilmente muchos gráficos; también puedes usar las interfaces
+disponibles.
+
+=head1 LISTA DE CORREO
+
+Recuerda subscribirte a la lista de correo. Aunque no contestes los
+correos que aparecen en ella, te servirá de ayuda a ti y a los demás.
+Mucho de lo que se sobre MRTG (y por tanto sobre RRDtool), lo aprendÃ
+tan sólo con leer la lista, sin escribir. No hay por que preguntar las
+preguntas básicas, que ya tienen su respuesta en la FAQ (¡léela!). Con
+miles de usuarios a lo largo del mundo, siempre hay preguntas que tu
+puedes responder con lo aprendido en este y otros documentos.
+
+=head1 VER TAMBIÉN
+
+Las páginas del manual de RRDtool
+
+=head1 AUTOR
+
+Espero que hayas disfrutado con los ejemplos y las descripciones.
+Si es asÃ, ayuda a otros refiriéndolos a este documento cuando te
+hagan preguntas básicas. No sólo obtendrán la respuesta, sino que
+aprenderán muchas otras cosas.
+
+Alex van den Bogaerdt <alex@ergens.op.het.net>
diff --git a/program/doc/rrdtutorial.pod b/program/doc/rrdtutorial.pod
--- /dev/null
@@ -0,0 +1,1178 @@
+=head1 NAME
+
+rrdtutorial - Alex van den Bogaerdt's RRDtool tutorial
+
+=head1 DESCRIPTION
+
+RRDtool is written by Tobias Oetiker <tobi@oetiker.ch> with
+contributions from many people all around the world. This document is
+written by Alex van den Bogaerdt <alex@ergens.op.het.net> to help you
+understand what RRDtool is and what it can do for you.
+
+The documentation provided with RRDtool can be too technical for some
+people. This tutorial is here to help you understand the basics of
+RRDtool. It should prepare you to read the documentation yourself.
+It also explains the general things about statistics with a focus on
+networking.
+
+=head1 TUTORIAL
+
+=head2 Important
+
+Please don't skip ahead in this document! The first part of this
+document explains the basics and may be boring. But if you don't
+understand the basics, the examples will not be as meaningful to you.
+
+=head2 What is RRDtool?
+
+RRDtool refers to Round Robin Database tool.
+Round robin is a technique that works with a fixed amount of data, and a
+pointer to the current element. Think of a circle with some dots plotted
+on the edge -- these dots are the places where data can be stored. Draw an
+arrow from the center of the circle to one of the dots -- this is the pointer.
+When the current data is read or written, the pointer moves to the next
+element. As we are on a circle there is neither a beginning nor an end, you can
+go on and on and on. After a while, all the available places will be used and
+the process automatically reuses old locations. This way, the dataset
+will not grow in size and therefore requires no maintenance.
+RRDtool works with with Round Robin Databases (RRDs). It stores and retrieves
+data from them.
+
+=head2 What data can be put into an RRD?
+
+You name it, it will probably fit as long as it is some sort of time-series
+data. This means you have to be able to measure some value at several points in time and
+provide this information to RRDtool. If you can do this, RRDtool will be
+able to store it. The values must be numerical but don't have to be
+integers, as is the case with MRTG (the next section will give more details
+on this more specialized application).
+
+Many examples below talk about SNMP which is an acronym for Simple Network
+Management Protocol. "Simple" refers to the protocol -- it does not
+mean it is simple to manage or monitor a network. After working your
+way through this document, you should know enough to be able to
+understand what people are talking about. For now, just realize that
+SNMP can be used to query devices for the values of counters they keep. It
+is the value from those counters that we want to store in the RRD.
+
+=head2 What can I do with this tool?
+
+RRDtool originated from MRTG (Multi Router Traffic Grapher). MRTG
+started as a tiny little script for graphing the use of a university's
+connection to the Internet. MRTG was later (ab-)used as a tool for
+graphing other data sources including temperature, speed, voltage,
+number of printouts and the like.
+
+Most likely you will start to use RRDtool to store and process data
+collected via SNMP. The data will most likely be bytes (or bits)
+transfered from and to a network or a computer. But it can also be
+used to display tidal waves, solar radiation, power consumption,
+number of visitors at an exhibition, noise levels near an airport,
+temperature on your favorite holiday location, temperature in the
+fridge and whatever you imagination can come up with.
+
+You only need a sensor to measure the data and be able to feed the
+numbers into RRDtool. RRDtool then lets you create a database, store
+data in it, retrieve that data and create graphs in PNG format for
+display on a web browser. Those PNG images are dependent on the data
+you collected and could be, for instance, an overview of the average
+network usage, or the peaks that occurred.
+
+=head2 What if I still have problems after reading this document?
+
+First of all: read it again! You may have missed something.
+If you are unable to compile the sources and you have a fairly common
+OS, it will probably not be the fault of RRDtool. There may be pre-compiled
+versions around on the Internet. If they come from trusted sources, get
+one of those.
+
+If on the other hand the program works but does not give you the
+expected results, it will be a problem with configuring it. Review
+your configuration and compare it with the examples that follow.
+
+There is a mailing list and an archive of it. Read the list for a few
+weeks and search the archive. It is considered rude to just ask
+a question without searching the archives: your problem may already have been
+solved for somebody else! This is true for most, if not all, mailing lists
+and not only for this particular one. Look in the documentation that
+came with RRDtool for the location and usage of the list.
+
+I suggest you take a moment to subscribe to the mailing list right now
+by sending an email to E<lt>rrd-users-request@lists.oetiker.chE<gt> with a
+subject of "subscribe". If you ever want to leave this list, just write
+an email to the same address but now with a subject of "unsubscribe".
+
+=head2 How will you help me?
+
+By giving you some detailed descriptions with detailed examples.
+I assume that following the instructions in the order presented
+will give you enough knowledge of RRDtool to experiment for yourself.
+If it doesn't work the first time, don't give up. Reread the stuff that
+you did understand, you may have missed something.
+
+By following the examples you get some hands-on experience and, even
+more important, some background information of how it works.
+
+You will need to know something about hexadecimal numbers. If you don't
+then start with reading L<bin_dec_hex> before you continue here.
+
+=head2 Your first Round Robin Database
+
+In my opinion the best way to learn something is to actually do it.
+Why not start right now? We will create a database, put some values
+in it and extract this data again. Your output should be the same
+as the output that is included in this document.
+
+We will start with some easy stuff and compare a car with a router,
+or compare kilometers (miles if you wish) with bits and bytes. It's
+all the same: some number over some time.
+
+Assume we have a device that transfers bytes to and from the Internet.
+This device keeps a counter that starts at zero when it is turned on,
+increasing with every byte that is transfered. This counter will probably have
+a maximum value. If this value is reached and an extra byte is counted,
+the counter starts over at zero. This is the same as many counters
+in the world such as the mileage counter in a car.
+
+Most discussions about networking talk about bits per second so lets
+get used to that right away. Assume a byte is eight bits and start to
+think in bits not bytes. The counter, however, still counts bytes!
+In the SNMP world most of the counters are 32 bits. That means they are
+counting from 0 to 4'294'967'295. We will use these values in the examples.
+The device, when asked, returns the current value of the counter. We
+know the time that has passes since we last asked so we now know how
+many bytes have been transfered ***on average*** per second. This is
+not very hard to calculate. First in words, then in calculations:
+
+=over 3
+
+=item 1.
+
+Take the current counter, subtract the previous value from it.
+
+=item 2.
+
+Do the same with the current time and the previous time (in seconds).
+
+=item 3.
+
+Divide the outcome of (1) by the outcome of (2), the result is
+the amount of bytes per second. Multiply by eight to get the
+number of bits per second (bps).
+
+=back
+
+ bps = (counter_now - counter_before) / (time_now - time_before) * 8
+
+For some people it may help to translate this to an automobile example.
+Do not try this example, and if you do, don't blame me for the results!
+
+People who are not used to think in kilometers per hour can translate
+most into miles per hour by dividing km by 1.6 (close enough).
+I will use the following abbreviations:
+
+ M: meter
+ KM: kilometer (= 1'000 meters).
+ H: hour
+ S: second
+ KM/H: kilometers per hour
+ M/S: meters per second
+
+You are driving a car. At 12:05 you read the counter in the dashboard
+and it tells you that the car has moved 12'345 KM until that moment.
+At 12:10 you look again, it reads 12'357 KM. This means you have
+traveled 12 KM in five minutes. A scientist would translate that
+into meters per second and this makes a nice comparison toward the
+problem of (bytes per five minutes) versus (bits per second).
+
+We traveled 12 kilometers which is 12'000 meters. We did that in five
+minutes or 300 seconds. Our speed is 12'000M / 300S or 40 M/S.
+
+We could also calculate the speed in KM/H: 12 times 5 minutes
+is an hour, so we have to multiply 12 KM by 12 to get 144 KM/H.
+For our native English speaking friends: that's 90 MPH so don't
+try this example at home or where I live :)
+
+Remember: these numbers are averages only. There is no way to figure out
+from the numbers, if you drove at a constant speed. There is an example
+later on in this tutorial that explains this.
+
+I hope you understand that there is no difference in calculating M/S or
+bps; only the way we collect the data is different. Even the K from kilo
+is the same as in networking terms k also means 1'000.
+
+We will now create a database where we can keep all these interesting
+numbers. The method used to start the program may differ slightly from
+OS to OS, but I assume you can figure it out if it works different on
+your's. Make sure you do not overwrite any file on your system when
+executing the following command and type the whole line as one long
+line (I had to split it for readability)
+and skip all of the '\' characters.
+
+ rrdtool create test.rrd \
+ --start 920804400 \
+ DS:speed:COUNTER:600:U:U \
+ RRA:AVERAGE:0.5:1:24 \
+ RRA:AVERAGE:0.5:6:10
+
+(So enter: C<rrdtool create test.rrd --start 920804400 DS ...>)
+
+=head2 What has been created?
+
+We created the round robin database called test (test.rrd) which
+starts at noon the day I started writing this document, 7th of March,
+1999 (this date translates to 920'804'400 seconds as explained
+below). Our database holds one data source (DS) named "speed" that
+represents a counter. This counter is read every five minutes
+(default). In the same database two round robin archives (RRAs) are
+kept, one averages the data every time it is read (e.g., there's
+nothing to average) and keeps 24 samples (24 times 5 minutes is 2
+hours). The other averages 6 values (half hour) and contains 10
+such averages (e.g., 5 hours).
+
+RRDtool works with special time stamps coming from the UNIX world.
+This time stamp is the number of seconds that passed since January
+1st 1970 UTC. The time stamp value is translated into local time and
+it will therefore look different for different time zones.
+
+Chances are that you are not in the same part of the world as I am.
+This means your time zone is different. In all examples where I talk
+about time, the hours may be wrong for you. This has little effect on
+the results of the examples, just correct the hours while reading.
+As an example: where I will see "12:05" the UK folks will see "11:05".
+
+We now have to fill our database with some numbers. We'll pretend to
+have read the following numbers:
+
+ 12:05 12345 KM
+ 12:10 12357 KM
+ 12:15 12363 KM
+ 12:20 12363 KM
+ 12:25 12363 KM
+ 12:30 12373 KM
+ 12:35 12383 KM
+ 12:40 12393 KM
+ 12:45 12399 KM
+ 12:50 12405 KM
+ 12:55 12411 KM
+ 13:00 12415 KM
+ 13:05 12420 KM
+ 13:10 12422 KM
+ 13:15 12423 KM
+
+We fill the database as follows:
+
+ rrdtool update test.rrd 920804700:12345 920805000:12357 920805300:12363
+ rrdtool update test.rrd 920805600:12363 920805900:12363 920806200:12373
+ rrdtool update test.rrd 920806500:12383 920806800:12393 920807100:12399
+ rrdtool update test.rrd 920807400:12405 920807700:12411 920808000:12415
+ rrdtool update test.rrd 920808300:12420 920808600:12422 920808900:12423
+
+This reads: update our test database with the following numbers
+
+ time 920804700, value 12345
+ time 920805000, value 12357
+
+etcetera.
+
+As you can see, it is possible to feed more than one value into the
+database in one command. I had to stop at three for readability but
+the real maximum per line is OS dependent.
+
+We can now retrieve the data from our database using "rrdtool fetch":
+
+ rrdtool fetch test.rrd AVERAGE --start 920804400 --end 920809200
+
+It should return the following output:
+
+ speed
+
+ 920804700: nan
+ 920805000: 4.0000000000e-02
+ 920805300: 2.0000000000e-02
+ 920805600: 0.0000000000e+00
+ 920805900: 0.0000000000e+00
+ 920806200: 3.3333333333e-02
+ 920806500: 3.3333333333e-02
+ 920806800: 3.3333333333e-02
+ 920807100: 2.0000000000e-02
+ 920807400: 2.0000000000e-02
+ 920807700: 2.0000000000e-02
+ 920808000: 1.3333333333e-02
+ 920808300: 1.6666666667e-02
+ 920808600: 6.6666666667e-03
+ 920808900: 3.3333333333e-03
+ 920809200: nan
+
+If it doesn't, something may be wrong. Perhaps your OS will print
+"NaN" in a different form. "NaN" stands for "Not A Number". If your OS
+writes "U" or "UNKN" or something similar that's okay. If something
+else is wrong, it will probably be due to an error you made (assuming
+that my tutorial is correct of course :-). In that case: delete the
+database and try again. Sometimes things change. This example used
+to provide numbers like "0.04" in stead of "4.00000e-02". Those are
+really the same numbers, just written down differently. Don't be
+alarmed if a future version of rrdtool displays a slightly different
+form of output. The examples in this document are correct for version
+1.2.0 of RRDtool.
+
+The meaning of the above output will become clear below.
+
+=head2 Time to create some graphics
+
+Try the following command:
+
+ rrdtool graph speed.png \
+ --start 920804400 --end 920808000 \
+ DEF:myspeed=test.rrd:speed:AVERAGE \
+ LINE2:myspeed#FF0000
+
+This will create speed.png which starts at 12:00 and ends at 13:00.
+There is a definition of a variable called myspeed, using the data from RRA
+"speed" out of database "test.rrd". The line drawn is 2 pixels high
+and represents the variable myspeed. The color is red (specified by
+its rgb-representation, see below).
+
+You'll notice that the start of the graph is not at 12:00 but at 12:05.
+This is because we have insufficient data to tell the average before
+that time. This will only happen when you miss some samples, this will
+not happen a lot, hopefully.
+
+If this has worked: congratulations! If not, check what went wrong.
+
+
+The colors are built up from red, green and blue. For each of the
+components, you specify how much to use in hexadecimal where 00 means
+not included and FF means fully included.
+The "color" white is a mixture of red, green and blue: FFFFFF
+The "color" black is all colors off: 000000
+
+ red #FF0000
+ green #00FF00
+ blue #0000FF
+ magenta #FF00FF (mixed red with blue)
+ gray #555555 (one third of all components)
+
+Additionally you can add an alpha channel (transparency). The default
+will be "FF" which means non-transparent.
+
+The PNG you just created can be displayed using your favorite image
+viewer. Web browsers will display the PNG via the URL
+"file:///the/path/to/speed.png"
+
+=head2 Graphics with some math
+
+When looking at the image, you notice that the horizontal axis is labeled
+12:10, 12:20, 12:30, 12:40 and 12:50. Sometimes a label doesn't fit (12:00
+and 13:00 would be candidates) so they are skipped.
+
+The vertical axis displays the range we entered. We provided
+kilometers and when divided by 300 seconds, we get very small
+numbers. To be exact, the first value was 12 (12'357-12'345) and divided
+by 300 this makes 0.04, which is displayed by RRDtool as "40 m"
+meaning "40/1'000". The "m" (milli) has nothing to do with meters,
+kilometers or millimeters! RRDtool doesn't know about the physical
+units of our data, it just works with dimensionless numbers.
+
+If we had measured our distances in meters, this would have been
+(12'357'000-12'345'000)/300 = 12'000/300 = 40.
+
+As most people have a better feel for numbers in this range, we'll
+correct that. We could recreate our database and store the correct
+data, but there is a better way: we do some calculations while creating
+the png file!
+
+ rrdtool graph speed2.png \
+ --start 920804400 --end 920808000 \
+ --vertical-label m/s \
+ DEF:myspeed=test.rrd:speed:AVERAGE \
+ CDEF:realspeed=myspeed,1000,\* \
+ LINE2:realspeed#FF0000
+
+Note: Make sure not to forget the backslash \ in front of the
+multiplication operator * above. The backslash is needed to "escape"
+the * as some operating systems might interpret and expand * instead
+of passing it to the rrdtool command.
+
+After viewing this PNG, you notice the "m" (milli) has
+disappeared. This it what the correct result would be. Also, a label
+has been added to the image. Apart from the things mentioned above,
+the PNG should look the same.
+
+The calculations are specified in the CDEF part above and are in
+Reverse Polish Notation ("RPN"). What we requested RRDtool to do is:
+"take the data source myspeed and the number 1000; multiply
+those". Don't bother with RPN yet, it will be explained later on in
+more detail. Also, you may want to read my tutorial on CDEFs and Steve
+Rader's tutorial on RPN. But first finish this tutorial.
+
+Hang on! If we can multiply values with 1'000, it should also be possible
+to display kilometers per hour from the same data!
+
+To change a value that is measured in meters per second:
+
+ Calculate meters per hour: value * 3'600
+ Calculate kilometers per hour: value / 1'000
+ Together this makes: value * (3'600/1'000) or value * 3.6
+
+In our example database we made a mistake and we need to compensate for
+this by multiplying with 1'000. Applying that correction:
+
+ value * 3.6 * 1'000 == value * 3'600
+
+Now let's create this PNG, and add some more magic ...
+
+ rrdtool graph speed3.png \
+ --start 920804400 --end 920808000 \
+ --vertical-label km/h \
+ DEF:myspeed=test.rrd:speed:AVERAGE \
+ "CDEF:kmh=myspeed,3600,*" \
+ CDEF:fast=kmh,100,GT,kmh,0,IF \
+ CDEF:good=kmh,100,GT,0,kmh,IF \
+ HRULE:100#0000FF:"Maximum allowed" \
+ AREA:good#00FF00:"Good speed" \
+ AREA:fast#FF0000:"Too fast"
+
+Note: here we use another means to escape the * operator by enclosing
+the whole string in double quotes.
+
+This graph looks much better. Speed is shown in KM/H and there is even
+an extra line with the maximum allowed speed (on the road I travel
+on). I also changed the colors used to display speed and changed it
+from a line into an area.
+
+The calculations are more complex now. For speed measurements within
+the speed limit they are:
+
+ Check if kmh is greater than 100 ( kmh,100 ) GT
+ If so, return 0, else kmh ((( kmh,100 ) GT ), 0, kmh) IF
+
+For values above the speed limit:
+
+ Check if kmh is greater than 100 ( kmh,100 ) GT
+ If so, return kmh, else return 0 ((( kmh,100) GT ), kmh, 0) IF
+
+=head2 Graphics Magic
+
+I like to believe there are virtually no limits to how RRDtool graph
+can manipulate data. I will not explain how it works, but look at the
+following PNG:
+
+ rrdtool graph speed4.png \
+ --start 920804400 --end 920808000 \
+ --vertical-label km/h \
+ DEF:myspeed=test.rrd:speed:AVERAGE \
+ "CDEF:kmh=myspeed,3600,*" \
+ CDEF:fast=kmh,100,GT,100,0,IF \
+ CDEF:over=kmh,100,GT,kmh,100,-,0,IF \
+ CDEF:good=kmh,100,GT,0,kmh,IF \
+ HRULE:100#0000FF:"Maximum allowed" \
+ AREA:good#00FF00:"Good speed" \
+ AREA:fast#550000:"Too fast" \
+ STACK:over#FF0000:"Over speed"
+
+Let's create a quick and dirty HTML page to view the three PNGs:
+
+ <HTML><HEAD><TITLE>Speed</TITLE></HEAD><BODY>
+ <IMG src="speed2.png" alt="Speed in meters per second">
+ <BR>
+ <IMG src="speed3.png" alt="Speed in kilometers per hour">
+ <BR>
+ <IMG src="speed4.png" alt="Traveled too fast?">
+ </BODY></HTML>
+
+Name the file "speed.html" or similar, and look at it in your web browser.
+
+Now, all you have to do is measure the values regularly and update the
+database. When you want to view the data, recreate the PNGs and make
+sure to refresh them in your browser. (Note: just clicking reload may
+not be enough, especially when proxies are involved. Try shift-reload
+or ctrl-F5).
+
+=head2 Updates in Reality
+
+We've already used the "update" command: it took one or more
+parameters in the form of "<time>:<value>". You'll be glad to know
+that you can specify the current time by filling in a "N" as the time.
+Or you could use the "time" function in Perl (the shortest example in
+this tutorial):
+
+ perl -e 'print time, "\n" '
+
+How to run a program on regular intervals is OS specific. But here is
+an example in pseudo code:
+
+ - Get the value and put it in variable "$speed"
+ - rrdtool update speed.rrd N:$speed
+
+(do not try this with our test database, we'll use it in further examples)
+
+This is all. Run the above script every five minutes. When you need to know
+what the graphs look like, run the examples above. You could put them
+in a script as well. After running that script, view the page
+index.html we created above.
+
+=head2 Some words on SNMP
+
+I can imagine very few people that will be able to get real data from
+their car every five minutes. All other people will have to settle for
+some other kind of counter. You could measure the number of pages
+printed by a printer, for example, the cups of coffee made by the
+coffee machine, a device that counts the electricity used,
+whatever. Any incrementing counter can be monitored and graphed using
+the stuff you learned so far. Later on we will also be able to monitor
+other types of values like temperature.
+
+Most (?) people interested in RRDtool will use the counter that keeps track
+of octets (bytes) transfered by a network device. So let's do just
+that next. We will start with a description of how to collect data.
+
+Some people will make a remark that there are tools which can do this data
+collection for you. They are right! However, I feel it is important that
+you understand they are not necessary. When you have to determine why
+things went wrong you need to know how they work.
+
+One tool used in the example has been talked about very briefly in the
+beginning of this document, it is called SNMP. It is a way of talking
+to networked equipment. The tool I use below is called "snmpget" and
+this is how it works:
+
+ snmpget device password OID
+
+or
+
+ snmpget -v[version] -c[password] device OID
+
+For device you substitute the name, or the IP address, of your device.
+For password you use the "community read string" as it is called in the
+SNMP world. For some devices the default of "public" might work, however
+this can be disabled, altered or protected for privacy and security
+reasons. Read the documentation that comes with your device or program.
+
+Then there is this parameter, called OID, which means "object identifier".
+
+When you start to learn about SNMP it looks very confusing. It isn't
+all that difficult when you look at the Management Information Base
+("MIB"). It is an upside-down tree that describes data, with a single node
+as the root and from there a number of branches. These branches end
+up in another node, they branch out, etc. All the branches have a name
+and they form the path that we follow all the way down. The branches
+that we follow are named: iso, org, dod, internet, mgmt and mib-2.
+These names can also be written down as numbers and are 1 3 6 1 2 1.
+
+ iso.org.dod.internet.mgmt.mib-2 (1.3.6.1.2.1)
+
+There is a lot of confusion about the leading dot that some programs
+use. There is *no* leading dot in an OID. However, some programs
+can use the above part of OIDs as a default. To indicate the difference
+between abbreviated OIDs and full OIDs they need a leading dot when
+you specify the complete OID. Often those programs will leave out
+the default portion when returning the data to you. To make things
+worse, they have several default prefixes ...
+
+Ok, lets continue to the start of our OID: we had 1.3.6.1.2.1
+From there, we are especially interested in the branch "interfaces"
+which has number 2 (e.g., 1.3.6.1.2.1.2 or 1.3.6.1.2.1.interfaces).
+
+First, we have to get some SNMP program. First look if there is a
+pre-compiled package available for your OS. This is the preferred way.
+If not, you will have to get the sources yourself and compile those.
+The Internet is full of sources, programs etc. Find information using
+a search engine or whatever you prefer.
+
+Assume you got the program. First try to collect some data that is
+available on most systems. Remember: there is a short name for the
+part of the tree that interests us most in the world we live in!
+
+I will give an example which can be used on Fedora Core 3. If it
+doesn't work for you, work your way through the manual of snmp and
+adapt the example to make it work.
+
+ snmpget -v2c -c public myrouter system.sysDescr.0
+
+The device should answer with a description of itself, perhaps an
+empty one. Until you got a valid answer from a device, perhaps using a
+different "password", or a different device, there is no point in
+continuing.
+
+ snmpget -v2c -c public myrouter interfaces.ifNumber.0
+
+Hopefully you get a number as a result, the number of interfaces.
+If so, you can carry on and try a different program called "snmpwalk".
+
+ snmpwalk -v2c -c public myrouter interfaces.ifTable.ifEntry.ifDescr
+
+If it returns with a list of interfaces, you're almost there.
+Here's an example:
+ [user@host /home/alex]$ snmpwalk -v2c -c public cisco 2.2.1.2
+
+ interfaces.ifTable.ifEntry.ifDescr.1 = "BRI0: B-Channel 1"
+ interfaces.ifTable.ifEntry.ifDescr.2 = "BRI0: B-Channel 2"
+ interfaces.ifTable.ifEntry.ifDescr.3 = "BRI0" Hex: 42 52 49 30
+ interfaces.ifTable.ifEntry.ifDescr.4 = "Ethernet0"
+ interfaces.ifTable.ifEntry.ifDescr.5 = "Loopback0"
+
+On this cisco equipment, I would like to monitor the "Ethernet0"
+interface and from the above output I see that it is number four. I try:
+
+ [user@host /home/alex]$ snmpget -v2c -c public cisco 2.2.1.10.4 2.2.1.16.4
+
+ interfaces.ifTable.ifEntry.ifInOctets.4 = 2290729126
+ interfaces.ifTable.ifEntry.ifOutOctets.4 = 1256486519
+
+So now I have two OIDs to monitor and they are (in full, this time):
+
+ 1.3.6.1.2.1.2.2.1.10
+
+and
+
+ 1.3.6.1.2.1.2.2.1.16
+
+both with an interface number of 4.
+
+Don't get fooled, this wasn't my first try. It took some time for me too
+to understand what all these numbers mean. It does help a lot when they
+get translated into descriptive text... At least, when people are talking
+about MIBs and OIDs you know what it's all about.
+Do not forget the interface number (0 if it is not interface dependent)
+and try snmpwalk if you don't get an answer from snmpget.
+
+If you understand the above section and get numbers from your device, continue
+on with this tutorial. If not, then go back and re-read this part.
+
+=head2 A Real World Example
+
+Let the fun begin. First, create a new database. It contains data from
+two counters, called input and output. The data is put into archives
+that average it. They take 1, 6, 24 or 288 samples at a time.
+They also go into archives that keep the maximum numbers. This will be
+explained later on. The time in-between samples is 300 seconds, a good
+starting point, which is the same as five minutes.
+
+ 1 sample "averaged" stays 1 period of 5 minutes
+ 6 samples averaged become one average on 30 minutes
+ 24 samples averaged become one average on 2 hours
+ 288 samples averaged become one average on 1 day
+
+Lets try to be compatible with MRTG which stores about the following
+amount of data:
+
+ 600 5-minute samples: 2 days and 2 hours
+ 600 30-minute samples: 12.5 days
+ 600 2-hour samples: 50 days
+ 732 1-day samples: 732 days
+
+These ranges are appended, so the total amount of data stored in the
+database is approximately 797 days. RRDtool stores the data
+differently, it doesn't start the "weekly" archive where the "daily"
+archive stopped. For both archives the most recent data will be near
+"now" and therefore we will need to keep more data than MRTG does!
+
+We will need:
+
+ 600 samples of 5 minutes (2 days and 2 hours)
+ 700 samples of 30 minutes (2 days and 2 hours, plus 12.5 days)
+ 775 samples of 2 hours (above + 50 days)
+ 797 samples of 1 day (above + 732 days, rounded up to 797)
+
+ rrdtool create myrouter.rrd \
+ DS:input:COUNTER:600:U:U \
+ DS:output:COUNTER:600:U:U \
+ RRA:AVERAGE:0.5:1:600 \
+ RRA:AVERAGE:0.5:6:700 \
+ RRA:AVERAGE:0.5:24:775 \
+ RRA:AVERAGE:0.5:288:797 \
+ RRA:MAX:0.5:1:600 \
+ RRA:MAX:0.5:6:700 \
+ RRA:MAX:0.5:24:775 \
+ RRA:MAX:0.5:288:797
+
+Next thing to do is to collect data and store it. Here is an example.
+It is written partially in pseudo code, you will have to find out what
+to do exactly on your OS to make it work.
+
+ while not the end of the universe
+ do
+ get result of
+ snmpget router community 2.2.1.10.4
+ into variable $in
+ get result of
+ snmpget router community 2.2.1.16.4
+ into variable $out
+
+ rrdtool update myrouter.rrd N:$in:$out
+
+ wait for 5 minutes
+ done
+
+Then, after collecting data for a day, try to create an image using:
+
+ rrdtool graph myrouter-day.png --start -86400 \
+ DEF:inoctets=myrouter.rrd:input:AVERAGE \
+ DEF:outoctets=myrouter.rrd:output:AVERAGE \
+ AREA:inoctets#00FF00:"In traffic" \
+ LINE1:outoctets#0000FF:"Out traffic"
+
+This should produce a picture with one day worth of traffic.
+One day is 24 hours of 60 minutes of 60 seconds: 24*60*60=86'400, we
+start at now minus 86'400 seconds. We define (with DEFs) inoctets and
+outoctets as the average values from the database myrouter.rrd and draw
+an area for the "in" traffic and a line for the "out" traffic.
+
+View the image and keep logging data for a few more days.
+If you like, you could try the examples from the test database and
+see if you can get various options and calculations to work.
+
+Suggestion: Display in bytes per second and in bits per second. Make
+the Ethernet graphics go red if they are over four megabits per
+second.
+
+=head2 Consolidation Functions
+
+A few paragraphs back I mentioned the possibility of keeping
+the maximum values instead of the average values. Let's go
+into this a bit more.
+
+Recall all the stuff about the speed of the car. Suppose we drove at 144
+KM/H during 5 minutes and then were stopped by the police for 25 minutes.
+At the end of the lecture we would take our laptop and create and view the
+image taken from the database. If we look at the second RRA we did
+create, we would have the average from 6 samples. The samples measured
+would be 144+0+0+0+0+0=144, divided by 30 minutes, corrected for the
+error by 1000, translated into KM/H, with a result of 24 KM/H.
+I would still get a ticket but not for speeding anymore :)
+
+Obviously, in this case we shouldn't look at the averages. In some
+cases they are handy. If you want to know how many KM you had traveled,
+the averaged picture would be the right one to look at. On the other hand, for
+the speed that we traveled at, the maximum numbers seen is much more
+interesting. Later we will see more types.
+
+It is the same for data. If you want to know the amount, look at the
+averages. If you want to know the rate, look at the maximum.
+Over time, they will grow apart more and more. In the last database
+we have created, there are two archives that keep data per day. The
+archive that keeps averages will show low numbers, the archive that
+shows maxima will have higher numbers.
+
+For my car this would translate in averages per day of 96/24=4 KM/H
+(as I travel about 94 kilometers on a day) during working days, and
+maxima of 120 KM/H (my top speed that I reach every day).
+
+Big difference. Do not look at the second graph to estimate the
+distances that I travel and do not look at the first graph to
+estimate my speed. This will work if the samples are close together,
+as they are in five minutes, but not if you average.
+
+On some days, I go for a long ride. If I go across Europe and travel
+for 12 hours, the first graph will rise to about 60 KM/H. The second
+one will show 180 KM/H. This means that I traveled a distance of 60
+KM/H times 24 H = 1440 KM. I did this with a higher speed and a
+maximum around 180 KM/H. However, it probably doesn't mean that I
+traveled for 8 hours at a constant speed of 180 KM/H!
+
+This is a real example: go with the flow through Germany (fast!) and stop
+a few times for gas and coffee. Drive slowly through Austria and the
+Netherlands. Be careful in the mountains and villages. If you would
+look at the graphs created from the five-minute averages you would
+get a totally different picture. You would see the same values on the
+average and maximum graphs (provided I measured every 300 seconds).
+You would be able to see when I stopped, when I was in top gear, when
+I drove over fast highways etc. The granularity of the data is much
+higher, so you can see more. However, this takes 12 samples per hour,
+or 288 values per day, so it would be a lot of data over a longer
+period of time. Therefore we average it, eventually to one value per
+day. From this one value, we cannot see much detail, of course.
+
+Make sure you understand the last few paragraphs. There is no value
+in only a line and a few axis, you need to know what they mean and
+interpret the data in ana appropriate way. This is true for all data.
+
+The biggest mistake you can make is to use the collected data for
+something that it is not suitable for. You would be better off if
+you didn't have the graph at all.
+
+
+=head2 Let's review what you now should know
+
+You know how to create a database and can put data in it. You can get
+the numbers out again by creating an image, do math on the data from
+the database and view the resulte instead of the raw data. You know
+about the difference between averages and maxima, and when to use
+which (or at least you should have an idea).
+
+RRDtool can do more than what we have learned up to now. Before you
+continue with the rest of this doc, I recommend that you reread from
+the start and try some modifications on the examples. Make sure you
+fully understand everything. It will be worth the effort and helps
+you not only with the rest of this tutorial, but also in your day to day
+monitoring long after you read this introduction.
+
+=head2 Data Source Types
+
+All right, you feel like continuing. Welcome back and get ready
+for an increased speed in the examples and explanations.
+
+You know that in order to view a counter over time, you have to
+take two numbers and divide the difference of them between the
+time lapsed. This makes sense for the examples I gave you but there
+are other possibilities. For instance, I'm able to retrieve the
+temperature from my router in three places namely the inlet, the
+so called hot-spot and the exhaust. These values are not counters.
+If I take the difference of the two samples and divide that by
+300 seconds I would be asking for the temperature change per second.
+Hopefully this is zero! If not, the computer room is probably on fire :)
+
+So, what can we do? We can tell RRDtool to store the values we measure
+directly as they are (this is not entirely true but close enough). The
+graphs we make will look much better, they will show a rather constant
+value. I know when the router is busy (it
+works -> it uses more electricity -> it generates more heat -> the
+temperature rises). I know when the doors are left open (the room is
+air conditioned) -> the warm air from the rest of the building flows into the
+computer room -> the inlet temperature rises). Etc. The data type we
+use when creating the database before was counter, we now have a
+different data type and thus a different name for it. It is called
+GAUGE. There are more such data types:
+
+ - COUNTER we already know this one
+ - GAUGE we just learned this one
+ - DERIVE
+ - ABSOLUTE
+
+The two additional types are DERIVE and ABSOLUTE. Absolute can be used like
+counter with one difference: RRDtool assumes the counter is reset when
+it's read. That is: its delta is known without calculation by RRDtool
+whereas RRDtool needs to calculate it for the counter type.
+Example: our first example (12'345, 12'357, 12'363, 12'363) would read:
+unknown, 12, 6, 0. The rest of the calculations stay the same.
+The other one, derive, is like counter. Unlike counter, it can also
+decrease so it can have a negative delta. Again, the rest of the
+calculations stay the same.
+
+Let's try them all:
+
+ rrdtool create all.rrd --start 978300900 \
+ DS:a:COUNTER:600:U:U \
+ DS:b:GAUGE:600:U:U \
+ DS:c:DERIVE:600:U:U \
+ DS:d:ABSOLUTE:600:U:U \
+ RRA:AVERAGE:0.5:1:10
+ rrdtool update all.rrd \
+ 978301200:300:1:600:300 \
+ 978301500:600:3:1200:600 \
+ 978301800:900:5:1800:900 \
+ 978302100:1200:3:2400:1200 \
+ 978302400:1500:1:2400:1500 \
+ 978302700:1800:2:1800:1800 \
+ 978303000:2100:4:0:2100 \
+ 978303300:2400:6:600:2400 \
+ 978303600:2700:4:600:2700 \
+ 978303900:3000:2:1200:3000
+ rrdtool graph all1.png -s 978300600 -e 978304200 -h 400 \
+ DEF:linea=all.rrd:a:AVERAGE LINE3:linea#FF0000:"Line A" \
+ DEF:lineb=all.rrd:b:AVERAGE LINE3:lineb#00FF00:"Line B" \
+ DEF:linec=all.rrd:c:AVERAGE LINE3:linec#0000FF:"Line C" \
+ DEF:lined=all.rrd:d:AVERAGE LINE3:lined#000000:"Line D"
+
+=head2 RRDtool under the Microscope
+
+=over 2
+
+=item *
+
+Line A is a COUNTER type, so it should continuously increment and RRDtool
+must calculate the differences. Also, RRDtool needs to divide the
+difference by the amount of time lapsed. This should end up as a
+straight line at 1 (the deltas are 300, the time is 300).
+
+=item *
+
+Line B is of type GAUGE. These are "real" values so they should match
+what we put in: a sort of a wave.
+
+=item *
+
+Line C is of type DERIVE. It should be a counter that can decrease. It does
+so between 2'400 and 0, with 1'800 in-between.
+
+=item *
+
+Line D is of type ABSOLUTE. This is like counter but it works on
+values without calculating the difference. The numbers are the same
+and as you can see (hopefully) this has a different result.
+
+=back
+
+This translates in the following values, starting at 23:10 and ending
+at 00:10 the next day (where "u" means unknown/unplotted):
+
+ - Line A: u u 1 1 1 1 1 1 1 1 1 u
+ - Line B: u 1 3 5 3 1 2 4 6 4 2 u
+ - Line C: u u 2 2 2 0 -2 -6 2 0 2 u
+ - Line D: u 1 2 3 4 5 6 7 8 9 10 u
+
+If your PNG shows all this, you know you have entered the data correctly,
+the RRDtool executable is working properly, your viewer doesn't fool you,
+and you successfully entered the year 2000 :)
+
+You could try the same example four times, each time with only one of
+the lines.
+
+Let's go over the data again:
+
+=over 2
+
+=item *
+
+Line A: 300,600,900 and so on. The counter delta is a constant 300 and
+so is the time delta. A number divided by itself is always 1 (except
+when dividing by zero which is undefined/illegal).
+
+Why is it that the first point is unknown? We do know what we put into
+the database, right? True, But we didn't have a value to calculate the delta
+from, so we don't know where we started. It would be wrong to assume we
+started at zero so we don't!
+
+=item *
+
+Line B: There is nothing to calculate. The numbers are as they are.
+
+=item *
+
+Line C: Again, the start-out value is unknown. The same story is holds
+as for line A. In this case the deltas are not constant, therefore the line
+is not either. If we would put the same numbers in the database as we did for
+line A, we would have gotten the same line. Unlike type counter,
+this type can decrease and I hope to show you later on why
+this makes a difference.
+
+=item *
+
+Line D: Here the device calculates the deltas. Therefore we DO know the
+first delta and it is plotted. We had the same input as with line A, but
+the meaning of this input is different and thus the line is different.
+In this case the deltas increase each time with 300. The time delta
+stays at a constant 300 and therefore the division of the two gives
+increasing values.
+
+=back
+
+=head2 Counter Wraps
+
+There are a few more basics to show. Some important options are still to
+be covered and we haven't look at counter wraps yet. First the counter wrap:
+In our car we notice that the counter shows 999'987. We travel 20 KM and
+the counter should go to 1'000'007. Unfortunately, there are only six digits
+on our counter so it really shows 000'007. If we would plot that on a type
+DERIVE, it would mean that the counter was set back 999'980 KM. It wasn't,
+and there has to be some protection for this. This protection is only
+available for type COUNTER which should be used for this kind of counter
+anyways. How does it work? Type counter should never decrease and
+therefore RRDtool must assume it wrapped if it does decrease!
+If the delta is negative, this can be compensated for by adding the
+maximum value of the counter + 1. For our car this would be:
+
+ Delta = 7 - 999'987 = -999'980 (instead of 1'000'007-999'987=20)
+
+ Real delta = -999'980 + 999'999 + 1 = 20
+
+At the time of writing this document, RRDtool knows of counters that
+are either 32 bits or 64 bits of size. These counters can handle the
+following different values:
+
+ - 32 bits: 0 .. 4'294'967'295
+ - 64 bits: 0 .. 18'446'744'073'709'551'615
+
+If these numbers look strange to you, you can view them in
+their hexadecimal form:
+
+ - 32 bits: 0 .. FFFFFFFF
+ - 64 bits: 0 .. FFFFFFFFFFFFFFFF
+
+RRDtool handles both counters the same. If an overflow occurs and
+the delta would be negative, RRDtool first adds the maximum of a small
+counter + 1 to the delta. If the delta is still negative, it had to be
+the large counter that wrapped. Add the maximum possible value of the
+large counter + 1 and subtract the erroneously added small value.
+
+There is a risk in this: suppose the large counter wrapped while adding
+a huge delta, it could happen, theoretically, that adding the smaller value
+would make the delta positive. In this unlikely case the results would
+not be correct. The increase should be nearly as high as the maximum
+counter value for that to happen, so chances are you would have several
+other problems as well and this particular problem would not even be
+worth thinking about. Even though, I did include an example, so you
+can judge for yourself.
+
+The next section gives you some numerical examples for counter-wraps.
+Try to do the calculations yourself or just believe me if your calculator
+can't handle the numbers :)
+
+Correction numbers:
+
+ - 32 bits: (4'294'967'295 + 1) = 4'294'967'296
+ - 64 bits: (18'446'744'073'709'551'615 + 1)
+ - correction1 = 18'446'744'069'414'584'320
+
+ Before: 4'294'967'200
+ Increase: 100
+ Should become: 4'294'967'300
+ But really is: 4
+ Delta: -4'294'967'196
+ Correction1: -4'294'967'196 + 4'294'967'296 = 100
+
+ Before: 18'446'744'073'709'551'000
+ Increase: 800
+ Should become: 18'446'744'073'709'551'800
+ But really is: 184
+ Delta: -18'446'744'073'709'550'816
+ Correction1: -18'446'744'073'709'550'816
+ + 4'294'967'296 = -18'446'744'069'414'583'520
+ Correction2: -18'446'744'069'414'583'520
+ + 18'446'744'069'414'584'320 = 800
+
+ Before: 18'446'744'073'709'551'615 ( maximum value )
+ Increase: 18'446'744'069'414'584'320 ( absurd increase, minimum for
+ Should become: 36'893'488'143'124'135'935 this example to work )
+ But really is: 18'446'744'069'414'584'319
+ Delta: -4'294'967'296
+ Correction1: -4'294'967'296 + 4'294'967'296 = 0
+ (not negative -> no correction2)
+
+ Before: 18'446'744'073'709'551'615 ( maximum value )
+ Increase: 18'446'744'069'414'584'319 ( one less increase )
+ Should become: 36'893'488'143'124'135'934
+ But really is: 18'446'744'069'414'584'318
+ Delta: -4'294'967'297
+ Correction1: -4'294'967'297 + 4'294'967'296 = -1
+ Correction2: -1 + 18'446'744'069'414'584'320 = 18'446'744'069'414'584'319
+
+As you can see from the last two examples, you need strange numbers
+for RRDtool to fail (provided it's bug free of course), so this should
+not happen. However, SNMP or whatever method you choose to collect the
+data, might also report wrong numbers occasionally. We can't prevent all
+errors, but there are some things we can do. The RRDtool "create" command
+takes two special parameters for this. They define
+the minimum and maximum allowed values. Until now, we used "U", meaning
+"unknown". If you provide values for one or both of them and if RRDtool
+receives data points that are outside these limits, it will ignore those
+values. For a thermometer in degrees Celsius, the absolute minimum is
+just under -273. For my router, I can assume this minimum is much higher
+so I would set it to 10, where as the maximum temperature I would
+set to 80. Any higher and the device would be out of order.
+
+For the speed of my car, I would never expect negative numbers and
+also I would not expect a speed higher than 230. Anything else,
+and there must have been an error. Remember: the opposite is not true,
+if the numbers pass this check, it doesn't mean that they are
+correct. Always judge the graph with a healthy dose of suspicion if it
+seems weird to you.
+
+=head2 Data Resampling
+
+One important feature of RRDtool has not been explained yet: it is
+virtually impossible to collect data and feed it into RRDtool on exact
+intervals. RRDtool therefore interpolates the data, so they are stored
+on exact intervals. If you do not know what this means or how it
+works, then here's the help you seek:
+
+Suppose a counter increases by exactly one for every second. You want
+to measure it in 300 seconds intervals. You should retrieve values
+that are exactly 300 apart. However, due to various circumstances you
+are a few seconds late and the interval is 303. The delta will also be
+303 in that case. Obviously, RRDtool should not put 303 in the database
+and make you believe that the counter increased by 303 in 300 seconds.
+This is where RRDtool interpolates: it alters the 303 value as if it
+would have been stored earlier and it will be 300 in 300 seconds.
+Next time you are at exactly the right time. This means that the current
+interval is 297 seconds and also the counter increased by 297. Again,
+RRDtool interpolates and stores 300 as it should be.
+
+ in the RRD in reality
+
+ time+000: 0 delta="U" time+000: 0 delta="U"
+ time+300: 300 delta=300 time+300: 300 delta=300
+ time+600: 600 delta=300 time+603: 603 delta=303
+ time+900: 900 delta=300 time+900: 900 delta=297
+
+Let's create two identical databases. I've chosen the time range 920'805'000
+to 920'805'900 as this goes very well with the example numbers.
+
+ rrdtool create seconds1.rrd \
+ --start 920804700 \
+ DS:seconds:COUNTER:600:U:U \
+ RRA:AVERAGE:0.5:1:24
+
+Make a copy
+
+ for Unix: cp seconds1.rrd seconds2.rrd
+ for Dos: copy seconds1.rrd seconds2.rrd
+ for vms: how would I know :)
+
+Put in some data
+
+ rrdtool update seconds1.rrd \
+ 920805000:000 920805300:300 920805600:600 920805900:900
+ rrdtool update seconds2.rrd \
+ 920805000:000 920805300:300 920805603:603 920805900:900
+
+Create output
+
+ rrdtool graph seconds1.png \
+ --start 920804700 --end 920806200 \
+ --height 200 \
+ --upper-limit 1.05 --lower-limit 0.95 --rigid \
+ DEF:seconds=seconds1.rrd:seconds:AVERAGE \
+ CDEF:unknown=seconds,UN \
+ LINE2:seconds#0000FF \
+ AREA:unknown#FF0000
+ rrdtool graph seconds2.png \
+ --start 920804700 --end 920806200 \
+ --height 200 \
+ --upper-limit 1.05 --lower-limit 0.95 --rigid \
+ DEF:seconds=seconds2.rrd:seconds:AVERAGE \
+ CDEF:unknown=seconds,UN \
+ LINE2:seconds#0000FF \
+ AREA:unknown#FF0000
+
+View both images together (add them to your index.html file)
+and compare. Both graphs should show the same, despite the
+input being different.
+
+=head1 WRAPUP
+
+It's time now to wrap up this tutorial. We covered all the basics for
+you to be able to work with RRDtool and to read the additional
+documentation available. There is plenty more to discover about
+RRDtool and you will find more and more uses for this package. You can
+easly create graphs using just the examples provided and using only
+RRDtool. You can also use one of the front ends to RRDtool that are
+available.
+
+=head1 MAILINGLIST
+
+Remember to subscribe to the RRDtool mailing list. Even if you are not
+answering to mails that come by, it helps both you and the rest of the
+users. A lot of the stuff that I know about MRTG (and therefore about
+RRDtool) I've learned while just reading the list without posting to
+it. I did not need to ask the basic questions as they are answered in
+the FAQ (read it!) and in various mails by other users. With
+thousands of users all over the world, there will always be people who
+ask questions that you can answer because you read this and other
+documentation and they didn't.
+
+=head1 SEE ALSO
+
+The RRDtool manpages
+
+=head1 AUTHOR
+
+I hope you enjoyed the examples and their descriptions. If you do, help
+other people by pointing them to this document when they are asking
+basic questions. They will not only get their answers, but at the same
+time learn a whole lot more.
+
+Alex van den Bogaerdt
+E<lt>alex@ergens.op.het.netE<gt>
+
diff --git a/program/doc/rrdupdate.pod b/program/doc/rrdupdate.pod
--- /dev/null
@@ -0,0 +1,101 @@
+=head1 NAME
+
+rrdupdate - Store a new set of values into the RRD
+
+=head1 SYNOPSIS
+
+B<rrdtool> {B<update> | B<updatev>} I<filename>
+S<[B<--template>|B<-t> I<ds-name>[B<:>I<ds-name>]...]>
+S<B<N>|I<timestamp>B<:>I<value>[B<:>I<value>...]>
+S<I<at-timestamp>B<@>I<value>[B<:>I<value>...]>
+S<[I<timestamp>B<:>I<value>[B<:>I<value>...] ...]>
+
+=head1 DESCRIPTION
+
+The B<update> function feeds new data values into an B<RRD>. The data
+is time aligned (interpolated) according to the properties of the
+B<RRD> to which the data is written.
+
+=over 8
+
+=item B<updatev>
+
+This alternate version of B<update> takes the same arguments and
+performs the same function. The I<v> stands for I<verbose>, which
+describes the output returned. B<updatev> returns a list of any and all
+consolidated data points (CDPs) written to disk as a result of the
+invocation of update. The values are indexed by timestamp (time_t),
+RRA (consolidation function and PDPs per CDP), and data source (name).
+Note that depending on the arguments of the current and previous call to
+update, the list may have no entries or a large number of entries.
+
+=item I<filename>
+
+The name of the B<RRD> you want to update.
+
+=item B<--template>|B<-t> I<ds-name>[B<:>I<ds-name>]...
+
+By default, the B<update> function expects its data input in the order
+the data sources are defined in the RRD, excluding any COMPUTE data
+sources (i.e. if the third data source B<DST> is COMPUTE, the third
+input value will be mapped to the fourth data source in the B<RRD> and
+so on). This is not very error resistant, as you might be sending the
+wrong data into an RRD.
+
+The template switch allows you to specify which data sources you are
+going to update and in which order. If the data sources specified in
+the template are not available in the RRD file, the update process
+will abort with an error message.
+
+While it appears possible with the template switch to update data sources
+asynchronously, B<RRDtool> implicitly assigns non-COMPUTE data sources missing
+from the template the I<*UNKNOWN*> value.
+
+Do not specify a value for a COMPUTE B<DST> in the B<update>
+function. If this is done accidentally (and this can only be done
+using the template switch), B<RRDtool> will ignore the value specified
+for the COMPUTE B<DST>.
+
+=item B<N>|I<timestamp>B<:>I<value>[B<:>I<value>...]
+
+The data used for updating the RRD was acquired at a certain
+time. This time can either be defined in seconds since 1970-01-01 or
+by using the letter 'N', in which case the update time is set to be
+the current time. Negative time values are subtracted from the current
+time. An AT_STYLE TIME SPECIFICATION (see the I<rrdfetch>
+documentation) may also be used by delimiting the end of the time
+specification with the '@' character instead of a ':'. Getting the
+timing right to the second is especially important when you are
+working with data-sources of type B<COUNTER>, B<DERIVE> or
+B<ABSOLUTE>.
+
+The remaining elements of the argument are DS updates. The order of
+this list is the same as the order the data sources were defined in
+the RRA. If there is no data for a certain data-source, the letter
+B<U> (e.g., N:0.1:U:1) can be specified.
+
+The format of the value acquired from the data source is dependent on
+the data source type chosen. Normally it will be numeric, but the data
+acquisition modules may impose their very own parsing of this
+parameter as long as the colon (B<:>) remains the data source value
+separator.
+
+=back
+
+=head1 EXAMPLE
+
+C<rrdtool update demo1.rrd N:3.44:3.15:U:23>
+
+Update the database file demo1.rrd with 3 known and one I<*UNKNOWN*>
+value. Use the current time as the update time.
+
+C<rrdtool update demo2.rrd 887457267:U 887457521:22 887457903:2.7>
+
+Update the database file demo2.rrd which expects data from a single
+data-source, three times. First with an I<*UNKNOWN*> value then with two
+regular readings. The update interval seems to be around 300 seconds.
+
+=head1 AUTHOR
+
+Tobias Oetiker <tobi@oetiker.ch>
+
diff --git a/program/doc/rrdxport.pod b/program/doc/rrdxport.pod
--- /dev/null
+++ b/program/doc/rrdxport.pod
@@ -0,0 +1,144 @@
+=head1 NAME
+
+rrdxport - Export data in XML format based on data from one or several RRD
+
+=head1 SYNOPSIS
+
+B<rrdtool> B<xport>
+S<[B<-s>|B<--start> I<seconds>]>
+S<[B<-e>|B<--end> I<seconds>]>
+S<[B<-m>|B<--maxrows> I<rows>]>
+S<[B<--step> I<value>]>
+S<[B<DEF:>I<vname>B<=>I<rrd>B<:>I<ds-name>B<:>I<CF>]>
+S<[B<CDEF:>I<vname>B<=>I<rpn-expression>]>
+S<[B<XPORT>B<:>I<vname>[B<:>I<legend>]]>
+
+=head1 DESCRIPTION
+
+The B<xport> function's main purpose is to write an XML formatted
+representation of the data stored in one or several B<RRD>s. It
+can also extract numerical reports.
+
+If no I<XPORT> statements are found, there will be no output.
+
+=over
+
+=item B<-s>|B<--start> I<seconds> (default end-1day)
+
+The time when the exported range should begin. Time in seconds since
+epoch (1970-01-01) is required. Negative numbers are relative to the
+current time. By default one day worth of data will be printed.
+See also AT-STYLE TIME SPECIFICATION section in the I<rrdfetch>
+documentation for a detailed explanation on how to specify time.
+
+=item B<-e>|B<--end> I<seconds> (default now)
+
+The time when the exported range should end. Time in seconds since epoch.
+See also AT-STYLE TIME SPECIFICATION section in the I<rrdfetch>
+documentation for a detailed explanation of ways to specify time.
+
+=item B<-m>|B<--maxrows> I<rows> (default 400 rows)
+
+This works like the B<-w>|B<--width> parameter of I<rrdgraph>.
+In fact it is exactly the same, but the parameter was renamed to
+describe its purpose in this module. See I<rrdgraph> documentation
+for details.
+
+=item B<--step> I<value> (default automatic)
+
+See L<rrdgraph> documentation.
+
+=item B<--enumds>
+
+The generated xml should contain the data values in enumerated tags.
+
+ <v0>val</v0><v1>val</v1>
+
+=item B<DEF:>I<vname>B<=>I<rrd>B<:>I<ds-name>B<:>I<CF>
+
+See I<rrdgraph> documentation.
+
+=item B<CDEF:>I<vname>B<=>I<rpn-expression>
+
+See I<rrdgraph> documentation.
+
+=item B<XPORT:>I<vname>B<:>B<:>I<legend>
+
+At least one I<XPORT> statement should be present. The values
+referenced by I<vname> are printed. Optionally add a legend.
+
+=back
+
+=head1 Output format
+
+The output is enclosed in an B<xport> element and contains two
+blocks. The first block is enclosed by a B<meta> element and
+contains some meta data. The second block is enclosed by a
+B<data> element and contains the data rows.
+
+Let's assume that the I<xport> command looks like this:
+
+ rrdtool xport \
+ --start now-1h --end now \
+ DEF:xx=host-inout.lo.rrd:output:AVERAGE \
+ DEF:yy=host-inout.lo.rrd:input:AVERAGE \
+ CDEF:aa=xx,yy,+,8,* \
+ XPORT:xx:"out bytes" \
+ XPORT:aa:"in and out bits"
+
+The resulting meta data section is (the values will depend on the
+RRD characteristics):
+
+ <meta>
+ <start>1020611700</start>
+ <step>300</step>
+ <end>1020615600</end>
+ <rows>14</rows>
+ <columns>2</columns>
+ <legend>
+ <entry>out bytes</entry>
+ <entry>in and out bits</entry>
+ </legend>
+ </meta>
+
+The resulting data section is:
+
+ <data>
+ <row><t>1020611700</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020612000</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020612300</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020612600</t><v>3.4113333333e+00</v><v>5.4581333333e+01</v></row>
+ <row><t>1020612900</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020613200</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020613500</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020613800</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020614100</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020614400</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020614700</t><v>3.7333333333e+00</v><v>5.9733333333e+01</v></row>
+ <row><t>1020615000</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020615300</t><v>3.4000000000e+00</v><v>5.4400000000e+01</v></row>
+ <row><t>1020615600</t><v>NaN</v><v>NaN</v></row>
+ </data>
+
+
+=head1 EXAMPLE 1
+
+ rrdtool xport \
+ DEF:out=if1-inouts.rrd:outoctets:AVERAGE \
+ XPORT:out:"out bytes"
+
+=head1 EXAMPLE 2
+
+ rrdtool xport \
+ DEF:out1=if1-inouts.rrd:outoctets:AVERAGE \
+ DEF:out2=if2-inouts.rrd:outoctets:AVERAGE \
+ CDEF:sum=out1,out2,+ \
+ XPORT:out1:"if1 out bytes" \
+ XPORT:out2:"if2 out bytes" \
+ XPORT:sum:"output sum"
+
+
+=head1 AUTHOR
+
+Tobias Oetiker E<lt>tobi@oetiker.chE<gt>
+
diff --git a/program/examples/4charts.pl.in b/program/examples/4charts.pl.in
--- /dev/null
@@ -0,0 +1,124 @@
+#! @PERL@
+
+#makes things work when run without install
+use lib qw( @prefix@/lib/perl );
+
+use RRDs;
+
+my $start=time;
+my $rrd="randome.rrd";
+my $name = $0;
+$name =~ s/.*\///g;
+$name =~ s/\.pl.*//g;
+
+RRDs::create ($rrd, "--start",$start-1, "--step",300,
+ "DS:a:GAUGE:600:U:U",
+ "DS:b:GAUGE:600:U:U",
+ "RRA:AVERAGE:0.5:1:300",
+ "RRA:MIN:0.5:12:300",
+ "RRA:MAX:0.5:12:300",
+);
+
+my $ERROR = RRDs::error;
+die "$0: unable to create `$rrd': $ERROR\n" if $ERROR;
+
+# dropt some data into the rrd
+my $t;
+for ($t=$start; $t<$start+300*300; $t+=300){
+ RRDs::update $rrd, "$t:".(sin($t/3000)*50+50).":".(sin($t/2500)*50+50);
+ if ($ERROR = RRDs::error) {
+ die "$0: unable to update `$rrd': $ERROR\n";
+ }
+}
+
+my $c1="f57912a0";
+my $c2="2a79e9a0";
+my $w=300;
+my $h=140;
+
+RRDs::graph "$name-L.png",
+ "--title", "2 LINES",
+ "--start", "now",
+ "--end", "start+15h",
+ "--lower-limit=0",
+ "--interlace",
+ "--imgformat","PNG",
+ "--width=$w",
+ "--height=$h",
+ "DEF:a=$rrd:a:AVERAGE",
+ "DEF:b=$rrd:b:AVERAGE",
+ "LINE1:a#$c1:Value A",
+ "LINE3:b#$c2:Value B",
+;
+
+RRDs::graph "$name-A.png",
+ "--title", "LINE and AREA",
+ "--start", "now",
+ "--end", "start+15h",
+ "--lower-limit=0",
+ "--interlace",
+ "--imgformat","PNG",
+ "--width=$w",
+ "--height=$h",
+ "DEF:a=$rrd:a:AVERAGE",
+ "DEF:b=$rrd:b:AVERAGE",
+ "AREA:a#$c1:Value A",
+ "LINE2:b#$c2:Value B",
+;
+
+RRDs::graph "$name-S.png",
+ "--title", "STACKED AREAS",
+ "--start", "now",
+ "--end", "start+15h",
+ "--lower-limit=0",
+ "--interlace",
+ "--imgformat","PNG",
+ "--width=$w",
+ "--height=$h",
+ "DEF:a=$rrd:a:AVERAGE",
+ "DEF:b=$rrd:b:AVERAGE",
+ "AREA:a#$c1:Value A",
+ "STACK:b#$c2:Value B",
+;
+
+
+RRDs::graph "$name-M.png",
+ "--title", "RPN Magic",
+ "--start", "now",
+ "--end", "start+15h",
+ "--lower-limit=0",
+ "--interlace",
+ "--imgformat","PNG",
+ "--width=$w",
+ "--height=$h",
+ "DEF:a=$rrd:a:AVERAGE",
+ "DEF:b=$rrd:b:AVERAGE",
+ "CDEF:alpha=TIME,3600,%,1800,LT,a,UNKN,IF",
+ "CDEF:beta=TIME,3600,%,1800,GE,b,UNKN,IF",
+ "AREA:alpha#$c1:Value A",
+ "LINE1:a#$c1",
+ "AREA:beta#$c2:Value B",
+ "LINE1:b#$c2",
+;
+
+RRDs::graph "$name-sample.png",
+ "--title", "Sample",
+ "--start", "now",
+ "--end", "start+15h",
+ "--lower-limit=0",
+ "--interlace",
+ "--imgformat","PNG",
+ "--width=600",
+ "--height=50",
+ "DEF:a=$rrd:a:AVERAGE",
+ "DEF:b=$rrd:a:MAX",
+ "AREA:a#00ff00:Incoming",
+ "LINE1:b#ff0000:Max Incoming",
+;
+
+if ($ERROR = RRDs::error) {
+ die "ERROR: $ERROR\n";
+};
+
+print "This script has created $name.png in the current directory\n";
+print "This demonstrates the use of the TIME and % RPN operators\n";
diff --git a/program/examples/Makefile.am b/program/examples/Makefile.am
--- /dev/null
@@ -0,0 +1,16 @@
+## Process this file with automake to produce Makefile.in
+
+#AUTOMAKE_OPTIONS = foreign
+
+#ACLOCAL_M4 = $(top_srcdir)/config/aclocal.m4
+
+EXTRA_DIST = cgi-demo.cgi.in
+
+examplesdir = $(pkgdatadir)/examples
+examples_SCRIPTS = cgi-demo.cgi piped-demo.pl shared-demo.pl \
+ stripes.pl bigtops.pl minmax.pl 4charts.pl perftest.pl
+
+cgi-demo.cgi: cgi-demo.cgi.in $(top_builddir)/config.status
+ sed 's,@''exec_prefix@,$(exec_prefix),' cgi-demo.cgi.in > $@
+ chmod a+x $@
+
diff --git a/program/examples/bigtops.pl.in b/program/examples/bigtops.pl.in
--- /dev/null
@@ -0,0 +1,50 @@
+#! @PERL@
+# this is for after install
+use lib qw( @prefix@/lib/perl );
+
+use RRDs;
+my $start=time;
+my $rrd="randome.rrd";
+my $name = $0;
+$name =~ s/.*\///g;
+$name =~ s/\.pl.*//g;
+
+RRDs::create ($rrd, "--start",$start-1, "--step",300,
+ "DS:a:GAUGE:600:U:U",
+ "DS:b:GAUGE:600:U:U",
+ "RRA:AVERAGE:0.5:1:300");
+my $ERROR = RRDs::error;
+die "$0: unable to create `$rrd': $ERROR\n" if $ERROR;
+
+# dropt some data into the rrd
+my $t;
+for ($t=$start; $t<$start+300*300; $t+=300){
+ RRDs::update $rrd, "$t:".rand(100).":".(sin($t/800)*50+50);
+ if ($ERROR = RRDs::error) {
+ die "$0: unable to update `$rrd': $ERROR\n";
+ }
+}
+
+RRDs::graph "$name.png",
+ "--title", uc($name)." Demo",
+ "--start", "$start + 1 h",
+ "--end", "start + 1000 min",
+ "--interlace",
+ "--imgformat","PNG",
+ "--width=450",
+ "DEF:a=$rrd:a:AVERAGE",
+ "DEF:b=$rrd:b:AVERAGE",
+ "CDEF:line=TIME,2400,%,300,LT,a,UNKN,IF",
+ "AREA:b#00b6e4:beta",
+ "AREA:line#0022e9:alpha",
+ "LINE3:line#ff0000",
+
+;
+
+if ($ERROR = RRDs::error) {
+ die "ERROR: $ERROR\n";
+};
+
+
+print "This script has created $name.png in the current directory\n";
+print "This demonstrates the use of the TIME and % RPN operators\n";
diff --git a/program/examples/cgi-demo.cgi.in b/program/examples/cgi-demo.cgi.in
--- /dev/null
@@ -0,0 +1,40 @@
+#! @exec_prefix@/bin/rrdcgi
+
+<HTML>
+<HEAD>
+<TITLE>RRDCGI Demo</TITLE>
+</HEAD>
+<BODY>
+Note: This Demo will only work if have previously run
+the <TT>shared-demo.pl</TT>.
+
+<H1>This is NOT traffic</H1>
+
+
+<P><RRD::GRAPH cgi-demo1.png
+ --lower-limit 0
+ --start 'end-10h'
+ --title "Graph in Localtime <RRD::TIME::NOW %c>"
+ DEF:alpha=shared-demo.rrd:a:AVERAGE
+ DEF:beta=shared-demo.rrd:b:AVERAGE
+ AREA:alpha#0022e9:"Trees on Mars"
+ STACK:beta#00b871:"Elchs in Norway">
+</P>
+
+<P><RRD::SETENV TZ UTC>
+ <RRD::GRAPH cgi-demo2.png
+ --lower-limit 0
+ --start 'end-10h'
+ --title "Graph in UTC"
+ DEF:alpha=shared-demo.rrd:a:AVERAGE
+ DEF:beta=shared-demo.rrd:b:AVERAGE
+ AREA:alpha#0022e9:"Trees on Mars"
+ STACK:beta#00b871:"Elchs in Norway">
+</P>
+
+</BODY>
+</HTML>
+
+
+
+
diff --git a/program/examples/minmax.pl.in b/program/examples/minmax.pl.in
--- /dev/null
@@ -0,0 +1,52 @@
+#! @PERL@
+
+use lib qw( @prefix@/lib/perl );
+
+use RRDs;
+my $start=time;
+my $rrd="randome.rrd";
+my $name = $0;
+$name =~ s/.*\///g;
+$name =~ s/\.pl.*//g;
+
+RRDs::create ($rrd, "--start",$start-1, "--step",300,
+ "DS:a:GAUGE:600:U:U",
+ "RRA:AVERAGE:0.5:1:300",
+ "RRA:MIN:0.5:12:300",
+ "RRA:MAX:0.5:12:300",
+);
+my $ERROR = RRDs::error;
+die "$0: unable to create `$rrd': $ERROR\n" if $ERROR;
+
+# dropt some data into the rrd
+my $t;
+for ($t=$start; $t<$start+300*300; $t+=300){
+ RRDs::update $rrd, "$t:".(sin($t/3000)*50+50);
+ if ($ERROR = RRDs::error) {
+ die "$0: unable to update `$rrd': $ERROR\n";
+ }
+}
+
+RRDs::graph "$name.png",
+ "--title", uc($name)." Demo",
+ "--start", "now",
+ "--end", "start+1d",
+ "--lower-limit=0",
+ "--interlace",
+ "--imgformat","PNG",
+ "--width=450",
+ "DEF:a=$rrd:a:AVERAGE",
+ "DEF:b=$rrd:a:MIN",
+ "DEF:c=$rrd:a:MAX",
+ "AREA:a#00b6e4:real",
+ "LINE1:b#0022e9:min",
+ "LINE1:c#00ee22:max",
+;
+
+if ($ERROR = RRDs::error) {
+ die "ERROR: $ERROR\n";
+};
+
+
+print "This script has created $name.png in the current directory\n";
+print "This demonstrates the use of MIN and MAX archives\n";
diff --git a/program/examples/perftest.pl.in b/program/examples/perftest.pl.in
--- /dev/null
@@ -0,0 +1,193 @@
+#! @PERL@
+#
+# $Id:$
+#
+# Created By Tobi Oetiker <tobi@oetiker.ch>
+# Date 2006-10-27
+#
+#makes programm work AFTER install
+
+use lib qw( @prefix@/lib/perl );
+
+print <<NOTE;
+
+RRDtool Performance Tester
+--------------------------
+Runnion on $RRDs::VERSION;
+
+RRDtool update performance is ultimately disk-bound. Since very little data
+does actually get written to disk in a single update, the performance
+is highly dependent on the cache situation in your machine.
+
+This test tries to cater for this. It works like this:
+
+1) Create 100 RRD files (and sync them to disk)
+
+2) Update the 100 RRD file three times in a row.
+ We run the Update several times to see the difference
+ it makes in the cache.
+
+3) Go back to 1)
+
+NOTE
+
+use strict;
+use Time::HiRes qw(time);
+use RRDs;
+use IO::File;
+use Time::HiRes qw( usleep );
+
+sub create($$){
+ my $file = shift;
+ my $time = shift;
+ my $start = time; #since we loaded HiRes
+ RRDs::create ( $file.".rrd", "-b$time", qw(
+ -s300
+ DS:in:GAUGE:400:U:U
+ DS:out:GAUGE:400:U:U
+ RRA:AVERAGE:0.5:1:600
+ RRA:AVERAGE:0.5:6:600
+ RRA:MAX:0.5:6:600
+ RRA:AVERAGE:0.5:24:600
+ RRA:MAX:0.5:24:600
+ RRA:AVERAGE:0.5:144:600
+ RRA:MAX:0.5:144:600
+ ));
+ my $total = time - $start;
+ my $error = RRDs::error;
+ die $error if $error;
+ return $total;
+}
+
+sub update($$){
+ my $file = shift;
+ my $time = shift;
+ my $in = rand(1000);
+ my $out = rand(1000);
+ my $start = time;
+ my $ret = RRDs::updatev($file.".rrd", $time.":$in:$out");
+# print join("",map {" $_ " . $ret->{$_}."\n" } grep /AVERAGE.\[1\]/, sort keys %$ret)."\n** $time\n\n";
+ # sync updates to disk immediately
+# usleep(1) if (rand(3) <1 );
+ my $total = time - $start;
+ my $error = RRDs::error;
+ die $error if $error;
+ return $total;
+}
+
+sub tune($){
+ my $file = shift;
+ my $start = time;
+ RRDs::tune ($file.".rrd", "-a","in:U","-a","out:U","-d","in:GAUGE","-d","out:GAUGE");
+ my $total = time - $start;
+ my $error = RRDs::error;
+ die $error if $error;
+ return $total;
+}
+
+sub infofetch($){
+ my $file = shift;
+ my $start = time;
+ my $info = RRDs::info ($file.".rrd");
+ my $error = RRDs::error;
+ die $error if $error;
+ my $lasttime = $info->{last_update} - $info->{last_update} % $info->{step};
+ my $fetch = RRDs::fetch ($file.".rrd",'AVERAGE','-s',$lasttime-1,'-e',$lasttime);
+ my $total = time - $start;
+ my $error = RRDs::error;
+ die $error if $error;
+ return $total;
+}
+
+sub stddev ($$$){ #http://en.wikipedia.org/wiki/Standard_deviation
+ my $sum = shift;
+ my $squaresum = shift;
+ my $count = shift;
+ return sqrt( 1 / $count * ( $squaresum - $sum*$sum / $count ))
+}
+
+sub makerrds($$$$){
+ my $count = shift;
+ my $total = shift;
+ my $list = shift;
+ my $time = shift;
+ my @files;
+ for (1..$count){
+ my $id = sprintf ("%07d",$total);
+ $id =~ s/^(.)(.)(.)(.)(.)//;
+ push @$list, "$1/$2/$3/$4/$5/$id";
+ -d "$1" or mkdir "$1";
+ -d "$1/$2" or mkdir "$1/$2";
+ -d "$1/$2/$3" or mkdir "$1/$2/$3";
+ -d "$1/$2/$3/$4" or mkdir "$1/$2/$3/$4";
+ -d "$1/$2/$3/$4/$5" or mkdir "$1/$2/$3/$4/$5";
+ push @files, $list->[$total];
+ create $list->[$total++],$time-2;
+ print STDERR ".";
+ }
+ for (@files){
+ my $fd = new IO::File("$_.rrd","r");
+ if (defined $fd) {
+ $fd->sync;
+ $fd->close;
+ } else {
+ warn "failed to sync $_\n";
+ }
+ }
+ return $count;
+}
+
+
+sub main (){
+ mkdir "db-$$" or die $!;
+ chdir "db-$$";
+
+ my $step = 100000; # number of rrds to creat for every round
+
+ my @path;
+ my $time=int(time);
+
+ my $tracksize = 0;
+ my $uppntr = 0;
+
+
+ my %squaresum = ( cr => 0, up => 0 );
+ my %sum = ( cr => 0, up => 0 );
+ my %count =( cr => 0, up => 0 );
+
+ my $printtime = time;
+ while (1) {
+ # enhance the track
+ $time += 300;
+ $tracksize += makerrds $step,$tracksize,\@path,$time;
+ # run benchmark
+ for (0..10){
+ $time += 300;
+ my $count = 0;
+ my $sum = 0;
+ my $squaresum = 0;
+ for (my $i = 0; $i<$tracksize;$i ++){
+ my $elapsed = update($path[$i],$time);
+ $sum += $elapsed;
+ $squaresum += $elapsed**2;
+ $count++;
+ };
+# for (my $i = 0; $i<$tracksize;$i ++){
+# my $fh = new IO::File "$path[$i].rrd","r";
+# if (defined $fh) {
+# $fh->sync;
+# $fh->close;
+# } else {
+# warn "failed to sync $path[$i]\n";
+# }
+# }
+ my $ups = $count/$sum;
+ my $sdv = stddev($sum,$squaresum,$count);
+ printf STDERR "%4d %6.0f Up/s (%6.5f sdv)\n",$count,$ups,$sdv;
+ }
+ print STDERR "\n";
+ exit ;
+ }
+}
+
+main;
diff --git a/program/examples/piped-demo.pl.in b/program/examples/piped-demo.pl.in
--- /dev/null
@@ -0,0 +1,148 @@
+#! @PERL@
+
+use lib qw( @prefix@/lib/perl );
+
+use RRDp;
+
+# this simpulates a standard mrtg-2.x setup ... we can use this to
+# compare performance ...
+
+$main::DEBUG=0;
+$STEP = 300;
+$RUNS = 12*24*30*6;
+$GRUNS = 20;
+$RRD = "piped-demo.rrd";
+$SVG = "piped-demo.svg";
+$PNG = "piped-demo.png";
+
+# some magic to find the correct rrdtol executable
+$prefix="@prefix@";
+
+if ( -x "@exec_prefix@/bin/rrdtool") {
+ RRDp::start "@exec_prefix@/bin/rrdtool";
+} elsif ( -x "../../../bin/rrdtool") {
+ RRDp::start "../../../bin/rrdtool";
+} else {
+ RRDp::start "../src/rrdtool";
+}
+
+print "* Creating RRD with properties equivalent to mrtg-2.x logfile\n\n";
+
+$START = time()-$RUNS*$STEP;
+
+RRDp::cmd "create $RRD -b $START -s $STEP
+ DS:in:GAUGE:400:U:U
+ DS:out:GAUGE:400:U:U
+ RRA:AVERAGE:0.5:1:600
+ RRA:AVERAGE:0.5:6:600
+ RRA:MAX:0.5:6:600
+ RRA:AVERAGE:0.5:24:600
+ RRA:MAX:0.5:24:600
+ RRA:AVERAGE:0.5:144:600
+ RRA:MAX:0.5:144:600";
+
+$answer = RRDp::read;
+($user,$sys,$real) = ($RRDp::user,$RRDp::sys,$RRDp::real);
+
+print "* Filling RRD with $RUNS Values. One moment please ...\n";
+print " If you are running over NFS this will take *MUCH* longer\n\n";
+
+for ($i=$START+1;
+ $i<$START+$STEP*$RUNS;
+ $i+=$STEP+int((rand()-0.5)*7)){
+
+ $line = "update $RRD $i:".int(rand(100000)).":".int(rand(100000));
+ RRDp::cmd $line;
+ $answer = RRDp::read;
+}
+
+($user1,$sys1,$real1) = ($RRDp::user,$RRDp::sys,$RRDp::real);
+
+printf "-- performance analysis Update test\n".
+ " usr/upd: %1.5fs sys/upd: %1.5fs real/upd: %1.5fs upd/sec: %1.0f\n",
+ ($user1-$user)/($RUNS), ($sys1-$sys)/($RUNS),
+ ($real1-$real)/($RUNS), ($RUNS)/($real1-$real);
+print "\n";
+# creating some graphs
+
+print "* Creating $GRUNS SVG graphs: $SVG\n\n";
+$now = time;
+$localtime = scalar localtime(time);
+$localtime = s/:/\\:/g;
+for ($i=0;$i<$GRUNS;$i++) {
+RRDp::cmd "graph $SVG ", "--title 'Test GRAPH' ",
+ "--imgformat SVG --height 150 --vertical-label 'Dummy Units' ".
+ "--start now".(-$RUNS*$STEP),
+ "--color ARROW#bfbfbf",
+ "DEF:alpha=$RRD:in:AVERAGE",
+ "DEF:beta=$RRD:out:AVERAGE",
+ "CDEF:calc=alpha,beta,+,1.5,/",
+ "AREA:alpha#0022e9:Alpha",
+ "STACK:beta#00b871:Beta",
+ "STACK:calc#ff0091:Calc\\j",
+ "PRINT:alpha:AVERAGE:'Average Alpha\\: %1.2lf %S'",
+ "PRINT:alpha:MIN:'Min Alpha\\: %1.2lf %S'",
+ "PRINT:alpha:MAX:'Max Alpha\\: %1.2lf %S'",
+ "GPRINT:calc:AVERAGE:'Average calc\\: %1.2lf %S\\r'",
+ "GPRINT:calc:MIN:'Min calc\\: %1.2lf %S'",
+ "GPRINT:calc:MAX:'Max calc\\: %1.2lf %S'",
+ "VRULE:".($now-3600)."#008877:'60 Minutes ago'",
+ "COMMENT:'\\s'",
+ "COMMENT:'Graph created on\\: ".$localtime."\\c'";
+
+$answer = RRDp::read;
+}
+($user2,$sys2,$real2) = ($RRDp::user,$RRDp::sys,$RRDp::real);
+
+print "ANSWER:\n$$answer";
+
+printf "\n-- average Time for one Graph\n".
+ " usr/grf: %1.5fs sys/grf: %1.5fs real/grf: %1.5fs graphs/sec: %1.2f\n",
+ ($user2-$user1)/$GRUNS,
+ ($sys2-$sys1)/$GRUNS,
+ ($real2-$real1)/$GRUNS,
+ $GRUNS/($real2-$real1);
+
+print "\n\n* Creating $GRUNS PNG graphs: $PNG\n\n";
+
+$now = time;
+($user1,$sys1,$real1) = ($RRDp::user,$RRDp::sys,$RRDp::real);
+my $local = "".localtime(time());
+$local =~ s/:/\\:/g;
+
+for ($i=0;$i<$GRUNS;$i++) {
+RRDp::cmd "graph $PNG ", "--title 'Test GRAPH' ",
+ "--imgformat PNG --height 150 --vertical-label 'Dummy Units' ".
+ "--start now".(-$RUNS*$STEP),
+ "--color ARROW#bfbfbf",
+ "DEF:alpha=$RRD:in:AVERAGE",
+ "DEF:beta=$RRD:out:AVERAGE",
+ "CDEF:calc=alpha,beta,+,1.5,/",
+ "AREA:alpha#0022e9:Alpha",
+ "STACK:beta#00b871:Beta",
+ "STACK:calc#ff0091:Calc\\j",
+ "PRINT:alpha:AVERAGE:'Average Alpha\\: %1.2lf %S'",
+ "PRINT:alpha:MIN:'Min Alpha\\: %1.2lf %S'",
+ "PRINT:alpha:MAX:'Max Alpha\\: %1.2lf %S'",
+ "GPRINT:calc:AVERAGE:'Average calc\\: %1.2lf %S\\r'",
+ "GPRINT:calc:MIN:'Min calc\\: %1.2lf %S'",
+ "GPRINT:calc:MAX:'Max calc\\: %1.2lf %S'",
+ "VRULE:".($now-3600)."#008877:'60 Minutes ago'",
+ "COMMENT:'\\s'",
+ "COMMENT:'Graph created on\\: $local\\c'";
+
+$answer = RRDp::read;
+}
+($user2,$sys2,$real2) = ($RRDp::user,$RRDp::sys,$RRDp::real);
+
+print "ANSWER:\n$$answer";
+
+printf "\n-- average Time for one PNG Graph\n".
+ " usr/grf: %1.5fs sys/grf: %1.5fs real/grf: %1.5fs".
+ " graphs/sec: %1.2f\n\n",
+ ($user2-$user1)/$GRUNS,
+ ($sys2-$sys1)/$GRUNS,
+ ($real2-$real1)/$GRUNS,
+ $GRUNS/($real2-$real1);
+
+RRDp::end;
diff --git a/program/examples/shared-demo.pl.in b/program/examples/shared-demo.pl.in
--- /dev/null
@@ -0,0 +1,215 @@
+#! @PERL@
+
+
+END {
+ print "not ok 1\n" unless $loaded;
+ unlink "demo.rrd";
+}
+
+sub ok
+{
+ my($what, $result) = @_ ;
+ $ok_count++;
+ print "not " unless $result;
+ print "ok $ok_count $what\n";
+}
+
+#makes programm work AFTER install
+use lib qw( @prefix@/lib/perl );
+
+use strict;
+use vars qw(@ISA $loaded);
+
+use RRDs;
+$loaded = 1;
+my $ok_count = 1;
+
+ok("loading",1);
+
+######################### End of black magic.
+
+my $STEP = 100;
+my $RUNS = 500;
+my $GRUNS = 4;
+my $RRD1 = "shared-demo.rrd";
+my $RRD2 = "shared-demob.rrd";
+my $PNG1 = "shared-demo1.png";
+my $PNG2 = "shared-demo2.png";
+my $time = 30*int(time/30);
+my $START = $time-$RUNS*$STEP;
+
+my @options = ("-b", $START, "-s", $STEP,
+ "DS:a:GAUGE:2000:U:U",
+ "DS:b:GAUGE:200:U:U",
+ "DS:c:GAUGE:200:U:U",
+ "DS:d:GAUGE:200:U:U",
+ "DS:e:DERIVE:200:U:U",
+ "RRA:AVERAGE:0.5:1:5000",
+ "RRA:AVERAGE:0.5:10:500");
+
+print "* Creating RRD $RRD1 starting at $time.\n\n";
+RRDs::create $RRD1, @options;
+
+my $ERROR = RRDs::error;
+ok("create A", !$ERROR); # 2
+if ($ERROR) {
+ die "$0: unable to create `$RRD1': $ERROR\n";
+}
+
+print "* Creating RRD $RRD2 starting at $time.\n\n";
+RRDs::create $RRD2, @options;
+
+$ERROR= RRDs::error;
+ok("create B",!$ERROR); # 3
+if ($ERROR) {
+ die "$0: unable to create `$RRD2': $ERROR\n";
+}
+
+my $last = RRDs::last $RRD1;
+if ($ERROR = RRDs::error) {
+ die "$0: unable to get last `$RRD1': $ERROR\n";
+}
+ok("last A", $last == $START); # 4
+
+$last = RRDs::last $RRD2;
+if ($ERROR = RRDs::error) {
+ die "$0: unable to get last `$RRD2': $ERROR\n";
+}
+ok("last B", $last == $START); # 5
+
+print "* Filling $RRD1 and $RRD2 with $RUNS*5 values. One moment please ...\n";
+print "* If you are running over NFS this will take *MUCH* longer\n\n";
+
+srand(int($time / 100));
+
+@options = ();
+
+my $counter = 1e7;
+for (my $t=$START+1;
+ $t<$START+$STEP*$RUNS;
+ $t+=$STEP+int((rand()-0.5)*7)){
+ $counter += int(2500*sin($t/2000)*$STEP);
+ my $data = (1000+500*sin($t/1000)).":".
+ (1000+900*sin($t/2330)).":".
+ (2000*cos($t/1550)).":".
+ (3220*sin($t/3420)).":$counter";
+ push(@options, "$t:$data");
+ RRDs::update $RRD1, "$t:$data";
+ if ($ERROR = RRDs::error) {
+ die "$0: unable to update `$RRD1': $ERROR\n";
+ }
+}
+
+RRDs::update $RRD2, @options;
+
+if ($ERROR = RRDs::error) {
+ die "$0: unable to update `$RRD2': $ERROR\n";
+}
+
+print "* Creating $GRUNS graphs: $PNG1 & $PNG2\n\n";
+my $now = $time;
+for (my $i=0;$i<$GRUNS;$i++) {
+ my @rrd_pngs = ($RRD1, $PNG1, $RRD2, $PNG2);
+ while (@rrd_pngs) {
+ my $RRD = shift(@rrd_pngs);
+ my $PNG = shift(@rrd_pngs);
+ my ($graphret,$xs,$ys) = RRDs::graph $PNG, "--title", 'Test GRAPH',
+ '--base', '1024',
+ "--vertical-label", 'Dummy Units', "--start", (-$RUNS*$STEP),
+ "--end", $time,
+ "--interlace", "--imgformat","PNG",
+ "DEF:alpha=$RRD:a:AVERAGE",
+ "DEF:beta=$RRD:b:AVERAGE",
+ "DEF:gamma=$RRD:c:AVERAGE",
+ "DEF:delta=$RRD:d:AVERAGE",
+ "DEF:epsilon=$RRD:e:AVERAGE",
+ "CDEF:calc=alpha,beta,+,2,/,100,*,102,/",
+ "AREA:alpha#0022e9:Short",
+ "GPRINT:calc:MAX:Max calc %1.2lf",
+ "STACK:beta#00b871:Demo Text",
+ "GPRINT:calc:AVERAGE:Average calc %1.2lf",
+ "STACK:beta#0ad871:Demo Text 2",
+ "LINE1:gamma#ff0000:Line 1",
+ "LINE2:delta#888800:Line 2",
+ "LINE3:calc#00ff44:Line 3",
+ "LINE3:epsilon#000000:Line 4",
+ "HRULE:1500#ff8800:Horizontal Line at 1500",
+ "PRINT:alpha:AVERAGE:Average Alpha %1.2lf",
+ "PRINT:alpha:MIN:Min Alpha %1.2lf",
+ "PRINT:alpha:MAX:Max Alpha %1.2lf",
+ "GPRINT:calc:MIN:Min calc %1.2lf",
+ "VRULE:".($now-3600)."#008877:60 Minutes ago",
+ "VRULE:".($now-7200)."#008877:120 Minutes ago";
+
+ if ($ERROR = RRDs::error) {
+ die "ERROR: $ERROR\n";
+ } else {
+ print "Image Size: ${xs}x${ys}\n";
+ print "Graph Return:\n",(join "\n", @$graphret),"\n\n";
+ }
+ }
+}
+
+
+
+my ($start,$step,$names,$array) = RRDs::fetch $RRD1, "AVERAGE";
+$ERROR = RRDs::error;
+die "ERROR: $ERROR\n" if $ERROR ;
+print "start=$start, step=$step\n";
+print " ";
+map {printf("%12s",$_)} @$names ;
+print "\n";
+foreach my $line (@$array){
+ print "".localtime($start)," ";
+ $start += $step;
+ foreach my $val (@$line) {
+ printf "%12.1f", $val;
+ }
+ print "\n";
+}
+
+
+
+my ($start,$end,$step,$col_cnt,$legend,$data) =
+ RRDs::xport ("-m", 400,
+ "--start", "now-1day",
+ "--end", "now",
+ "DEF:alpha=$RRD1:a:AVERAGE",
+ "DEF:beta=$RRD1:d:AVERAGE",
+ "CDEF:calc=alpha,beta,+,2,/,100,*,102,/",
+ "XPORT:alpha:original ds",
+ "XPORT:calc:calculated values",
+ );
+
+my $ERROR = RRDs::error;
+die "$0: unable to xport: $ERROR\n" if $ERROR;
+
+print "\nrrdxport test:\n\n";
+print "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n\n";
+print "<xport>\n";
+print " <meta>\n";
+print " <start>$start</start>\n";
+print " <step>$step</step>\n";
+print " <end>$end</end>\n";
+print " <rows>", $#$data + 1, "</rows>\n";
+print " <columns>$col_cnt</columns>\n";
+print " <legend>\n";
+foreach my $entry (@$legend) {
+ print " <entry>$entry</entry>\n";
+}
+print " </legend>\n";
+print " </meta>\n";
+print " <data>\n";
+my $row_counter = 0;
+foreach my $row (@$data) {
+ $row_counter++;
+ print " <row id=\"$row_counter\"><t is=\"", scalar localtime($start), "\">$start</t>";
+ $start += $step;
+ foreach my $val (@$row) {
+ printf ("<v>%1.10e</v>",$val) if $val ne '';
+ print "<v>NaN</v>" if $val eq '';
+ }
+ print "</row>\n";
+}
+print " </data>\n";
+print "</xport>\n";
diff --git a/program/examples/stripes.pl.in b/program/examples/stripes.pl.in
--- /dev/null
@@ -0,0 +1,46 @@
+#! @PERL@
+use lib qw( @prefix@/lib/perl );
+
+use strict;
+use vars qw(@ISA $loaded);
+
+use RRDs;
+my $start=time;
+my $rrd="random.rrd";
+RRDs::create ($rrd, "--start",$start-1, "--step",300,
+ "DS:a:GAUGE:600:U:U",
+ "DS:b:GAUGE:600:U:U",
+ "RRA:AVERAGE:0.5:1:200");
+my $ERROR = RRDs::error;
+die "$0: unable to create `$rrd': $ERROR\n" if $ERROR;
+my $t;
+for ($t=$start; $t<$start+200*300; $t+=300){
+ RRDs::update $rrd, "$t:".rand(100).":".(sin($t/800)*50+50);
+ if ($ERROR = RRDs::error) {
+ die "$0: unable to update `$rrd': $ERROR\n";
+ }
+}
+RRDs::graph "stripes.png",
+ "--title", "Stripes Demo",
+ "--start", $start,
+ "--end", "start + 400 min",
+ "--interlace",
+ "--imgformat","PNG",
+ "--width=450",
+ "DEF:a=$rrd:a:AVERAGE",
+ "DEF:b=$rrd:b:AVERAGE",
+ "CDEF:alpha=TIME,1200,%,600,LT,a,UNKN,IF",
+ "CDEF:beta=TIME,1200,%,600,GE,b,UNKN,IF",
+ "AREA:alpha#0022e9:alpha",
+ "AREA:beta#00b674:beta",
+ "LINE1:b#ff4400:beta envelope\\c",
+ "COMMENT:\\s",
+ "COMMENT:alpha=TIME,1200,%,600,LT,a,UNKN,IF",
+ "COMMENT:beta=TIME,1200,%,600,GE,b,UNKN,IF\\j";
+if ($ERROR = RRDs::error) {
+ die "ERROR: $ERROR\n";
+};
+
+
+print "This script has created stripes.png in the current directory\n";
+print "This demonstrates the use of the TIME and % RPN operators\n";
diff --git a/program/favicon.ico b/program/favicon.ico
new file mode 100644 (file)
index 0000000..7d08dd4
Binary files /dev/null and b/program/favicon.ico differ
index 0000000..7d08dd4
Binary files /dev/null and b/program/favicon.ico differ
diff --git a/program/libraries/Makefile.am b/program/libraries/Makefile.am
--- /dev/null
@@ -0,0 +1,2 @@
+SUBDIRS=afm
+DIST_SUBDIRS=afm
diff --git a/program/libraries/afm/COPYRIGHT.txt b/program/libraries/afm/COPYRIGHT.txt
--- /dev/null
@@ -0,0 +1,17 @@
+For info on copyright, afm format, etc, go to:
+http://partners.adobe.com/asn/developer/technotes/fonts.html
+This page includes the afm file format specification.
+
+The afm files have been fetched from:
+http://partners.adobe.com/asn/developer/technotes/fontinfo/Core14_AFMs.tar
+
+Adobe copyright:
+This file and the 14 PostScript(R) AFM files it accompanies
+may be used, copied, and distributed for any purpose and
+without charge, with or without modification, provided that
+all copyright notices are retained; that the AFM files are not
+distributed without this file; that all modifications to this
+file or any of the AFM files are prominently noted in the
+modified file(s); and that this paragraph is not modified.
+Adobe Systems has no responsibility or obligation to
+support the use of the AFM files.
diff --git a/program/libraries/afm/Courier-Bold.afm b/program/libraries/afm/Courier-Bold.afm
--- /dev/null
@@ -0,0 +1,342 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
+Comment Creation Date: Mon Jun 23 16:28:00 1997
+Comment UniqueID 43048
+Comment VMusage 41139 52164
+FontName Courier-Bold
+FullName Courier Bold
+FamilyName Courier
+Weight Bold
+ItalicAngle 0
+IsFixedPitch true
+CharacterSet ExtendedRoman
+FontBBox -113 -250 749 801
+UnderlinePosition -100
+UnderlineThickness 50
+Version 003.000
+Notice Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
+EncodingScheme AdobeStandardEncoding
+CapHeight 562
+XHeight 439
+Ascender 629
+Descender -157
+StdHW 84
+StdVW 106
+StartCharMetrics 315
+C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 600 ; N exclam ; B 202 -15 398 572 ;
+C 34 ; WX 600 ; N quotedbl ; B 135 277 465 562 ;
+C 35 ; WX 600 ; N numbersign ; B 56 -45 544 651 ;
+C 36 ; WX 600 ; N dollar ; B 82 -126 519 666 ;
+C 37 ; WX 600 ; N percent ; B 5 -15 595 616 ;
+C 38 ; WX 600 ; N ampersand ; B 36 -15 546 543 ;
+C 39 ; WX 600 ; N quoteright ; B 171 277 423 562 ;
+C 40 ; WX 600 ; N parenleft ; B 219 -102 461 616 ;
+C 41 ; WX 600 ; N parenright ; B 139 -102 381 616 ;
+C 42 ; WX 600 ; N asterisk ; B 91 219 509 601 ;
+C 43 ; WX 600 ; N plus ; B 71 39 529 478 ;
+C 44 ; WX 600 ; N comma ; B 123 -111 393 174 ;
+C 45 ; WX 600 ; N hyphen ; B 100 203 500 313 ;
+C 46 ; WX 600 ; N period ; B 192 -15 408 171 ;
+C 47 ; WX 600 ; N slash ; B 98 -77 502 626 ;
+C 48 ; WX 600 ; N zero ; B 87 -15 513 616 ;
+C 49 ; WX 600 ; N one ; B 81 0 539 616 ;
+C 50 ; WX 600 ; N two ; B 61 0 499 616 ;
+C 51 ; WX 600 ; N three ; B 63 -15 501 616 ;
+C 52 ; WX 600 ; N four ; B 53 0 507 616 ;
+C 53 ; WX 600 ; N five ; B 70 -15 521 601 ;
+C 54 ; WX 600 ; N six ; B 90 -15 521 616 ;
+C 55 ; WX 600 ; N seven ; B 55 0 494 601 ;
+C 56 ; WX 600 ; N eight ; B 83 -15 517 616 ;
+C 57 ; WX 600 ; N nine ; B 79 -15 510 616 ;
+C 58 ; WX 600 ; N colon ; B 191 -15 407 425 ;
+C 59 ; WX 600 ; N semicolon ; B 123 -111 408 425 ;
+C 60 ; WX 600 ; N less ; B 66 15 523 501 ;
+C 61 ; WX 600 ; N equal ; B 71 118 529 398 ;
+C 62 ; WX 600 ; N greater ; B 77 15 534 501 ;
+C 63 ; WX 600 ; N question ; B 98 -14 501 580 ;
+C 64 ; WX 600 ; N at ; B 16 -15 584 616 ;
+C 65 ; WX 600 ; N A ; B -9 0 609 562 ;
+C 66 ; WX 600 ; N B ; B 30 0 573 562 ;
+C 67 ; WX 600 ; N C ; B 22 -18 560 580 ;
+C 68 ; WX 600 ; N D ; B 30 0 594 562 ;
+C 69 ; WX 600 ; N E ; B 25 0 560 562 ;
+C 70 ; WX 600 ; N F ; B 39 0 570 562 ;
+C 71 ; WX 600 ; N G ; B 22 -18 594 580 ;
+C 72 ; WX 600 ; N H ; B 20 0 580 562 ;
+C 73 ; WX 600 ; N I ; B 77 0 523 562 ;
+C 74 ; WX 600 ; N J ; B 37 -18 601 562 ;
+C 75 ; WX 600 ; N K ; B 21 0 599 562 ;
+C 76 ; WX 600 ; N L ; B 39 0 578 562 ;
+C 77 ; WX 600 ; N M ; B -2 0 602 562 ;
+C 78 ; WX 600 ; N N ; B 8 -12 610 562 ;
+C 79 ; WX 600 ; N O ; B 22 -18 578 580 ;
+C 80 ; WX 600 ; N P ; B 48 0 559 562 ;
+C 81 ; WX 600 ; N Q ; B 32 -138 578 580 ;
+C 82 ; WX 600 ; N R ; B 24 0 599 562 ;
+C 83 ; WX 600 ; N S ; B 47 -22 553 582 ;
+C 84 ; WX 600 ; N T ; B 21 0 579 562 ;
+C 85 ; WX 600 ; N U ; B 4 -18 596 562 ;
+C 86 ; WX 600 ; N V ; B -13 0 613 562 ;
+C 87 ; WX 600 ; N W ; B -18 0 618 562 ;
+C 88 ; WX 600 ; N X ; B 12 0 588 562 ;
+C 89 ; WX 600 ; N Y ; B 12 0 589 562 ;
+C 90 ; WX 600 ; N Z ; B 62 0 539 562 ;
+C 91 ; WX 600 ; N bracketleft ; B 245 -102 475 616 ;
+C 92 ; WX 600 ; N backslash ; B 99 -77 503 626 ;
+C 93 ; WX 600 ; N bracketright ; B 125 -102 355 616 ;
+C 94 ; WX 600 ; N asciicircum ; B 108 250 492 616 ;
+C 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ;
+C 96 ; WX 600 ; N quoteleft ; B 178 277 428 562 ;
+C 97 ; WX 600 ; N a ; B 35 -15 570 454 ;
+C 98 ; WX 600 ; N b ; B 0 -15 584 626 ;
+C 99 ; WX 600 ; N c ; B 40 -15 545 459 ;
+C 100 ; WX 600 ; N d ; B 20 -15 591 626 ;
+C 101 ; WX 600 ; N e ; B 40 -15 563 454 ;
+C 102 ; WX 600 ; N f ; B 83 0 547 626 ; L i fi ; L l fl ;
+C 103 ; WX 600 ; N g ; B 30 -146 580 454 ;
+C 104 ; WX 600 ; N h ; B 5 0 592 626 ;
+C 105 ; WX 600 ; N i ; B 77 0 523 658 ;
+C 106 ; WX 600 ; N j ; B 63 -146 440 658 ;
+C 107 ; WX 600 ; N k ; B 20 0 585 626 ;
+C 108 ; WX 600 ; N l ; B 77 0 523 626 ;
+C 109 ; WX 600 ; N m ; B -22 0 626 454 ;
+C 110 ; WX 600 ; N n ; B 18 0 592 454 ;
+C 111 ; WX 600 ; N o ; B 30 -15 570 454 ;
+C 112 ; WX 600 ; N p ; B -1 -142 570 454 ;
+C 113 ; WX 600 ; N q ; B 20 -142 591 454 ;
+C 114 ; WX 600 ; N r ; B 47 0 580 454 ;
+C 115 ; WX 600 ; N s ; B 68 -17 535 459 ;
+C 116 ; WX 600 ; N t ; B 47 -15 532 562 ;
+C 117 ; WX 600 ; N u ; B -1 -15 569 439 ;
+C 118 ; WX 600 ; N v ; B -1 0 601 439 ;
+C 119 ; WX 600 ; N w ; B -18 0 618 439 ;
+C 120 ; WX 600 ; N x ; B 6 0 594 439 ;
+C 121 ; WX 600 ; N y ; B -4 -142 601 439 ;
+C 122 ; WX 600 ; N z ; B 81 0 520 439 ;
+C 123 ; WX 600 ; N braceleft ; B 160 -102 464 616 ;
+C 124 ; WX 600 ; N bar ; B 255 -250 345 750 ;
+C 125 ; WX 600 ; N braceright ; B 136 -102 440 616 ;
+C 126 ; WX 600 ; N asciitilde ; B 71 153 530 356 ;
+C 161 ; WX 600 ; N exclamdown ; B 202 -146 398 449 ;
+C 162 ; WX 600 ; N cent ; B 66 -49 518 614 ;
+C 163 ; WX 600 ; N sterling ; B 72 -28 558 611 ;
+C 164 ; WX 600 ; N fraction ; B 25 -60 576 661 ;
+C 165 ; WX 600 ; N yen ; B 10 0 590 562 ;
+C 166 ; WX 600 ; N florin ; B -30 -131 572 616 ;
+C 167 ; WX 600 ; N section ; B 83 -70 517 580 ;
+C 168 ; WX 600 ; N currency ; B 54 49 546 517 ;
+C 169 ; WX 600 ; N quotesingle ; B 227 277 373 562 ;
+C 170 ; WX 600 ; N quotedblleft ; B 71 277 535 562 ;
+C 171 ; WX 600 ; N guillemotleft ; B 8 70 553 446 ;
+C 172 ; WX 600 ; N guilsinglleft ; B 141 70 459 446 ;
+C 173 ; WX 600 ; N guilsinglright ; B 141 70 459 446 ;
+C 174 ; WX 600 ; N fi ; B 12 0 593 626 ;
+C 175 ; WX 600 ; N fl ; B 12 0 593 626 ;
+C 177 ; WX 600 ; N endash ; B 65 203 535 313 ;
+C 178 ; WX 600 ; N dagger ; B 106 -70 494 580 ;
+C 179 ; WX 600 ; N daggerdbl ; B 106 -70 494 580 ;
+C 180 ; WX 600 ; N periodcentered ; B 196 165 404 351 ;
+C 182 ; WX 600 ; N paragraph ; B 6 -70 576 580 ;
+C 183 ; WX 600 ; N bullet ; B 140 132 460 430 ;
+C 184 ; WX 600 ; N quotesinglbase ; B 175 -142 427 143 ;
+C 185 ; WX 600 ; N quotedblbase ; B 65 -142 529 143 ;
+C 186 ; WX 600 ; N quotedblright ; B 61 277 525 562 ;
+C 187 ; WX 600 ; N guillemotright ; B 47 70 592 446 ;
+C 188 ; WX 600 ; N ellipsis ; B 26 -15 574 116 ;
+C 189 ; WX 600 ; N perthousand ; B -113 -15 713 616 ;
+C 191 ; WX 600 ; N questiondown ; B 99 -146 502 449 ;
+C 193 ; WX 600 ; N grave ; B 132 508 395 661 ;
+C 194 ; WX 600 ; N acute ; B 205 508 468 661 ;
+C 195 ; WX 600 ; N circumflex ; B 103 483 497 657 ;
+C 196 ; WX 600 ; N tilde ; B 89 493 512 636 ;
+C 197 ; WX 600 ; N macron ; B 88 505 512 585 ;
+C 198 ; WX 600 ; N breve ; B 83 468 517 631 ;
+C 199 ; WX 600 ; N dotaccent ; B 230 498 370 638 ;
+C 200 ; WX 600 ; N dieresis ; B 128 498 472 638 ;
+C 202 ; WX 600 ; N ring ; B 198 481 402 678 ;
+C 203 ; WX 600 ; N cedilla ; B 205 -206 387 0 ;
+C 205 ; WX 600 ; N hungarumlaut ; B 68 488 588 661 ;
+C 206 ; WX 600 ; N ogonek ; B 169 -199 400 0 ;
+C 207 ; WX 600 ; N caron ; B 103 493 497 667 ;
+C 208 ; WX 600 ; N emdash ; B -10 203 610 313 ;
+C 225 ; WX 600 ; N AE ; B -29 0 602 562 ;
+C 227 ; WX 600 ; N ordfeminine ; B 147 196 453 580 ;
+C 232 ; WX 600 ; N Lslash ; B 39 0 578 562 ;
+C 233 ; WX 600 ; N Oslash ; B 22 -22 578 584 ;
+C 234 ; WX 600 ; N OE ; B -25 0 595 562 ;
+C 235 ; WX 600 ; N ordmasculine ; B 147 196 453 580 ;
+C 241 ; WX 600 ; N ae ; B -4 -15 601 454 ;
+C 245 ; WX 600 ; N dotlessi ; B 77 0 523 439 ;
+C 248 ; WX 600 ; N lslash ; B 77 0 523 626 ;
+C 249 ; WX 600 ; N oslash ; B 30 -24 570 463 ;
+C 250 ; WX 600 ; N oe ; B -18 -15 611 454 ;
+C 251 ; WX 600 ; N germandbls ; B 22 -15 596 626 ;
+C -1 ; WX 600 ; N Idieresis ; B 77 0 523 761 ;
+C -1 ; WX 600 ; N eacute ; B 40 -15 563 661 ;
+C -1 ; WX 600 ; N abreve ; B 35 -15 570 661 ;
+C -1 ; WX 600 ; N uhungarumlaut ; B -1 -15 628 661 ;
+C -1 ; WX 600 ; N ecaron ; B 40 -15 563 667 ;
+C -1 ; WX 600 ; N Ydieresis ; B 12 0 589 761 ;
+C -1 ; WX 600 ; N divide ; B 71 16 529 500 ;
+C -1 ; WX 600 ; N Yacute ; B 12 0 589 784 ;
+C -1 ; WX 600 ; N Acircumflex ; B -9 0 609 780 ;
+C -1 ; WX 600 ; N aacute ; B 35 -15 570 661 ;
+C -1 ; WX 600 ; N Ucircumflex ; B 4 -18 596 780 ;
+C -1 ; WX 600 ; N yacute ; B -4 -142 601 661 ;
+C -1 ; WX 600 ; N scommaaccent ; B 68 -250 535 459 ;
+C -1 ; WX 600 ; N ecircumflex ; B 40 -15 563 657 ;
+C -1 ; WX 600 ; N Uring ; B 4 -18 596 801 ;
+C -1 ; WX 600 ; N Udieresis ; B 4 -18 596 761 ;
+C -1 ; WX 600 ; N aogonek ; B 35 -199 586 454 ;
+C -1 ; WX 600 ; N Uacute ; B 4 -18 596 784 ;
+C -1 ; WX 600 ; N uogonek ; B -1 -199 585 439 ;
+C -1 ; WX 600 ; N Edieresis ; B 25 0 560 761 ;
+C -1 ; WX 600 ; N Dcroat ; B 30 0 594 562 ;
+C -1 ; WX 600 ; N commaaccent ; B 205 -250 397 -57 ;
+C -1 ; WX 600 ; N copyright ; B 0 -18 600 580 ;
+C -1 ; WX 600 ; N Emacron ; B 25 0 560 708 ;
+C -1 ; WX 600 ; N ccaron ; B 40 -15 545 667 ;
+C -1 ; WX 600 ; N aring ; B 35 -15 570 678 ;
+C -1 ; WX 600 ; N Ncommaaccent ; B 8 -250 610 562 ;
+C -1 ; WX 600 ; N lacute ; B 77 0 523 801 ;
+C -1 ; WX 600 ; N agrave ; B 35 -15 570 661 ;
+C -1 ; WX 600 ; N Tcommaaccent ; B 21 -250 579 562 ;
+C -1 ; WX 600 ; N Cacute ; B 22 -18 560 784 ;
+C -1 ; WX 600 ; N atilde ; B 35 -15 570 636 ;
+C -1 ; WX 600 ; N Edotaccent ; B 25 0 560 761 ;
+C -1 ; WX 600 ; N scaron ; B 68 -17 535 667 ;
+C -1 ; WX 600 ; N scedilla ; B 68 -206 535 459 ;
+C -1 ; WX 600 ; N iacute ; B 77 0 523 661 ;
+C -1 ; WX 600 ; N lozenge ; B 66 0 534 740 ;
+C -1 ; WX 600 ; N Rcaron ; B 24 0 599 790 ;
+C -1 ; WX 600 ; N Gcommaaccent ; B 22 -250 594 580 ;
+C -1 ; WX 600 ; N ucircumflex ; B -1 -15 569 657 ;
+C -1 ; WX 600 ; N acircumflex ; B 35 -15 570 657 ;
+C -1 ; WX 600 ; N Amacron ; B -9 0 609 708 ;
+C -1 ; WX 600 ; N rcaron ; B 47 0 580 667 ;
+C -1 ; WX 600 ; N ccedilla ; B 40 -206 545 459 ;
+C -1 ; WX 600 ; N Zdotaccent ; B 62 0 539 761 ;
+C -1 ; WX 600 ; N Thorn ; B 48 0 557 562 ;
+C -1 ; WX 600 ; N Omacron ; B 22 -18 578 708 ;
+C -1 ; WX 600 ; N Racute ; B 24 0 599 784 ;
+C -1 ; WX 600 ; N Sacute ; B 47 -22 553 784 ;
+C -1 ; WX 600 ; N dcaron ; B 20 -15 727 626 ;
+C -1 ; WX 600 ; N Umacron ; B 4 -18 596 708 ;
+C -1 ; WX 600 ; N uring ; B -1 -15 569 678 ;
+C -1 ; WX 600 ; N threesuperior ; B 138 222 433 616 ;
+C -1 ; WX 600 ; N Ograve ; B 22 -18 578 784 ;
+C -1 ; WX 600 ; N Agrave ; B -9 0 609 784 ;
+C -1 ; WX 600 ; N Abreve ; B -9 0 609 784 ;
+C -1 ; WX 600 ; N multiply ; B 81 39 520 478 ;
+C -1 ; WX 600 ; N uacute ; B -1 -15 569 661 ;
+C -1 ; WX 600 ; N Tcaron ; B 21 0 579 790 ;
+C -1 ; WX 600 ; N partialdiff ; B 63 -38 537 728 ;
+C -1 ; WX 600 ; N ydieresis ; B -4 -142 601 638 ;
+C -1 ; WX 600 ; N Nacute ; B 8 -12 610 784 ;
+C -1 ; WX 600 ; N icircumflex ; B 73 0 523 657 ;
+C -1 ; WX 600 ; N Ecircumflex ; B 25 0 560 780 ;
+C -1 ; WX 600 ; N adieresis ; B 35 -15 570 638 ;
+C -1 ; WX 600 ; N edieresis ; B 40 -15 563 638 ;
+C -1 ; WX 600 ; N cacute ; B 40 -15 545 661 ;
+C -1 ; WX 600 ; N nacute ; B 18 0 592 661 ;
+C -1 ; WX 600 ; N umacron ; B -1 -15 569 585 ;
+C -1 ; WX 600 ; N Ncaron ; B 8 -12 610 790 ;
+C -1 ; WX 600 ; N Iacute ; B 77 0 523 784 ;
+C -1 ; WX 600 ; N plusminus ; B 71 24 529 515 ;
+C -1 ; WX 600 ; N brokenbar ; B 255 -175 345 675 ;
+C -1 ; WX 600 ; N registered ; B 0 -18 600 580 ;
+C -1 ; WX 600 ; N Gbreve ; B 22 -18 594 784 ;
+C -1 ; WX 600 ; N Idotaccent ; B 77 0 523 761 ;
+C -1 ; WX 600 ; N summation ; B 15 -10 586 706 ;
+C -1 ; WX 600 ; N Egrave ; B 25 0 560 784 ;
+C -1 ; WX 600 ; N racute ; B 47 0 580 661 ;
+C -1 ; WX 600 ; N omacron ; B 30 -15 570 585 ;
+C -1 ; WX 600 ; N Zacute ; B 62 0 539 784 ;
+C -1 ; WX 600 ; N Zcaron ; B 62 0 539 790 ;
+C -1 ; WX 600 ; N greaterequal ; B 26 0 523 696 ;
+C -1 ; WX 600 ; N Eth ; B 30 0 594 562 ;
+C -1 ; WX 600 ; N Ccedilla ; B 22 -206 560 580 ;
+C -1 ; WX 600 ; N lcommaaccent ; B 77 -250 523 626 ;
+C -1 ; WX 600 ; N tcaron ; B 47 -15 532 703 ;
+C -1 ; WX 600 ; N eogonek ; B 40 -199 563 454 ;
+C -1 ; WX 600 ; N Uogonek ; B 4 -199 596 562 ;
+C -1 ; WX 600 ; N Aacute ; B -9 0 609 784 ;
+C -1 ; WX 600 ; N Adieresis ; B -9 0 609 761 ;
+C -1 ; WX 600 ; N egrave ; B 40 -15 563 661 ;
+C -1 ; WX 600 ; N zacute ; B 81 0 520 661 ;
+C -1 ; WX 600 ; N iogonek ; B 77 -199 523 658 ;
+C -1 ; WX 600 ; N Oacute ; B 22 -18 578 784 ;
+C -1 ; WX 600 ; N oacute ; B 30 -15 570 661 ;
+C -1 ; WX 600 ; N amacron ; B 35 -15 570 585 ;
+C -1 ; WX 600 ; N sacute ; B 68 -17 535 661 ;
+C -1 ; WX 600 ; N idieresis ; B 77 0 523 618 ;
+C -1 ; WX 600 ; N Ocircumflex ; B 22 -18 578 780 ;
+C -1 ; WX 600 ; N Ugrave ; B 4 -18 596 784 ;
+C -1 ; WX 600 ; N Delta ; B 6 0 594 688 ;
+C -1 ; WX 600 ; N thorn ; B -14 -142 570 626 ;
+C -1 ; WX 600 ; N twosuperior ; B 143 230 436 616 ;
+C -1 ; WX 600 ; N Odieresis ; B 22 -18 578 761 ;
+C -1 ; WX 600 ; N mu ; B -1 -142 569 439 ;
+C -1 ; WX 600 ; N igrave ; B 77 0 523 661 ;
+C -1 ; WX 600 ; N ohungarumlaut ; B 30 -15 668 661 ;
+C -1 ; WX 600 ; N Eogonek ; B 25 -199 576 562 ;
+C -1 ; WX 600 ; N dcroat ; B 20 -15 591 626 ;
+C -1 ; WX 600 ; N threequarters ; B -47 -60 648 661 ;
+C -1 ; WX 600 ; N Scedilla ; B 47 -206 553 582 ;
+C -1 ; WX 600 ; N lcaron ; B 77 0 597 626 ;
+C -1 ; WX 600 ; N Kcommaaccent ; B 21 -250 599 562 ;
+C -1 ; WX 600 ; N Lacute ; B 39 0 578 784 ;
+C -1 ; WX 600 ; N trademark ; B -9 230 749 562 ;
+C -1 ; WX 600 ; N edotaccent ; B 40 -15 563 638 ;
+C -1 ; WX 600 ; N Igrave ; B 77 0 523 784 ;
+C -1 ; WX 600 ; N Imacron ; B 77 0 523 708 ;
+C -1 ; WX 600 ; N Lcaron ; B 39 0 637 562 ;
+C -1 ; WX 600 ; N onehalf ; B -47 -60 648 661 ;
+C -1 ; WX 600 ; N lessequal ; B 26 0 523 696 ;
+C -1 ; WX 600 ; N ocircumflex ; B 30 -15 570 657 ;
+C -1 ; WX 600 ; N ntilde ; B 18 0 592 636 ;
+C -1 ; WX 600 ; N Uhungarumlaut ; B 4 -18 638 784 ;
+C -1 ; WX 600 ; N Eacute ; B 25 0 560 784 ;
+C -1 ; WX 600 ; N emacron ; B 40 -15 563 585 ;
+C -1 ; WX 600 ; N gbreve ; B 30 -146 580 661 ;
+C -1 ; WX 600 ; N onequarter ; B -56 -60 656 661 ;
+C -1 ; WX 600 ; N Scaron ; B 47 -22 553 790 ;
+C -1 ; WX 600 ; N Scommaaccent ; B 47 -250 553 582 ;
+C -1 ; WX 600 ; N Ohungarumlaut ; B 22 -18 628 784 ;
+C -1 ; WX 600 ; N degree ; B 86 243 474 616 ;
+C -1 ; WX 600 ; N ograve ; B 30 -15 570 661 ;
+C -1 ; WX 600 ; N Ccaron ; B 22 -18 560 790 ;
+C -1 ; WX 600 ; N ugrave ; B -1 -15 569 661 ;
+C -1 ; WX 600 ; N radical ; B -19 -104 473 778 ;
+C -1 ; WX 600 ; N Dcaron ; B 30 0 594 790 ;
+C -1 ; WX 600 ; N rcommaaccent ; B 47 -250 580 454 ;
+C -1 ; WX 600 ; N Ntilde ; B 8 -12 610 759 ;
+C -1 ; WX 600 ; N otilde ; B 30 -15 570 636 ;
+C -1 ; WX 600 ; N Rcommaaccent ; B 24 -250 599 562 ;
+C -1 ; WX 600 ; N Lcommaaccent ; B 39 -250 578 562 ;
+C -1 ; WX 600 ; N Atilde ; B -9 0 609 759 ;
+C -1 ; WX 600 ; N Aogonek ; B -9 -199 625 562 ;
+C -1 ; WX 600 ; N Aring ; B -9 0 609 801 ;
+C -1 ; WX 600 ; N Otilde ; B 22 -18 578 759 ;
+C -1 ; WX 600 ; N zdotaccent ; B 81 0 520 638 ;
+C -1 ; WX 600 ; N Ecaron ; B 25 0 560 790 ;
+C -1 ; WX 600 ; N Iogonek ; B 77 -199 523 562 ;
+C -1 ; WX 600 ; N kcommaaccent ; B 20 -250 585 626 ;
+C -1 ; WX 600 ; N minus ; B 71 203 529 313 ;
+C -1 ; WX 600 ; N Icircumflex ; B 77 0 523 780 ;
+C -1 ; WX 600 ; N ncaron ; B 18 0 592 667 ;
+C -1 ; WX 600 ; N tcommaaccent ; B 47 -250 532 562 ;
+C -1 ; WX 600 ; N logicalnot ; B 71 103 529 413 ;
+C -1 ; WX 600 ; N odieresis ; B 30 -15 570 638 ;
+C -1 ; WX 600 ; N udieresis ; B -1 -15 569 638 ;
+C -1 ; WX 600 ; N notequal ; B 12 -47 537 563 ;
+C -1 ; WX 600 ; N gcommaaccent ; B 30 -146 580 714 ;
+C -1 ; WX 600 ; N eth ; B 58 -27 543 626 ;
+C -1 ; WX 600 ; N zcaron ; B 81 0 520 667 ;
+C -1 ; WX 600 ; N ncommaaccent ; B 18 -250 592 454 ;
+C -1 ; WX 600 ; N onesuperior ; B 153 230 447 616 ;
+C -1 ; WX 600 ; N imacron ; B 77 0 523 585 ;
+C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+EndFontMetrics
diff --git a/program/libraries/afm/Courier-BoldOblique.afm b/program/libraries/afm/Courier-BoldOblique.afm
--- /dev/null
@@ -0,0 +1,342 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
+Comment Creation Date: Mon Jun 23 16:28:46 1997
+Comment UniqueID 43049
+Comment VMusage 17529 79244
+FontName Courier-BoldOblique
+FullName Courier Bold Oblique
+FamilyName Courier
+Weight Bold
+ItalicAngle -12
+IsFixedPitch true
+CharacterSet ExtendedRoman
+FontBBox -57 -250 869 801
+UnderlinePosition -100
+UnderlineThickness 50
+Version 003.000
+Notice Copyright (c) 1989, 1990, 1991, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
+EncodingScheme AdobeStandardEncoding
+CapHeight 562
+XHeight 439
+Ascender 629
+Descender -157
+StdHW 84
+StdVW 106
+StartCharMetrics 315
+C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 600 ; N exclam ; B 215 -15 495 572 ;
+C 34 ; WX 600 ; N quotedbl ; B 211 277 585 562 ;
+C 35 ; WX 600 ; N numbersign ; B 88 -45 641 651 ;
+C 36 ; WX 600 ; N dollar ; B 87 -126 630 666 ;
+C 37 ; WX 600 ; N percent ; B 101 -15 625 616 ;
+C 38 ; WX 600 ; N ampersand ; B 61 -15 595 543 ;
+C 39 ; WX 600 ; N quoteright ; B 229 277 543 562 ;
+C 40 ; WX 600 ; N parenleft ; B 265 -102 592 616 ;
+C 41 ; WX 600 ; N parenright ; B 117 -102 444 616 ;
+C 42 ; WX 600 ; N asterisk ; B 179 219 598 601 ;
+C 43 ; WX 600 ; N plus ; B 114 39 596 478 ;
+C 44 ; WX 600 ; N comma ; B 99 -111 430 174 ;
+C 45 ; WX 600 ; N hyphen ; B 143 203 567 313 ;
+C 46 ; WX 600 ; N period ; B 206 -15 427 171 ;
+C 47 ; WX 600 ; N slash ; B 90 -77 626 626 ;
+C 48 ; WX 600 ; N zero ; B 135 -15 593 616 ;
+C 49 ; WX 600 ; N one ; B 93 0 562 616 ;
+C 50 ; WX 600 ; N two ; B 61 0 594 616 ;
+C 51 ; WX 600 ; N three ; B 71 -15 571 616 ;
+C 52 ; WX 600 ; N four ; B 81 0 559 616 ;
+C 53 ; WX 600 ; N five ; B 77 -15 621 601 ;
+C 54 ; WX 600 ; N six ; B 135 -15 652 616 ;
+C 55 ; WX 600 ; N seven ; B 147 0 622 601 ;
+C 56 ; WX 600 ; N eight ; B 115 -15 604 616 ;
+C 57 ; WX 600 ; N nine ; B 75 -15 592 616 ;
+C 58 ; WX 600 ; N colon ; B 205 -15 480 425 ;
+C 59 ; WX 600 ; N semicolon ; B 99 -111 481 425 ;
+C 60 ; WX 600 ; N less ; B 120 15 613 501 ;
+C 61 ; WX 600 ; N equal ; B 96 118 614 398 ;
+C 62 ; WX 600 ; N greater ; B 97 15 589 501 ;
+C 63 ; WX 600 ; N question ; B 183 -14 592 580 ;
+C 64 ; WX 600 ; N at ; B 65 -15 642 616 ;
+C 65 ; WX 600 ; N A ; B -9 0 632 562 ;
+C 66 ; WX 600 ; N B ; B 30 0 630 562 ;
+C 67 ; WX 600 ; N C ; B 74 -18 675 580 ;
+C 68 ; WX 600 ; N D ; B 30 0 664 562 ;
+C 69 ; WX 600 ; N E ; B 25 0 670 562 ;
+C 70 ; WX 600 ; N F ; B 39 0 684 562 ;
+C 71 ; WX 600 ; N G ; B 74 -18 675 580 ;
+C 72 ; WX 600 ; N H ; B 20 0 700 562 ;
+C 73 ; WX 600 ; N I ; B 77 0 643 562 ;
+C 74 ; WX 600 ; N J ; B 58 -18 721 562 ;
+C 75 ; WX 600 ; N K ; B 21 0 692 562 ;
+C 76 ; WX 600 ; N L ; B 39 0 636 562 ;
+C 77 ; WX 600 ; N M ; B -2 0 722 562 ;
+C 78 ; WX 600 ; N N ; B 8 -12 730 562 ;
+C 79 ; WX 600 ; N O ; B 74 -18 645 580 ;
+C 80 ; WX 600 ; N P ; B 48 0 643 562 ;
+C 81 ; WX 600 ; N Q ; B 83 -138 636 580 ;
+C 82 ; WX 600 ; N R ; B 24 0 617 562 ;
+C 83 ; WX 600 ; N S ; B 54 -22 673 582 ;
+C 84 ; WX 600 ; N T ; B 86 0 679 562 ;
+C 85 ; WX 600 ; N U ; B 101 -18 716 562 ;
+C 86 ; WX 600 ; N V ; B 84 0 733 562 ;
+C 87 ; WX 600 ; N W ; B 79 0 738 562 ;
+C 88 ; WX 600 ; N X ; B 12 0 690 562 ;
+C 89 ; WX 600 ; N Y ; B 109 0 709 562 ;
+C 90 ; WX 600 ; N Z ; B 62 0 637 562 ;
+C 91 ; WX 600 ; N bracketleft ; B 223 -102 606 616 ;
+C 92 ; WX 600 ; N backslash ; B 222 -77 496 626 ;
+C 93 ; WX 600 ; N bracketright ; B 103 -102 486 616 ;
+C 94 ; WX 600 ; N asciicircum ; B 171 250 556 616 ;
+C 95 ; WX 600 ; N underscore ; B -27 -125 585 -75 ;
+C 96 ; WX 600 ; N quoteleft ; B 297 277 487 562 ;
+C 97 ; WX 600 ; N a ; B 61 -15 593 454 ;
+C 98 ; WX 600 ; N b ; B 13 -15 636 626 ;
+C 99 ; WX 600 ; N c ; B 81 -15 631 459 ;
+C 100 ; WX 600 ; N d ; B 60 -15 645 626 ;
+C 101 ; WX 600 ; N e ; B 81 -15 605 454 ;
+C 102 ; WX 600 ; N f ; B 83 0 677 626 ; L i fi ; L l fl ;
+C 103 ; WX 600 ; N g ; B 40 -146 674 454 ;
+C 104 ; WX 600 ; N h ; B 18 0 615 626 ;
+C 105 ; WX 600 ; N i ; B 77 0 546 658 ;
+C 106 ; WX 600 ; N j ; B 36 -146 580 658 ;
+C 107 ; WX 600 ; N k ; B 33 0 643 626 ;
+C 108 ; WX 600 ; N l ; B 77 0 546 626 ;
+C 109 ; WX 600 ; N m ; B -22 0 649 454 ;
+C 110 ; WX 600 ; N n ; B 18 0 615 454 ;
+C 111 ; WX 600 ; N o ; B 71 -15 622 454 ;
+C 112 ; WX 600 ; N p ; B -32 -142 622 454 ;
+C 113 ; WX 600 ; N q ; B 60 -142 685 454 ;
+C 114 ; WX 600 ; N r ; B 47 0 655 454 ;
+C 115 ; WX 600 ; N s ; B 66 -17 608 459 ;
+C 116 ; WX 600 ; N t ; B 118 -15 567 562 ;
+C 117 ; WX 600 ; N u ; B 70 -15 592 439 ;
+C 118 ; WX 600 ; N v ; B 70 0 695 439 ;
+C 119 ; WX 600 ; N w ; B 53 0 712 439 ;
+C 120 ; WX 600 ; N x ; B 6 0 671 439 ;
+C 121 ; WX 600 ; N y ; B -21 -142 695 439 ;
+C 122 ; WX 600 ; N z ; B 81 0 614 439 ;
+C 123 ; WX 600 ; N braceleft ; B 203 -102 595 616 ;
+C 124 ; WX 600 ; N bar ; B 201 -250 505 750 ;
+C 125 ; WX 600 ; N braceright ; B 114 -102 506 616 ;
+C 126 ; WX 600 ; N asciitilde ; B 120 153 590 356 ;
+C 161 ; WX 600 ; N exclamdown ; B 196 -146 477 449 ;
+C 162 ; WX 600 ; N cent ; B 121 -49 605 614 ;
+C 163 ; WX 600 ; N sterling ; B 106 -28 650 611 ;
+C 164 ; WX 600 ; N fraction ; B 22 -60 708 661 ;
+C 165 ; WX 600 ; N yen ; B 98 0 710 562 ;
+C 166 ; WX 600 ; N florin ; B -57 -131 702 616 ;
+C 167 ; WX 600 ; N section ; B 74 -70 620 580 ;
+C 168 ; WX 600 ; N currency ; B 77 49 644 517 ;
+C 169 ; WX 600 ; N quotesingle ; B 303 277 493 562 ;
+C 170 ; WX 600 ; N quotedblleft ; B 190 277 594 562 ;
+C 171 ; WX 600 ; N guillemotleft ; B 62 70 639 446 ;
+C 172 ; WX 600 ; N guilsinglleft ; B 195 70 545 446 ;
+C 173 ; WX 600 ; N guilsinglright ; B 165 70 514 446 ;
+C 174 ; WX 600 ; N fi ; B 12 0 644 626 ;
+C 175 ; WX 600 ; N fl ; B 12 0 644 626 ;
+C 177 ; WX 600 ; N endash ; B 108 203 602 313 ;
+C 178 ; WX 600 ; N dagger ; B 175 -70 586 580 ;
+C 179 ; WX 600 ; N daggerdbl ; B 121 -70 587 580 ;
+C 180 ; WX 600 ; N periodcentered ; B 248 165 461 351 ;
+C 182 ; WX 600 ; N paragraph ; B 61 -70 700 580 ;
+C 183 ; WX 600 ; N bullet ; B 196 132 523 430 ;
+C 184 ; WX 600 ; N quotesinglbase ; B 144 -142 458 143 ;
+C 185 ; WX 600 ; N quotedblbase ; B 34 -142 560 143 ;
+C 186 ; WX 600 ; N quotedblright ; B 119 277 645 562 ;
+C 187 ; WX 600 ; N guillemotright ; B 71 70 647 446 ;
+C 188 ; WX 600 ; N ellipsis ; B 35 -15 587 116 ;
+C 189 ; WX 600 ; N perthousand ; B -45 -15 743 616 ;
+C 191 ; WX 600 ; N questiondown ; B 100 -146 509 449 ;
+C 193 ; WX 600 ; N grave ; B 272 508 503 661 ;
+C 194 ; WX 600 ; N acute ; B 312 508 609 661 ;
+C 195 ; WX 600 ; N circumflex ; B 212 483 607 657 ;
+C 196 ; WX 600 ; N tilde ; B 199 493 643 636 ;
+C 197 ; WX 600 ; N macron ; B 195 505 637 585 ;
+C 198 ; WX 600 ; N breve ; B 217 468 652 631 ;
+C 199 ; WX 600 ; N dotaccent ; B 348 498 493 638 ;
+C 200 ; WX 600 ; N dieresis ; B 246 498 595 638 ;
+C 202 ; WX 600 ; N ring ; B 319 481 528 678 ;
+C 203 ; WX 600 ; N cedilla ; B 168 -206 368 0 ;
+C 205 ; WX 600 ; N hungarumlaut ; B 171 488 729 661 ;
+C 206 ; WX 600 ; N ogonek ; B 143 -199 367 0 ;
+C 207 ; WX 600 ; N caron ; B 238 493 633 667 ;
+C 208 ; WX 600 ; N emdash ; B 33 203 677 313 ;
+C 225 ; WX 600 ; N AE ; B -29 0 708 562 ;
+C 227 ; WX 600 ; N ordfeminine ; B 188 196 526 580 ;
+C 232 ; WX 600 ; N Lslash ; B 39 0 636 562 ;
+C 233 ; WX 600 ; N Oslash ; B 48 -22 673 584 ;
+C 234 ; WX 600 ; N OE ; B 26 0 701 562 ;
+C 235 ; WX 600 ; N ordmasculine ; B 188 196 543 580 ;
+C 241 ; WX 600 ; N ae ; B 21 -15 652 454 ;
+C 245 ; WX 600 ; N dotlessi ; B 77 0 546 439 ;
+C 248 ; WX 600 ; N lslash ; B 77 0 587 626 ;
+C 249 ; WX 600 ; N oslash ; B 54 -24 638 463 ;
+C 250 ; WX 600 ; N oe ; B 18 -15 662 454 ;
+C 251 ; WX 600 ; N germandbls ; B 22 -15 629 626 ;
+C -1 ; WX 600 ; N Idieresis ; B 77 0 643 761 ;
+C -1 ; WX 600 ; N eacute ; B 81 -15 609 661 ;
+C -1 ; WX 600 ; N abreve ; B 61 -15 658 661 ;
+C -1 ; WX 600 ; N uhungarumlaut ; B 70 -15 769 661 ;
+C -1 ; WX 600 ; N ecaron ; B 81 -15 633 667 ;
+C -1 ; WX 600 ; N Ydieresis ; B 109 0 709 761 ;
+C -1 ; WX 600 ; N divide ; B 114 16 596 500 ;
+C -1 ; WX 600 ; N Yacute ; B 109 0 709 784 ;
+C -1 ; WX 600 ; N Acircumflex ; B -9 0 632 780 ;
+C -1 ; WX 600 ; N aacute ; B 61 -15 609 661 ;
+C -1 ; WX 600 ; N Ucircumflex ; B 101 -18 716 780 ;
+C -1 ; WX 600 ; N yacute ; B -21 -142 695 661 ;
+C -1 ; WX 600 ; N scommaaccent ; B 66 -250 608 459 ;
+C -1 ; WX 600 ; N ecircumflex ; B 81 -15 607 657 ;
+C -1 ; WX 600 ; N Uring ; B 101 -18 716 801 ;
+C -1 ; WX 600 ; N Udieresis ; B 101 -18 716 761 ;
+C -1 ; WX 600 ; N aogonek ; B 61 -199 593 454 ;
+C -1 ; WX 600 ; N Uacute ; B 101 -18 716 784 ;
+C -1 ; WX 600 ; N uogonek ; B 70 -199 592 439 ;
+C -1 ; WX 600 ; N Edieresis ; B 25 0 670 761 ;
+C -1 ; WX 600 ; N Dcroat ; B 30 0 664 562 ;
+C -1 ; WX 600 ; N commaaccent ; B 151 -250 385 -57 ;
+C -1 ; WX 600 ; N copyright ; B 53 -18 667 580 ;
+C -1 ; WX 600 ; N Emacron ; B 25 0 670 708 ;
+C -1 ; WX 600 ; N ccaron ; B 81 -15 633 667 ;
+C -1 ; WX 600 ; N aring ; B 61 -15 593 678 ;
+C -1 ; WX 600 ; N Ncommaaccent ; B 8 -250 730 562 ;
+C -1 ; WX 600 ; N lacute ; B 77 0 639 801 ;
+C -1 ; WX 600 ; N agrave ; B 61 -15 593 661 ;
+C -1 ; WX 600 ; N Tcommaaccent ; B 86 -250 679 562 ;
+C -1 ; WX 600 ; N Cacute ; B 74 -18 675 784 ;
+C -1 ; WX 600 ; N atilde ; B 61 -15 643 636 ;
+C -1 ; WX 600 ; N Edotaccent ; B 25 0 670 761 ;
+C -1 ; WX 600 ; N scaron ; B 66 -17 633 667 ;
+C -1 ; WX 600 ; N scedilla ; B 66 -206 608 459 ;
+C -1 ; WX 600 ; N iacute ; B 77 0 609 661 ;
+C -1 ; WX 600 ; N lozenge ; B 145 0 614 740 ;
+C -1 ; WX 600 ; N Rcaron ; B 24 0 659 790 ;
+C -1 ; WX 600 ; N Gcommaaccent ; B 74 -250 675 580 ;
+C -1 ; WX 600 ; N ucircumflex ; B 70 -15 597 657 ;
+C -1 ; WX 600 ; N acircumflex ; B 61 -15 607 657 ;
+C -1 ; WX 600 ; N Amacron ; B -9 0 633 708 ;
+C -1 ; WX 600 ; N rcaron ; B 47 0 655 667 ;
+C -1 ; WX 600 ; N ccedilla ; B 81 -206 631 459 ;
+C -1 ; WX 600 ; N Zdotaccent ; B 62 0 637 761 ;
+C -1 ; WX 600 ; N Thorn ; B 48 0 620 562 ;
+C -1 ; WX 600 ; N Omacron ; B 74 -18 663 708 ;
+C -1 ; WX 600 ; N Racute ; B 24 0 665 784 ;
+C -1 ; WX 600 ; N Sacute ; B 54 -22 673 784 ;
+C -1 ; WX 600 ; N dcaron ; B 60 -15 861 626 ;
+C -1 ; WX 600 ; N Umacron ; B 101 -18 716 708 ;
+C -1 ; WX 600 ; N uring ; B 70 -15 592 678 ;
+C -1 ; WX 600 ; N threesuperior ; B 193 222 526 616 ;
+C -1 ; WX 600 ; N Ograve ; B 74 -18 645 784 ;
+C -1 ; WX 600 ; N Agrave ; B -9 0 632 784 ;
+C -1 ; WX 600 ; N Abreve ; B -9 0 684 784 ;
+C -1 ; WX 600 ; N multiply ; B 104 39 606 478 ;
+C -1 ; WX 600 ; N uacute ; B 70 -15 599 661 ;
+C -1 ; WX 600 ; N Tcaron ; B 86 0 679 790 ;
+C -1 ; WX 600 ; N partialdiff ; B 91 -38 627 728 ;
+C -1 ; WX 600 ; N ydieresis ; B -21 -142 695 638 ;
+C -1 ; WX 600 ; N Nacute ; B 8 -12 730 784 ;
+C -1 ; WX 600 ; N icircumflex ; B 77 0 577 657 ;
+C -1 ; WX 600 ; N Ecircumflex ; B 25 0 670 780 ;
+C -1 ; WX 600 ; N adieresis ; B 61 -15 595 638 ;
+C -1 ; WX 600 ; N edieresis ; B 81 -15 605 638 ;
+C -1 ; WX 600 ; N cacute ; B 81 -15 649 661 ;
+C -1 ; WX 600 ; N nacute ; B 18 0 639 661 ;
+C -1 ; WX 600 ; N umacron ; B 70 -15 637 585 ;
+C -1 ; WX 600 ; N Ncaron ; B 8 -12 730 790 ;
+C -1 ; WX 600 ; N Iacute ; B 77 0 643 784 ;
+C -1 ; WX 600 ; N plusminus ; B 76 24 614 515 ;
+C -1 ; WX 600 ; N brokenbar ; B 217 -175 489 675 ;
+C -1 ; WX 600 ; N registered ; B 53 -18 667 580 ;
+C -1 ; WX 600 ; N Gbreve ; B 74 -18 684 784 ;
+C -1 ; WX 600 ; N Idotaccent ; B 77 0 643 761 ;
+C -1 ; WX 600 ; N summation ; B 15 -10 672 706 ;
+C -1 ; WX 600 ; N Egrave ; B 25 0 670 784 ;
+C -1 ; WX 600 ; N racute ; B 47 0 655 661 ;
+C -1 ; WX 600 ; N omacron ; B 71 -15 637 585 ;
+C -1 ; WX 600 ; N Zacute ; B 62 0 665 784 ;
+C -1 ; WX 600 ; N Zcaron ; B 62 0 659 790 ;
+C -1 ; WX 600 ; N greaterequal ; B 26 0 627 696 ;
+C -1 ; WX 600 ; N Eth ; B 30 0 664 562 ;
+C -1 ; WX 600 ; N Ccedilla ; B 74 -206 675 580 ;
+C -1 ; WX 600 ; N lcommaaccent ; B 77 -250 546 626 ;
+C -1 ; WX 600 ; N tcaron ; B 118 -15 627 703 ;
+C -1 ; WX 600 ; N eogonek ; B 81 -199 605 454 ;
+C -1 ; WX 600 ; N Uogonek ; B 101 -199 716 562 ;
+C -1 ; WX 600 ; N Aacute ; B -9 0 655 784 ;
+C -1 ; WX 600 ; N Adieresis ; B -9 0 632 761 ;
+C -1 ; WX 600 ; N egrave ; B 81 -15 605 661 ;
+C -1 ; WX 600 ; N zacute ; B 81 0 614 661 ;
+C -1 ; WX 600 ; N iogonek ; B 77 -199 546 658 ;
+C -1 ; WX 600 ; N Oacute ; B 74 -18 645 784 ;
+C -1 ; WX 600 ; N oacute ; B 71 -15 649 661 ;
+C -1 ; WX 600 ; N amacron ; B 61 -15 637 585 ;
+C -1 ; WX 600 ; N sacute ; B 66 -17 609 661 ;
+C -1 ; WX 600 ; N idieresis ; B 77 0 561 618 ;
+C -1 ; WX 600 ; N Ocircumflex ; B 74 -18 645 780 ;
+C -1 ; WX 600 ; N Ugrave ; B 101 -18 716 784 ;
+C -1 ; WX 600 ; N Delta ; B 6 0 594 688 ;
+C -1 ; WX 600 ; N thorn ; B -32 -142 622 626 ;
+C -1 ; WX 600 ; N twosuperior ; B 191 230 542 616 ;
+C -1 ; WX 600 ; N Odieresis ; B 74 -18 645 761 ;
+C -1 ; WX 600 ; N mu ; B 49 -142 592 439 ;
+C -1 ; WX 600 ; N igrave ; B 77 0 546 661 ;
+C -1 ; WX 600 ; N ohungarumlaut ; B 71 -15 809 661 ;
+C -1 ; WX 600 ; N Eogonek ; B 25 -199 670 562 ;
+C -1 ; WX 600 ; N dcroat ; B 60 -15 712 626 ;
+C -1 ; WX 600 ; N threequarters ; B 8 -60 699 661 ;
+C -1 ; WX 600 ; N Scedilla ; B 54 -206 673 582 ;
+C -1 ; WX 600 ; N lcaron ; B 77 0 731 626 ;
+C -1 ; WX 600 ; N Kcommaaccent ; B 21 -250 692 562 ;
+C -1 ; WX 600 ; N Lacute ; B 39 0 636 784 ;
+C -1 ; WX 600 ; N trademark ; B 86 230 869 562 ;
+C -1 ; WX 600 ; N edotaccent ; B 81 -15 605 638 ;
+C -1 ; WX 600 ; N Igrave ; B 77 0 643 784 ;
+C -1 ; WX 600 ; N Imacron ; B 77 0 663 708 ;
+C -1 ; WX 600 ; N Lcaron ; B 39 0 757 562 ;
+C -1 ; WX 600 ; N onehalf ; B 22 -60 716 661 ;
+C -1 ; WX 600 ; N lessequal ; B 26 0 671 696 ;
+C -1 ; WX 600 ; N ocircumflex ; B 71 -15 622 657 ;
+C -1 ; WX 600 ; N ntilde ; B 18 0 643 636 ;
+C -1 ; WX 600 ; N Uhungarumlaut ; B 101 -18 805 784 ;
+C -1 ; WX 600 ; N Eacute ; B 25 0 670 784 ;
+C -1 ; WX 600 ; N emacron ; B 81 -15 637 585 ;
+C -1 ; WX 600 ; N gbreve ; B 40 -146 674 661 ;
+C -1 ; WX 600 ; N onequarter ; B 13 -60 707 661 ;
+C -1 ; WX 600 ; N Scaron ; B 54 -22 689 790 ;
+C -1 ; WX 600 ; N Scommaaccent ; B 54 -250 673 582 ;
+C -1 ; WX 600 ; N Ohungarumlaut ; B 74 -18 795 784 ;
+C -1 ; WX 600 ; N degree ; B 173 243 570 616 ;
+C -1 ; WX 600 ; N ograve ; B 71 -15 622 661 ;
+C -1 ; WX 600 ; N Ccaron ; B 74 -18 689 790 ;
+C -1 ; WX 600 ; N ugrave ; B 70 -15 592 661 ;
+C -1 ; WX 600 ; N radical ; B 67 -104 635 778 ;
+C -1 ; WX 600 ; N Dcaron ; B 30 0 664 790 ;
+C -1 ; WX 600 ; N rcommaaccent ; B 47 -250 655 454 ;
+C -1 ; WX 600 ; N Ntilde ; B 8 -12 730 759 ;
+C -1 ; WX 600 ; N otilde ; B 71 -15 643 636 ;
+C -1 ; WX 600 ; N Rcommaaccent ; B 24 -250 617 562 ;
+C -1 ; WX 600 ; N Lcommaaccent ; B 39 -250 636 562 ;
+C -1 ; WX 600 ; N Atilde ; B -9 0 669 759 ;
+C -1 ; WX 600 ; N Aogonek ; B -9 -199 632 562 ;
+C -1 ; WX 600 ; N Aring ; B -9 0 632 801 ;
+C -1 ; WX 600 ; N Otilde ; B 74 -18 669 759 ;
+C -1 ; WX 600 ; N zdotaccent ; B 81 0 614 638 ;
+C -1 ; WX 600 ; N Ecaron ; B 25 0 670 790 ;
+C -1 ; WX 600 ; N Iogonek ; B 77 -199 643 562 ;
+C -1 ; WX 600 ; N kcommaaccent ; B 33 -250 643 626 ;
+C -1 ; WX 600 ; N minus ; B 114 203 596 313 ;
+C -1 ; WX 600 ; N Icircumflex ; B 77 0 643 780 ;
+C -1 ; WX 600 ; N ncaron ; B 18 0 633 667 ;
+C -1 ; WX 600 ; N tcommaaccent ; B 118 -250 567 562 ;
+C -1 ; WX 600 ; N logicalnot ; B 135 103 617 413 ;
+C -1 ; WX 600 ; N odieresis ; B 71 -15 622 638 ;
+C -1 ; WX 600 ; N udieresis ; B 70 -15 595 638 ;
+C -1 ; WX 600 ; N notequal ; B 30 -47 626 563 ;
+C -1 ; WX 600 ; N gcommaaccent ; B 40 -146 674 714 ;
+C -1 ; WX 600 ; N eth ; B 93 -27 661 626 ;
+C -1 ; WX 600 ; N zcaron ; B 81 0 643 667 ;
+C -1 ; WX 600 ; N ncommaaccent ; B 18 -250 615 454 ;
+C -1 ; WX 600 ; N onesuperior ; B 212 230 514 616 ;
+C -1 ; WX 600 ; N imacron ; B 77 0 575 585 ;
+C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+EndFontMetrics
diff --git a/program/libraries/afm/Courier-Oblique.afm b/program/libraries/afm/Courier-Oblique.afm
--- /dev/null
@@ -0,0 +1,342 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
+Comment Creation Date: Thu May 1 17:37:52 1997
+Comment UniqueID 43051
+Comment VMusage 16248 75829
+FontName Courier-Oblique
+FullName Courier Oblique
+FamilyName Courier
+Weight Medium
+ItalicAngle -12
+IsFixedPitch true
+CharacterSet ExtendedRoman
+FontBBox -27 -250 849 805
+UnderlinePosition -100
+UnderlineThickness 50
+Version 003.000
+Notice Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
+EncodingScheme AdobeStandardEncoding
+CapHeight 562
+XHeight 426
+Ascender 629
+Descender -157
+StdHW 51
+StdVW 51
+StartCharMetrics 315
+C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 600 ; N exclam ; B 243 -15 464 572 ;
+C 34 ; WX 600 ; N quotedbl ; B 273 328 532 562 ;
+C 35 ; WX 600 ; N numbersign ; B 133 -32 596 639 ;
+C 36 ; WX 600 ; N dollar ; B 108 -126 596 662 ;
+C 37 ; WX 600 ; N percent ; B 134 -15 599 622 ;
+C 38 ; WX 600 ; N ampersand ; B 87 -15 580 543 ;
+C 39 ; WX 600 ; N quoteright ; B 283 328 495 562 ;
+C 40 ; WX 600 ; N parenleft ; B 313 -108 572 622 ;
+C 41 ; WX 600 ; N parenright ; B 137 -108 396 622 ;
+C 42 ; WX 600 ; N asterisk ; B 212 257 580 607 ;
+C 43 ; WX 600 ; N plus ; B 129 44 580 470 ;
+C 44 ; WX 600 ; N comma ; B 157 -112 370 122 ;
+C 45 ; WX 600 ; N hyphen ; B 152 231 558 285 ;
+C 46 ; WX 600 ; N period ; B 238 -15 382 109 ;
+C 47 ; WX 600 ; N slash ; B 112 -80 604 629 ;
+C 48 ; WX 600 ; N zero ; B 154 -15 575 622 ;
+C 49 ; WX 600 ; N one ; B 98 0 515 622 ;
+C 50 ; WX 600 ; N two ; B 70 0 568 622 ;
+C 51 ; WX 600 ; N three ; B 82 -15 538 622 ;
+C 52 ; WX 600 ; N four ; B 108 0 541 622 ;
+C 53 ; WX 600 ; N five ; B 99 -15 589 607 ;
+C 54 ; WX 600 ; N six ; B 155 -15 629 622 ;
+C 55 ; WX 600 ; N seven ; B 182 0 612 607 ;
+C 56 ; WX 600 ; N eight ; B 132 -15 588 622 ;
+C 57 ; WX 600 ; N nine ; B 93 -15 574 622 ;
+C 58 ; WX 600 ; N colon ; B 238 -15 441 385 ;
+C 59 ; WX 600 ; N semicolon ; B 157 -112 441 385 ;
+C 60 ; WX 600 ; N less ; B 96 42 610 472 ;
+C 61 ; WX 600 ; N equal ; B 109 138 600 376 ;
+C 62 ; WX 600 ; N greater ; B 85 42 599 472 ;
+C 63 ; WX 600 ; N question ; B 222 -15 583 572 ;
+C 64 ; WX 600 ; N at ; B 127 -15 582 622 ;
+C 65 ; WX 600 ; N A ; B 3 0 607 562 ;
+C 66 ; WX 600 ; N B ; B 43 0 616 562 ;
+C 67 ; WX 600 ; N C ; B 93 -18 655 580 ;
+C 68 ; WX 600 ; N D ; B 43 0 645 562 ;
+C 69 ; WX 600 ; N E ; B 53 0 660 562 ;
+C 70 ; WX 600 ; N F ; B 53 0 660 562 ;
+C 71 ; WX 600 ; N G ; B 83 -18 645 580 ;
+C 72 ; WX 600 ; N H ; B 32 0 687 562 ;
+C 73 ; WX 600 ; N I ; B 96 0 623 562 ;
+C 74 ; WX 600 ; N J ; B 52 -18 685 562 ;
+C 75 ; WX 600 ; N K ; B 38 0 671 562 ;
+C 76 ; WX 600 ; N L ; B 47 0 607 562 ;
+C 77 ; WX 600 ; N M ; B 4 0 715 562 ;
+C 78 ; WX 600 ; N N ; B 7 -13 712 562 ;
+C 79 ; WX 600 ; N O ; B 94 -18 625 580 ;
+C 80 ; WX 600 ; N P ; B 79 0 644 562 ;
+C 81 ; WX 600 ; N Q ; B 95 -138 625 580 ;
+C 82 ; WX 600 ; N R ; B 38 0 598 562 ;
+C 83 ; WX 600 ; N S ; B 76 -20 650 580 ;
+C 84 ; WX 600 ; N T ; B 108 0 665 562 ;
+C 85 ; WX 600 ; N U ; B 125 -18 702 562 ;
+C 86 ; WX 600 ; N V ; B 105 -13 723 562 ;
+C 87 ; WX 600 ; N W ; B 106 -13 722 562 ;
+C 88 ; WX 600 ; N X ; B 23 0 675 562 ;
+C 89 ; WX 600 ; N Y ; B 133 0 695 562 ;
+C 90 ; WX 600 ; N Z ; B 86 0 610 562 ;
+C 91 ; WX 600 ; N bracketleft ; B 246 -108 574 622 ;
+C 92 ; WX 600 ; N backslash ; B 249 -80 468 629 ;
+C 93 ; WX 600 ; N bracketright ; B 135 -108 463 622 ;
+C 94 ; WX 600 ; N asciicircum ; B 175 354 587 622 ;
+C 95 ; WX 600 ; N underscore ; B -27 -125 584 -75 ;
+C 96 ; WX 600 ; N quoteleft ; B 343 328 457 562 ;
+C 97 ; WX 600 ; N a ; B 76 -15 569 441 ;
+C 98 ; WX 600 ; N b ; B 29 -15 625 629 ;
+C 99 ; WX 600 ; N c ; B 106 -15 608 441 ;
+C 100 ; WX 600 ; N d ; B 85 -15 640 629 ;
+C 101 ; WX 600 ; N e ; B 106 -15 598 441 ;
+C 102 ; WX 600 ; N f ; B 114 0 662 629 ; L i fi ; L l fl ;
+C 103 ; WX 600 ; N g ; B 61 -157 657 441 ;
+C 104 ; WX 600 ; N h ; B 33 0 592 629 ;
+C 105 ; WX 600 ; N i ; B 95 0 515 657 ;
+C 106 ; WX 600 ; N j ; B 52 -157 550 657 ;
+C 107 ; WX 600 ; N k ; B 58 0 633 629 ;
+C 108 ; WX 600 ; N l ; B 95 0 515 629 ;
+C 109 ; WX 600 ; N m ; B -5 0 615 441 ;
+C 110 ; WX 600 ; N n ; B 26 0 585 441 ;
+C 111 ; WX 600 ; N o ; B 102 -15 588 441 ;
+C 112 ; WX 600 ; N p ; B -24 -157 605 441 ;
+C 113 ; WX 600 ; N q ; B 85 -157 682 441 ;
+C 114 ; WX 600 ; N r ; B 60 0 636 441 ;
+C 115 ; WX 600 ; N s ; B 78 -15 584 441 ;
+C 116 ; WX 600 ; N t ; B 167 -15 561 561 ;
+C 117 ; WX 600 ; N u ; B 101 -15 572 426 ;
+C 118 ; WX 600 ; N v ; B 90 -10 681 426 ;
+C 119 ; WX 600 ; N w ; B 76 -10 695 426 ;
+C 120 ; WX 600 ; N x ; B 20 0 655 426 ;
+C 121 ; WX 600 ; N y ; B -4 -157 683 426 ;
+C 122 ; WX 600 ; N z ; B 99 0 593 426 ;
+C 123 ; WX 600 ; N braceleft ; B 233 -108 569 622 ;
+C 124 ; WX 600 ; N bar ; B 222 -250 485 750 ;
+C 125 ; WX 600 ; N braceright ; B 140 -108 477 622 ;
+C 126 ; WX 600 ; N asciitilde ; B 116 197 600 320 ;
+C 161 ; WX 600 ; N exclamdown ; B 225 -157 445 430 ;
+C 162 ; WX 600 ; N cent ; B 151 -49 588 614 ;
+C 163 ; WX 600 ; N sterling ; B 124 -21 621 611 ;
+C 164 ; WX 600 ; N fraction ; B 84 -57 646 665 ;
+C 165 ; WX 600 ; N yen ; B 120 0 693 562 ;
+C 166 ; WX 600 ; N florin ; B -26 -143 671 622 ;
+C 167 ; WX 600 ; N section ; B 104 -78 590 580 ;
+C 168 ; WX 600 ; N currency ; B 94 58 628 506 ;
+C 169 ; WX 600 ; N quotesingle ; B 345 328 460 562 ;
+C 170 ; WX 600 ; N quotedblleft ; B 262 328 541 562 ;
+C 171 ; WX 600 ; N guillemotleft ; B 92 70 652 446 ;
+C 172 ; WX 600 ; N guilsinglleft ; B 204 70 540 446 ;
+C 173 ; WX 600 ; N guilsinglright ; B 170 70 506 446 ;
+C 174 ; WX 600 ; N fi ; B 3 0 619 629 ;
+C 175 ; WX 600 ; N fl ; B 3 0 619 629 ;
+C 177 ; WX 600 ; N endash ; B 124 231 586 285 ;
+C 178 ; WX 600 ; N dagger ; B 217 -78 546 580 ;
+C 179 ; WX 600 ; N daggerdbl ; B 163 -78 546 580 ;
+C 180 ; WX 600 ; N periodcentered ; B 275 189 434 327 ;
+C 182 ; WX 600 ; N paragraph ; B 100 -78 630 562 ;
+C 183 ; WX 600 ; N bullet ; B 224 130 485 383 ;
+C 184 ; WX 600 ; N quotesinglbase ; B 185 -134 397 100 ;
+C 185 ; WX 600 ; N quotedblbase ; B 115 -134 478 100 ;
+C 186 ; WX 600 ; N quotedblright ; B 213 328 576 562 ;
+C 187 ; WX 600 ; N guillemotright ; B 58 70 618 446 ;
+C 188 ; WX 600 ; N ellipsis ; B 46 -15 575 111 ;
+C 189 ; WX 600 ; N perthousand ; B 59 -15 627 622 ;
+C 191 ; WX 600 ; N questiondown ; B 105 -157 466 430 ;
+C 193 ; WX 600 ; N grave ; B 294 497 484 672 ;
+C 194 ; WX 600 ; N acute ; B 348 497 612 672 ;
+C 195 ; WX 600 ; N circumflex ; B 229 477 581 654 ;
+C 196 ; WX 600 ; N tilde ; B 212 489 629 606 ;
+C 197 ; WX 600 ; N macron ; B 232 525 600 565 ;
+C 198 ; WX 600 ; N breve ; B 279 501 576 609 ;
+C 199 ; WX 600 ; N dotaccent ; B 373 537 478 640 ;
+C 200 ; WX 600 ; N dieresis ; B 272 537 579 640 ;
+C 202 ; WX 600 ; N ring ; B 332 463 500 627 ;
+C 203 ; WX 600 ; N cedilla ; B 197 -151 344 10 ;
+C 205 ; WX 600 ; N hungarumlaut ; B 239 497 683 672 ;
+C 206 ; WX 600 ; N ogonek ; B 189 -172 377 4 ;
+C 207 ; WX 600 ; N caron ; B 262 492 614 669 ;
+C 208 ; WX 600 ; N emdash ; B 49 231 661 285 ;
+C 225 ; WX 600 ; N AE ; B 3 0 655 562 ;
+C 227 ; WX 600 ; N ordfeminine ; B 209 249 512 580 ;
+C 232 ; WX 600 ; N Lslash ; B 47 0 607 562 ;
+C 233 ; WX 600 ; N Oslash ; B 94 -80 625 629 ;
+C 234 ; WX 600 ; N OE ; B 59 0 672 562 ;
+C 235 ; WX 600 ; N ordmasculine ; B 210 249 535 580 ;
+C 241 ; WX 600 ; N ae ; B 41 -15 626 441 ;
+C 245 ; WX 600 ; N dotlessi ; B 95 0 515 426 ;
+C 248 ; WX 600 ; N lslash ; B 95 0 587 629 ;
+C 249 ; WX 600 ; N oslash ; B 102 -80 588 506 ;
+C 250 ; WX 600 ; N oe ; B 54 -15 615 441 ;
+C 251 ; WX 600 ; N germandbls ; B 48 -15 617 629 ;
+C -1 ; WX 600 ; N Idieresis ; B 96 0 623 753 ;
+C -1 ; WX 600 ; N eacute ; B 106 -15 612 672 ;
+C -1 ; WX 600 ; N abreve ; B 76 -15 576 609 ;
+C -1 ; WX 600 ; N uhungarumlaut ; B 101 -15 723 672 ;
+C -1 ; WX 600 ; N ecaron ; B 106 -15 614 669 ;
+C -1 ; WX 600 ; N Ydieresis ; B 133 0 695 753 ;
+C -1 ; WX 600 ; N divide ; B 136 48 573 467 ;
+C -1 ; WX 600 ; N Yacute ; B 133 0 695 805 ;
+C -1 ; WX 600 ; N Acircumflex ; B 3 0 607 787 ;
+C -1 ; WX 600 ; N aacute ; B 76 -15 612 672 ;
+C -1 ; WX 600 ; N Ucircumflex ; B 125 -18 702 787 ;
+C -1 ; WX 600 ; N yacute ; B -4 -157 683 672 ;
+C -1 ; WX 600 ; N scommaaccent ; B 78 -250 584 441 ;
+C -1 ; WX 600 ; N ecircumflex ; B 106 -15 598 654 ;
+C -1 ; WX 600 ; N Uring ; B 125 -18 702 760 ;
+C -1 ; WX 600 ; N Udieresis ; B 125 -18 702 753 ;
+C -1 ; WX 600 ; N aogonek ; B 76 -172 569 441 ;
+C -1 ; WX 600 ; N Uacute ; B 125 -18 702 805 ;
+C -1 ; WX 600 ; N uogonek ; B 101 -172 572 426 ;
+C -1 ; WX 600 ; N Edieresis ; B 53 0 660 753 ;
+C -1 ; WX 600 ; N Dcroat ; B 43 0 645 562 ;
+C -1 ; WX 600 ; N commaaccent ; B 145 -250 323 -58 ;
+C -1 ; WX 600 ; N copyright ; B 53 -18 667 580 ;
+C -1 ; WX 600 ; N Emacron ; B 53 0 660 698 ;
+C -1 ; WX 600 ; N ccaron ; B 106 -15 614 669 ;
+C -1 ; WX 600 ; N aring ; B 76 -15 569 627 ;
+C -1 ; WX 600 ; N Ncommaaccent ; B 7 -250 712 562 ;
+C -1 ; WX 600 ; N lacute ; B 95 0 640 805 ;
+C -1 ; WX 600 ; N agrave ; B 76 -15 569 672 ;
+C -1 ; WX 600 ; N Tcommaaccent ; B 108 -250 665 562 ;
+C -1 ; WX 600 ; N Cacute ; B 93 -18 655 805 ;
+C -1 ; WX 600 ; N atilde ; B 76 -15 629 606 ;
+C -1 ; WX 600 ; N Edotaccent ; B 53 0 660 753 ;
+C -1 ; WX 600 ; N scaron ; B 78 -15 614 669 ;
+C -1 ; WX 600 ; N scedilla ; B 78 -151 584 441 ;
+C -1 ; WX 600 ; N iacute ; B 95 0 612 672 ;
+C -1 ; WX 600 ; N lozenge ; B 94 0 519 706 ;
+C -1 ; WX 600 ; N Rcaron ; B 38 0 642 802 ;
+C -1 ; WX 600 ; N Gcommaaccent ; B 83 -250 645 580 ;
+C -1 ; WX 600 ; N ucircumflex ; B 101 -15 572 654 ;
+C -1 ; WX 600 ; N acircumflex ; B 76 -15 581 654 ;
+C -1 ; WX 600 ; N Amacron ; B 3 0 607 698 ;
+C -1 ; WX 600 ; N rcaron ; B 60 0 636 669 ;
+C -1 ; WX 600 ; N ccedilla ; B 106 -151 614 441 ;
+C -1 ; WX 600 ; N Zdotaccent ; B 86 0 610 753 ;
+C -1 ; WX 600 ; N Thorn ; B 79 0 606 562 ;
+C -1 ; WX 600 ; N Omacron ; B 94 -18 628 698 ;
+C -1 ; WX 600 ; N Racute ; B 38 0 670 805 ;
+C -1 ; WX 600 ; N Sacute ; B 76 -20 650 805 ;
+C -1 ; WX 600 ; N dcaron ; B 85 -15 849 629 ;
+C -1 ; WX 600 ; N Umacron ; B 125 -18 702 698 ;
+C -1 ; WX 600 ; N uring ; B 101 -15 572 627 ;
+C -1 ; WX 600 ; N threesuperior ; B 213 240 501 622 ;
+C -1 ; WX 600 ; N Ograve ; B 94 -18 625 805 ;
+C -1 ; WX 600 ; N Agrave ; B 3 0 607 805 ;
+C -1 ; WX 600 ; N Abreve ; B 3 0 607 732 ;
+C -1 ; WX 600 ; N multiply ; B 103 43 607 470 ;
+C -1 ; WX 600 ; N uacute ; B 101 -15 602 672 ;
+C -1 ; WX 600 ; N Tcaron ; B 108 0 665 802 ;
+C -1 ; WX 600 ; N partialdiff ; B 45 -38 546 710 ;
+C -1 ; WX 600 ; N ydieresis ; B -4 -157 683 620 ;
+C -1 ; WX 600 ; N Nacute ; B 7 -13 712 805 ;
+C -1 ; WX 600 ; N icircumflex ; B 95 0 551 654 ;
+C -1 ; WX 600 ; N Ecircumflex ; B 53 0 660 787 ;
+C -1 ; WX 600 ; N adieresis ; B 76 -15 575 620 ;
+C -1 ; WX 600 ; N edieresis ; B 106 -15 598 620 ;
+C -1 ; WX 600 ; N cacute ; B 106 -15 612 672 ;
+C -1 ; WX 600 ; N nacute ; B 26 0 602 672 ;
+C -1 ; WX 600 ; N umacron ; B 101 -15 600 565 ;
+C -1 ; WX 600 ; N Ncaron ; B 7 -13 712 802 ;
+C -1 ; WX 600 ; N Iacute ; B 96 0 640 805 ;
+C -1 ; WX 600 ; N plusminus ; B 96 44 594 558 ;
+C -1 ; WX 600 ; N brokenbar ; B 238 -175 469 675 ;
+C -1 ; WX 600 ; N registered ; B 53 -18 667 580 ;
+C -1 ; WX 600 ; N Gbreve ; B 83 -18 645 732 ;
+C -1 ; WX 600 ; N Idotaccent ; B 96 0 623 753 ;
+C -1 ; WX 600 ; N summation ; B 15 -10 670 706 ;
+C -1 ; WX 600 ; N Egrave ; B 53 0 660 805 ;
+C -1 ; WX 600 ; N racute ; B 60 0 636 672 ;
+C -1 ; WX 600 ; N omacron ; B 102 -15 600 565 ;
+C -1 ; WX 600 ; N Zacute ; B 86 0 670 805 ;
+C -1 ; WX 600 ; N Zcaron ; B 86 0 642 802 ;
+C -1 ; WX 600 ; N greaterequal ; B 98 0 594 710 ;
+C -1 ; WX 600 ; N Eth ; B 43 0 645 562 ;
+C -1 ; WX 600 ; N Ccedilla ; B 93 -151 658 580 ;
+C -1 ; WX 600 ; N lcommaaccent ; B 95 -250 515 629 ;
+C -1 ; WX 600 ; N tcaron ; B 167 -15 587 717 ;
+C -1 ; WX 600 ; N eogonek ; B 106 -172 598 441 ;
+C -1 ; WX 600 ; N Uogonek ; B 124 -172 702 562 ;
+C -1 ; WX 600 ; N Aacute ; B 3 0 660 805 ;
+C -1 ; WX 600 ; N Adieresis ; B 3 0 607 753 ;
+C -1 ; WX 600 ; N egrave ; B 106 -15 598 672 ;
+C -1 ; WX 600 ; N zacute ; B 99 0 612 672 ;
+C -1 ; WX 600 ; N iogonek ; B 95 -172 515 657 ;
+C -1 ; WX 600 ; N Oacute ; B 94 -18 640 805 ;
+C -1 ; WX 600 ; N oacute ; B 102 -15 612 672 ;
+C -1 ; WX 600 ; N amacron ; B 76 -15 600 565 ;
+C -1 ; WX 600 ; N sacute ; B 78 -15 612 672 ;
+C -1 ; WX 600 ; N idieresis ; B 95 0 545 620 ;
+C -1 ; WX 600 ; N Ocircumflex ; B 94 -18 625 787 ;
+C -1 ; WX 600 ; N Ugrave ; B 125 -18 702 805 ;
+C -1 ; WX 600 ; N Delta ; B 6 0 598 688 ;
+C -1 ; WX 600 ; N thorn ; B -24 -157 605 629 ;
+C -1 ; WX 600 ; N twosuperior ; B 230 249 535 622 ;
+C -1 ; WX 600 ; N Odieresis ; B 94 -18 625 753 ;
+C -1 ; WX 600 ; N mu ; B 72 -157 572 426 ;
+C -1 ; WX 600 ; N igrave ; B 95 0 515 672 ;
+C -1 ; WX 600 ; N ohungarumlaut ; B 102 -15 723 672 ;
+C -1 ; WX 600 ; N Eogonek ; B 53 -172 660 562 ;
+C -1 ; WX 600 ; N dcroat ; B 85 -15 704 629 ;
+C -1 ; WX 600 ; N threequarters ; B 73 -56 659 666 ;
+C -1 ; WX 600 ; N Scedilla ; B 76 -151 650 580 ;
+C -1 ; WX 600 ; N lcaron ; B 95 0 667 629 ;
+C -1 ; WX 600 ; N Kcommaaccent ; B 38 -250 671 562 ;
+C -1 ; WX 600 ; N Lacute ; B 47 0 607 805 ;
+C -1 ; WX 600 ; N trademark ; B 75 263 742 562 ;
+C -1 ; WX 600 ; N edotaccent ; B 106 -15 598 620 ;
+C -1 ; WX 600 ; N Igrave ; B 96 0 623 805 ;
+C -1 ; WX 600 ; N Imacron ; B 96 0 628 698 ;
+C -1 ; WX 600 ; N Lcaron ; B 47 0 632 562 ;
+C -1 ; WX 600 ; N onehalf ; B 65 -57 669 665 ;
+C -1 ; WX 600 ; N lessequal ; B 98 0 645 710 ;
+C -1 ; WX 600 ; N ocircumflex ; B 102 -15 588 654 ;
+C -1 ; WX 600 ; N ntilde ; B 26 0 629 606 ;
+C -1 ; WX 600 ; N Uhungarumlaut ; B 125 -18 761 805 ;
+C -1 ; WX 600 ; N Eacute ; B 53 0 670 805 ;
+C -1 ; WX 600 ; N emacron ; B 106 -15 600 565 ;
+C -1 ; WX 600 ; N gbreve ; B 61 -157 657 609 ;
+C -1 ; WX 600 ; N onequarter ; B 65 -57 674 665 ;
+C -1 ; WX 600 ; N Scaron ; B 76 -20 672 802 ;
+C -1 ; WX 600 ; N Scommaaccent ; B 76 -250 650 580 ;
+C -1 ; WX 600 ; N Ohungarumlaut ; B 94 -18 751 805 ;
+C -1 ; WX 600 ; N degree ; B 214 269 576 622 ;
+C -1 ; WX 600 ; N ograve ; B 102 -15 588 672 ;
+C -1 ; WX 600 ; N Ccaron ; B 93 -18 672 802 ;
+C -1 ; WX 600 ; N ugrave ; B 101 -15 572 672 ;
+C -1 ; WX 600 ; N radical ; B 85 -15 765 792 ;
+C -1 ; WX 600 ; N Dcaron ; B 43 0 645 802 ;
+C -1 ; WX 600 ; N rcommaaccent ; B 60 -250 636 441 ;
+C -1 ; WX 600 ; N Ntilde ; B 7 -13 712 729 ;
+C -1 ; WX 600 ; N otilde ; B 102 -15 629 606 ;
+C -1 ; WX 600 ; N Rcommaaccent ; B 38 -250 598 562 ;
+C -1 ; WX 600 ; N Lcommaaccent ; B 47 -250 607 562 ;
+C -1 ; WX 600 ; N Atilde ; B 3 0 655 729 ;
+C -1 ; WX 600 ; N Aogonek ; B 3 -172 607 562 ;
+C -1 ; WX 600 ; N Aring ; B 3 0 607 750 ;
+C -1 ; WX 600 ; N Otilde ; B 94 -18 655 729 ;
+C -1 ; WX 600 ; N zdotaccent ; B 99 0 593 620 ;
+C -1 ; WX 600 ; N Ecaron ; B 53 0 660 802 ;
+C -1 ; WX 600 ; N Iogonek ; B 96 -172 623 562 ;
+C -1 ; WX 600 ; N kcommaaccent ; B 58 -250 633 629 ;
+C -1 ; WX 600 ; N minus ; B 129 232 580 283 ;
+C -1 ; WX 600 ; N Icircumflex ; B 96 0 623 787 ;
+C -1 ; WX 600 ; N ncaron ; B 26 0 614 669 ;
+C -1 ; WX 600 ; N tcommaaccent ; B 165 -250 561 561 ;
+C -1 ; WX 600 ; N logicalnot ; B 155 108 591 369 ;
+C -1 ; WX 600 ; N odieresis ; B 102 -15 588 620 ;
+C -1 ; WX 600 ; N udieresis ; B 101 -15 575 620 ;
+C -1 ; WX 600 ; N notequal ; B 43 -16 621 529 ;
+C -1 ; WX 600 ; N gcommaaccent ; B 61 -157 657 708 ;
+C -1 ; WX 600 ; N eth ; B 102 -15 639 629 ;
+C -1 ; WX 600 ; N zcaron ; B 99 0 624 669 ;
+C -1 ; WX 600 ; N ncommaaccent ; B 26 -250 585 441 ;
+C -1 ; WX 600 ; N onesuperior ; B 231 249 491 622 ;
+C -1 ; WX 600 ; N imacron ; B 95 0 543 565 ;
+C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+EndFontMetrics
diff --git a/program/libraries/afm/Courier.afm b/program/libraries/afm/Courier.afm
--- /dev/null
@@ -0,0 +1,342 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
+Comment Creation Date: Thu May 1 17:27:09 1997
+Comment UniqueID 43050
+Comment VMusage 39754 50779
+FontName Courier
+FullName Courier
+FamilyName Courier
+Weight Medium
+ItalicAngle 0
+IsFixedPitch true
+CharacterSet ExtendedRoman
+FontBBox -23 -250 715 805
+UnderlinePosition -100
+UnderlineThickness 50
+Version 003.000
+Notice Copyright (c) 1989, 1990, 1991, 1992, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
+EncodingScheme AdobeStandardEncoding
+CapHeight 562
+XHeight 426
+Ascender 629
+Descender -157
+StdHW 51
+StdVW 51
+StartCharMetrics 315
+C 32 ; WX 600 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 600 ; N exclam ; B 236 -15 364 572 ;
+C 34 ; WX 600 ; N quotedbl ; B 187 328 413 562 ;
+C 35 ; WX 600 ; N numbersign ; B 93 -32 507 639 ;
+C 36 ; WX 600 ; N dollar ; B 105 -126 496 662 ;
+C 37 ; WX 600 ; N percent ; B 81 -15 518 622 ;
+C 38 ; WX 600 ; N ampersand ; B 63 -15 538 543 ;
+C 39 ; WX 600 ; N quoteright ; B 213 328 376 562 ;
+C 40 ; WX 600 ; N parenleft ; B 269 -108 440 622 ;
+C 41 ; WX 600 ; N parenright ; B 160 -108 331 622 ;
+C 42 ; WX 600 ; N asterisk ; B 116 257 484 607 ;
+C 43 ; WX 600 ; N plus ; B 80 44 520 470 ;
+C 44 ; WX 600 ; N comma ; B 181 -112 344 122 ;
+C 45 ; WX 600 ; N hyphen ; B 103 231 497 285 ;
+C 46 ; WX 600 ; N period ; B 229 -15 371 109 ;
+C 47 ; WX 600 ; N slash ; B 125 -80 475 629 ;
+C 48 ; WX 600 ; N zero ; B 106 -15 494 622 ;
+C 49 ; WX 600 ; N one ; B 96 0 505 622 ;
+C 50 ; WX 600 ; N two ; B 70 0 471 622 ;
+C 51 ; WX 600 ; N three ; B 75 -15 466 622 ;
+C 52 ; WX 600 ; N four ; B 78 0 500 622 ;
+C 53 ; WX 600 ; N five ; B 92 -15 497 607 ;
+C 54 ; WX 600 ; N six ; B 111 -15 497 622 ;
+C 55 ; WX 600 ; N seven ; B 82 0 483 607 ;
+C 56 ; WX 600 ; N eight ; B 102 -15 498 622 ;
+C 57 ; WX 600 ; N nine ; B 96 -15 489 622 ;
+C 58 ; WX 600 ; N colon ; B 229 -15 371 385 ;
+C 59 ; WX 600 ; N semicolon ; B 181 -112 371 385 ;
+C 60 ; WX 600 ; N less ; B 41 42 519 472 ;
+C 61 ; WX 600 ; N equal ; B 80 138 520 376 ;
+C 62 ; WX 600 ; N greater ; B 66 42 544 472 ;
+C 63 ; WX 600 ; N question ; B 129 -15 492 572 ;
+C 64 ; WX 600 ; N at ; B 77 -15 533 622 ;
+C 65 ; WX 600 ; N A ; B 3 0 597 562 ;
+C 66 ; WX 600 ; N B ; B 43 0 559 562 ;
+C 67 ; WX 600 ; N C ; B 41 -18 540 580 ;
+C 68 ; WX 600 ; N D ; B 43 0 574 562 ;
+C 69 ; WX 600 ; N E ; B 53 0 550 562 ;
+C 70 ; WX 600 ; N F ; B 53 0 545 562 ;
+C 71 ; WX 600 ; N G ; B 31 -18 575 580 ;
+C 72 ; WX 600 ; N H ; B 32 0 568 562 ;
+C 73 ; WX 600 ; N I ; B 96 0 504 562 ;
+C 74 ; WX 600 ; N J ; B 34 -18 566 562 ;
+C 75 ; WX 600 ; N K ; B 38 0 582 562 ;
+C 76 ; WX 600 ; N L ; B 47 0 554 562 ;
+C 77 ; WX 600 ; N M ; B 4 0 596 562 ;
+C 78 ; WX 600 ; N N ; B 7 -13 593 562 ;
+C 79 ; WX 600 ; N O ; B 43 -18 557 580 ;
+C 80 ; WX 600 ; N P ; B 79 0 558 562 ;
+C 81 ; WX 600 ; N Q ; B 43 -138 557 580 ;
+C 82 ; WX 600 ; N R ; B 38 0 588 562 ;
+C 83 ; WX 600 ; N S ; B 72 -20 529 580 ;
+C 84 ; WX 600 ; N T ; B 38 0 563 562 ;
+C 85 ; WX 600 ; N U ; B 17 -18 583 562 ;
+C 86 ; WX 600 ; N V ; B -4 -13 604 562 ;
+C 87 ; WX 600 ; N W ; B -3 -13 603 562 ;
+C 88 ; WX 600 ; N X ; B 23 0 577 562 ;
+C 89 ; WX 600 ; N Y ; B 24 0 576 562 ;
+C 90 ; WX 600 ; N Z ; B 86 0 514 562 ;
+C 91 ; WX 600 ; N bracketleft ; B 269 -108 442 622 ;
+C 92 ; WX 600 ; N backslash ; B 118 -80 482 629 ;
+C 93 ; WX 600 ; N bracketright ; B 158 -108 331 622 ;
+C 94 ; WX 600 ; N asciicircum ; B 94 354 506 622 ;
+C 95 ; WX 600 ; N underscore ; B 0 -125 600 -75 ;
+C 96 ; WX 600 ; N quoteleft ; B 224 328 387 562 ;
+C 97 ; WX 600 ; N a ; B 53 -15 559 441 ;
+C 98 ; WX 600 ; N b ; B 14 -15 575 629 ;
+C 99 ; WX 600 ; N c ; B 66 -15 529 441 ;
+C 100 ; WX 600 ; N d ; B 45 -15 591 629 ;
+C 101 ; WX 600 ; N e ; B 66 -15 548 441 ;
+C 102 ; WX 600 ; N f ; B 114 0 531 629 ; L i fi ; L l fl ;
+C 103 ; WX 600 ; N g ; B 45 -157 566 441 ;
+C 104 ; WX 600 ; N h ; B 18 0 582 629 ;
+C 105 ; WX 600 ; N i ; B 95 0 505 657 ;
+C 106 ; WX 600 ; N j ; B 82 -157 410 657 ;
+C 107 ; WX 600 ; N k ; B 43 0 580 629 ;
+C 108 ; WX 600 ; N l ; B 95 0 505 629 ;
+C 109 ; WX 600 ; N m ; B -5 0 605 441 ;
+C 110 ; WX 600 ; N n ; B 26 0 575 441 ;
+C 111 ; WX 600 ; N o ; B 62 -15 538 441 ;
+C 112 ; WX 600 ; N p ; B 9 -157 555 441 ;
+C 113 ; WX 600 ; N q ; B 45 -157 591 441 ;
+C 114 ; WX 600 ; N r ; B 60 0 559 441 ;
+C 115 ; WX 600 ; N s ; B 80 -15 513 441 ;
+C 116 ; WX 600 ; N t ; B 87 -15 530 561 ;
+C 117 ; WX 600 ; N u ; B 21 -15 562 426 ;
+C 118 ; WX 600 ; N v ; B 10 -10 590 426 ;
+C 119 ; WX 600 ; N w ; B -4 -10 604 426 ;
+C 120 ; WX 600 ; N x ; B 20 0 580 426 ;
+C 121 ; WX 600 ; N y ; B 7 -157 592 426 ;
+C 122 ; WX 600 ; N z ; B 99 0 502 426 ;
+C 123 ; WX 600 ; N braceleft ; B 182 -108 437 622 ;
+C 124 ; WX 600 ; N bar ; B 275 -250 326 750 ;
+C 125 ; WX 600 ; N braceright ; B 163 -108 418 622 ;
+C 126 ; WX 600 ; N asciitilde ; B 63 197 540 320 ;
+C 161 ; WX 600 ; N exclamdown ; B 236 -157 364 430 ;
+C 162 ; WX 600 ; N cent ; B 96 -49 500 614 ;
+C 163 ; WX 600 ; N sterling ; B 84 -21 521 611 ;
+C 164 ; WX 600 ; N fraction ; B 92 -57 509 665 ;
+C 165 ; WX 600 ; N yen ; B 26 0 574 562 ;
+C 166 ; WX 600 ; N florin ; B 4 -143 539 622 ;
+C 167 ; WX 600 ; N section ; B 113 -78 488 580 ;
+C 168 ; WX 600 ; N currency ; B 73 58 527 506 ;
+C 169 ; WX 600 ; N quotesingle ; B 259 328 341 562 ;
+C 170 ; WX 600 ; N quotedblleft ; B 143 328 471 562 ;
+C 171 ; WX 600 ; N guillemotleft ; B 37 70 563 446 ;
+C 172 ; WX 600 ; N guilsinglleft ; B 149 70 451 446 ;
+C 173 ; WX 600 ; N guilsinglright ; B 149 70 451 446 ;
+C 174 ; WX 600 ; N fi ; B 3 0 597 629 ;
+C 175 ; WX 600 ; N fl ; B 3 0 597 629 ;
+C 177 ; WX 600 ; N endash ; B 75 231 525 285 ;
+C 178 ; WX 600 ; N dagger ; B 141 -78 459 580 ;
+C 179 ; WX 600 ; N daggerdbl ; B 141 -78 459 580 ;
+C 180 ; WX 600 ; N periodcentered ; B 222 189 378 327 ;
+C 182 ; WX 600 ; N paragraph ; B 50 -78 511 562 ;
+C 183 ; WX 600 ; N bullet ; B 172 130 428 383 ;
+C 184 ; WX 600 ; N quotesinglbase ; B 213 -134 376 100 ;
+C 185 ; WX 600 ; N quotedblbase ; B 143 -134 457 100 ;
+C 186 ; WX 600 ; N quotedblright ; B 143 328 457 562 ;
+C 187 ; WX 600 ; N guillemotright ; B 37 70 563 446 ;
+C 188 ; WX 600 ; N ellipsis ; B 37 -15 563 111 ;
+C 189 ; WX 600 ; N perthousand ; B 3 -15 600 622 ;
+C 191 ; WX 600 ; N questiondown ; B 108 -157 471 430 ;
+C 193 ; WX 600 ; N grave ; B 151 497 378 672 ;
+C 194 ; WX 600 ; N acute ; B 242 497 469 672 ;
+C 195 ; WX 600 ; N circumflex ; B 124 477 476 654 ;
+C 196 ; WX 600 ; N tilde ; B 105 489 503 606 ;
+C 197 ; WX 600 ; N macron ; B 120 525 480 565 ;
+C 198 ; WX 600 ; N breve ; B 153 501 447 609 ;
+C 199 ; WX 600 ; N dotaccent ; B 249 537 352 640 ;
+C 200 ; WX 600 ; N dieresis ; B 148 537 453 640 ;
+C 202 ; WX 600 ; N ring ; B 218 463 382 627 ;
+C 203 ; WX 600 ; N cedilla ; B 224 -151 362 10 ;
+C 205 ; WX 600 ; N hungarumlaut ; B 133 497 540 672 ;
+C 206 ; WX 600 ; N ogonek ; B 211 -172 407 4 ;
+C 207 ; WX 600 ; N caron ; B 124 492 476 669 ;
+C 208 ; WX 600 ; N emdash ; B 0 231 600 285 ;
+C 225 ; WX 600 ; N AE ; B 3 0 550 562 ;
+C 227 ; WX 600 ; N ordfeminine ; B 156 249 442 580 ;
+C 232 ; WX 600 ; N Lslash ; B 47 0 554 562 ;
+C 233 ; WX 600 ; N Oslash ; B 43 -80 557 629 ;
+C 234 ; WX 600 ; N OE ; B 7 0 567 562 ;
+C 235 ; WX 600 ; N ordmasculine ; B 157 249 443 580 ;
+C 241 ; WX 600 ; N ae ; B 19 -15 570 441 ;
+C 245 ; WX 600 ; N dotlessi ; B 95 0 505 426 ;
+C 248 ; WX 600 ; N lslash ; B 95 0 505 629 ;
+C 249 ; WX 600 ; N oslash ; B 62 -80 538 506 ;
+C 250 ; WX 600 ; N oe ; B 19 -15 559 441 ;
+C 251 ; WX 600 ; N germandbls ; B 48 -15 588 629 ;
+C -1 ; WX 600 ; N Idieresis ; B 96 0 504 753 ;
+C -1 ; WX 600 ; N eacute ; B 66 -15 548 672 ;
+C -1 ; WX 600 ; N abreve ; B 53 -15 559 609 ;
+C -1 ; WX 600 ; N uhungarumlaut ; B 21 -15 580 672 ;
+C -1 ; WX 600 ; N ecaron ; B 66 -15 548 669 ;
+C -1 ; WX 600 ; N Ydieresis ; B 24 0 576 753 ;
+C -1 ; WX 600 ; N divide ; B 87 48 513 467 ;
+C -1 ; WX 600 ; N Yacute ; B 24 0 576 805 ;
+C -1 ; WX 600 ; N Acircumflex ; B 3 0 597 787 ;
+C -1 ; WX 600 ; N aacute ; B 53 -15 559 672 ;
+C -1 ; WX 600 ; N Ucircumflex ; B 17 -18 583 787 ;
+C -1 ; WX 600 ; N yacute ; B 7 -157 592 672 ;
+C -1 ; WX 600 ; N scommaaccent ; B 80 -250 513 441 ;
+C -1 ; WX 600 ; N ecircumflex ; B 66 -15 548 654 ;
+C -1 ; WX 600 ; N Uring ; B 17 -18 583 760 ;
+C -1 ; WX 600 ; N Udieresis ; B 17 -18 583 753 ;
+C -1 ; WX 600 ; N aogonek ; B 53 -172 587 441 ;
+C -1 ; WX 600 ; N Uacute ; B 17 -18 583 805 ;
+C -1 ; WX 600 ; N uogonek ; B 21 -172 590 426 ;
+C -1 ; WX 600 ; N Edieresis ; B 53 0 550 753 ;
+C -1 ; WX 600 ; N Dcroat ; B 30 0 574 562 ;
+C -1 ; WX 600 ; N commaaccent ; B 198 -250 335 -58 ;
+C -1 ; WX 600 ; N copyright ; B 0 -18 600 580 ;
+C -1 ; WX 600 ; N Emacron ; B 53 0 550 698 ;
+C -1 ; WX 600 ; N ccaron ; B 66 -15 529 669 ;
+C -1 ; WX 600 ; N aring ; B 53 -15 559 627 ;
+C -1 ; WX 600 ; N Ncommaaccent ; B 7 -250 593 562 ;
+C -1 ; WX 600 ; N lacute ; B 95 0 505 805 ;
+C -1 ; WX 600 ; N agrave ; B 53 -15 559 672 ;
+C -1 ; WX 600 ; N Tcommaaccent ; B 38 -250 563 562 ;
+C -1 ; WX 600 ; N Cacute ; B 41 -18 540 805 ;
+C -1 ; WX 600 ; N atilde ; B 53 -15 559 606 ;
+C -1 ; WX 600 ; N Edotaccent ; B 53 0 550 753 ;
+C -1 ; WX 600 ; N scaron ; B 80 -15 513 669 ;
+C -1 ; WX 600 ; N scedilla ; B 80 -151 513 441 ;
+C -1 ; WX 600 ; N iacute ; B 95 0 505 672 ;
+C -1 ; WX 600 ; N lozenge ; B 18 0 443 706 ;
+C -1 ; WX 600 ; N Rcaron ; B 38 0 588 802 ;
+C -1 ; WX 600 ; N Gcommaaccent ; B 31 -250 575 580 ;
+C -1 ; WX 600 ; N ucircumflex ; B 21 -15 562 654 ;
+C -1 ; WX 600 ; N acircumflex ; B 53 -15 559 654 ;
+C -1 ; WX 600 ; N Amacron ; B 3 0 597 698 ;
+C -1 ; WX 600 ; N rcaron ; B 60 0 559 669 ;
+C -1 ; WX 600 ; N ccedilla ; B 66 -151 529 441 ;
+C -1 ; WX 600 ; N Zdotaccent ; B 86 0 514 753 ;
+C -1 ; WX 600 ; N Thorn ; B 79 0 538 562 ;
+C -1 ; WX 600 ; N Omacron ; B 43 -18 557 698 ;
+C -1 ; WX 600 ; N Racute ; B 38 0 588 805 ;
+C -1 ; WX 600 ; N Sacute ; B 72 -20 529 805 ;
+C -1 ; WX 600 ; N dcaron ; B 45 -15 715 629 ;
+C -1 ; WX 600 ; N Umacron ; B 17 -18 583 698 ;
+C -1 ; WX 600 ; N uring ; B 21 -15 562 627 ;
+C -1 ; WX 600 ; N threesuperior ; B 155 240 406 622 ;
+C -1 ; WX 600 ; N Ograve ; B 43 -18 557 805 ;
+C -1 ; WX 600 ; N Agrave ; B 3 0 597 805 ;
+C -1 ; WX 600 ; N Abreve ; B 3 0 597 732 ;
+C -1 ; WX 600 ; N multiply ; B 87 43 515 470 ;
+C -1 ; WX 600 ; N uacute ; B 21 -15 562 672 ;
+C -1 ; WX 600 ; N Tcaron ; B 38 0 563 802 ;
+C -1 ; WX 600 ; N partialdiff ; B 17 -38 459 710 ;
+C -1 ; WX 600 ; N ydieresis ; B 7 -157 592 620 ;
+C -1 ; WX 600 ; N Nacute ; B 7 -13 593 805 ;
+C -1 ; WX 600 ; N icircumflex ; B 94 0 505 654 ;
+C -1 ; WX 600 ; N Ecircumflex ; B 53 0 550 787 ;
+C -1 ; WX 600 ; N adieresis ; B 53 -15 559 620 ;
+C -1 ; WX 600 ; N edieresis ; B 66 -15 548 620 ;
+C -1 ; WX 600 ; N cacute ; B 66 -15 529 672 ;
+C -1 ; WX 600 ; N nacute ; B 26 0 575 672 ;
+C -1 ; WX 600 ; N umacron ; B 21 -15 562 565 ;
+C -1 ; WX 600 ; N Ncaron ; B 7 -13 593 802 ;
+C -1 ; WX 600 ; N Iacute ; B 96 0 504 805 ;
+C -1 ; WX 600 ; N plusminus ; B 87 44 513 558 ;
+C -1 ; WX 600 ; N brokenbar ; B 275 -175 326 675 ;
+C -1 ; WX 600 ; N registered ; B 0 -18 600 580 ;
+C -1 ; WX 600 ; N Gbreve ; B 31 -18 575 732 ;
+C -1 ; WX 600 ; N Idotaccent ; B 96 0 504 753 ;
+C -1 ; WX 600 ; N summation ; B 15 -10 585 706 ;
+C -1 ; WX 600 ; N Egrave ; B 53 0 550 805 ;
+C -1 ; WX 600 ; N racute ; B 60 0 559 672 ;
+C -1 ; WX 600 ; N omacron ; B 62 -15 538 565 ;
+C -1 ; WX 600 ; N Zacute ; B 86 0 514 805 ;
+C -1 ; WX 600 ; N Zcaron ; B 86 0 514 802 ;
+C -1 ; WX 600 ; N greaterequal ; B 98 0 502 710 ;
+C -1 ; WX 600 ; N Eth ; B 30 0 574 562 ;
+C -1 ; WX 600 ; N Ccedilla ; B 41 -151 540 580 ;
+C -1 ; WX 600 ; N lcommaaccent ; B 95 -250 505 629 ;
+C -1 ; WX 600 ; N tcaron ; B 87 -15 530 717 ;
+C -1 ; WX 600 ; N eogonek ; B 66 -172 548 441 ;
+C -1 ; WX 600 ; N Uogonek ; B 17 -172 583 562 ;
+C -1 ; WX 600 ; N Aacute ; B 3 0 597 805 ;
+C -1 ; WX 600 ; N Adieresis ; B 3 0 597 753 ;
+C -1 ; WX 600 ; N egrave ; B 66 -15 548 672 ;
+C -1 ; WX 600 ; N zacute ; B 99 0 502 672 ;
+C -1 ; WX 600 ; N iogonek ; B 95 -172 505 657 ;
+C -1 ; WX 600 ; N Oacute ; B 43 -18 557 805 ;
+C -1 ; WX 600 ; N oacute ; B 62 -15 538 672 ;
+C -1 ; WX 600 ; N amacron ; B 53 -15 559 565 ;
+C -1 ; WX 600 ; N sacute ; B 80 -15 513 672 ;
+C -1 ; WX 600 ; N idieresis ; B 95 0 505 620 ;
+C -1 ; WX 600 ; N Ocircumflex ; B 43 -18 557 787 ;
+C -1 ; WX 600 ; N Ugrave ; B 17 -18 583 805 ;
+C -1 ; WX 600 ; N Delta ; B 6 0 598 688 ;
+C -1 ; WX 600 ; N thorn ; B -6 -157 555 629 ;
+C -1 ; WX 600 ; N twosuperior ; B 177 249 424 622 ;
+C -1 ; WX 600 ; N Odieresis ; B 43 -18 557 753 ;
+C -1 ; WX 600 ; N mu ; B 21 -157 562 426 ;
+C -1 ; WX 600 ; N igrave ; B 95 0 505 672 ;
+C -1 ; WX 600 ; N ohungarumlaut ; B 62 -15 580 672 ;
+C -1 ; WX 600 ; N Eogonek ; B 53 -172 561 562 ;
+C -1 ; WX 600 ; N dcroat ; B 45 -15 591 629 ;
+C -1 ; WX 600 ; N threequarters ; B 8 -56 593 666 ;
+C -1 ; WX 600 ; N Scedilla ; B 72 -151 529 580 ;
+C -1 ; WX 600 ; N lcaron ; B 95 0 533 629 ;
+C -1 ; WX 600 ; N Kcommaaccent ; B 38 -250 582 562 ;
+C -1 ; WX 600 ; N Lacute ; B 47 0 554 805 ;
+C -1 ; WX 600 ; N trademark ; B -23 263 623 562 ;
+C -1 ; WX 600 ; N edotaccent ; B 66 -15 548 620 ;
+C -1 ; WX 600 ; N Igrave ; B 96 0 504 805 ;
+C -1 ; WX 600 ; N Imacron ; B 96 0 504 698 ;
+C -1 ; WX 600 ; N Lcaron ; B 47 0 554 562 ;
+C -1 ; WX 600 ; N onehalf ; B 0 -57 611 665 ;
+C -1 ; WX 600 ; N lessequal ; B 98 0 502 710 ;
+C -1 ; WX 600 ; N ocircumflex ; B 62 -15 538 654 ;
+C -1 ; WX 600 ; N ntilde ; B 26 0 575 606 ;
+C -1 ; WX 600 ; N Uhungarumlaut ; B 17 -18 590 805 ;
+C -1 ; WX 600 ; N Eacute ; B 53 0 550 805 ;
+C -1 ; WX 600 ; N emacron ; B 66 -15 548 565 ;
+C -1 ; WX 600 ; N gbreve ; B 45 -157 566 609 ;
+C -1 ; WX 600 ; N onequarter ; B 0 -57 600 665 ;
+C -1 ; WX 600 ; N Scaron ; B 72 -20 529 802 ;
+C -1 ; WX 600 ; N Scommaaccent ; B 72 -250 529 580 ;
+C -1 ; WX 600 ; N Ohungarumlaut ; B 43 -18 580 805 ;
+C -1 ; WX 600 ; N degree ; B 123 269 477 622 ;
+C -1 ; WX 600 ; N ograve ; B 62 -15 538 672 ;
+C -1 ; WX 600 ; N Ccaron ; B 41 -18 540 802 ;
+C -1 ; WX 600 ; N ugrave ; B 21 -15 562 672 ;
+C -1 ; WX 600 ; N radical ; B 3 -15 597 792 ;
+C -1 ; WX 600 ; N Dcaron ; B 43 0 574 802 ;
+C -1 ; WX 600 ; N rcommaaccent ; B 60 -250 559 441 ;
+C -1 ; WX 600 ; N Ntilde ; B 7 -13 593 729 ;
+C -1 ; WX 600 ; N otilde ; B 62 -15 538 606 ;
+C -1 ; WX 600 ; N Rcommaaccent ; B 38 -250 588 562 ;
+C -1 ; WX 600 ; N Lcommaaccent ; B 47 -250 554 562 ;
+C -1 ; WX 600 ; N Atilde ; B 3 0 597 729 ;
+C -1 ; WX 600 ; N Aogonek ; B 3 -172 608 562 ;
+C -1 ; WX 600 ; N Aring ; B 3 0 597 750 ;
+C -1 ; WX 600 ; N Otilde ; B 43 -18 557 729 ;
+C -1 ; WX 600 ; N zdotaccent ; B 99 0 502 620 ;
+C -1 ; WX 600 ; N Ecaron ; B 53 0 550 802 ;
+C -1 ; WX 600 ; N Iogonek ; B 96 -172 504 562 ;
+C -1 ; WX 600 ; N kcommaaccent ; B 43 -250 580 629 ;
+C -1 ; WX 600 ; N minus ; B 80 232 520 283 ;
+C -1 ; WX 600 ; N Icircumflex ; B 96 0 504 787 ;
+C -1 ; WX 600 ; N ncaron ; B 26 0 575 669 ;
+C -1 ; WX 600 ; N tcommaaccent ; B 87 -250 530 561 ;
+C -1 ; WX 600 ; N logicalnot ; B 87 108 513 369 ;
+C -1 ; WX 600 ; N odieresis ; B 62 -15 538 620 ;
+C -1 ; WX 600 ; N udieresis ; B 21 -15 562 620 ;
+C -1 ; WX 600 ; N notequal ; B 15 -16 540 529 ;
+C -1 ; WX 600 ; N gcommaaccent ; B 45 -157 566 708 ;
+C -1 ; WX 600 ; N eth ; B 62 -15 538 629 ;
+C -1 ; WX 600 ; N zcaron ; B 99 0 502 669 ;
+C -1 ; WX 600 ; N ncommaaccent ; B 26 -250 575 441 ;
+C -1 ; WX 600 ; N onesuperior ; B 172 249 428 622 ;
+C -1 ; WX 600 ; N imacron ; B 95 0 505 565 ;
+C -1 ; WX 600 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+EndFontMetrics
diff --git a/program/libraries/afm/Helvetica-Bold.afm b/program/libraries/afm/Helvetica-Bold.afm
--- /dev/null
@@ -0,0 +1,2827 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.
+Comment Creation Date: Thu May 1 12:43:52 1997
+Comment UniqueID 43052
+Comment VMusage 37169 48194
+FontName Helvetica-Bold
+FullName Helvetica Bold
+FamilyName Helvetica
+Weight Bold
+ItalicAngle 0
+IsFixedPitch false
+CharacterSet ExtendedRoman
+FontBBox -170 -228 1003 962
+UnderlinePosition -100
+UnderlineThickness 50
+Version 002.000
+Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries.
+EncodingScheme AdobeStandardEncoding
+CapHeight 718
+XHeight 532
+Ascender 718
+Descender -207
+StdHW 118
+StdVW 140
+StartCharMetrics 315
+C 32 ; WX 278 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 333 ; N exclam ; B 90 0 244 718 ;
+C 34 ; WX 474 ; N quotedbl ; B 98 447 376 718 ;
+C 35 ; WX 556 ; N numbersign ; B 18 0 538 698 ;
+C 36 ; WX 556 ; N dollar ; B 30 -115 523 775 ;
+C 37 ; WX 889 ; N percent ; B 28 -19 861 710 ;
+C 38 ; WX 722 ; N ampersand ; B 54 -19 701 718 ;
+C 39 ; WX 278 ; N quoteright ; B 69 445 209 718 ;
+C 40 ; WX 333 ; N parenleft ; B 35 -208 314 734 ;
+C 41 ; WX 333 ; N parenright ; B 19 -208 298 734 ;
+C 42 ; WX 389 ; N asterisk ; B 27 387 362 718 ;
+C 43 ; WX 584 ; N plus ; B 40 0 544 506 ;
+C 44 ; WX 278 ; N comma ; B 64 -168 214 146 ;
+C 45 ; WX 333 ; N hyphen ; B 27 215 306 345 ;
+C 46 ; WX 278 ; N period ; B 64 0 214 146 ;
+C 47 ; WX 278 ; N slash ; B -33 -19 311 737 ;
+C 48 ; WX 556 ; N zero ; B 32 -19 524 710 ;
+C 49 ; WX 556 ; N one ; B 69 0 378 710 ;
+C 50 ; WX 556 ; N two ; B 26 0 511 710 ;
+C 51 ; WX 556 ; N three ; B 27 -19 516 710 ;
+C 52 ; WX 556 ; N four ; B 27 0 526 710 ;
+C 53 ; WX 556 ; N five ; B 27 -19 516 698 ;
+C 54 ; WX 556 ; N six ; B 31 -19 520 710 ;
+C 55 ; WX 556 ; N seven ; B 25 0 528 698 ;
+C 56 ; WX 556 ; N eight ; B 32 -19 524 710 ;
+C 57 ; WX 556 ; N nine ; B 30 -19 522 710 ;
+C 58 ; WX 333 ; N colon ; B 92 0 242 512 ;
+C 59 ; WX 333 ; N semicolon ; B 92 -168 242 512 ;
+C 60 ; WX 584 ; N less ; B 38 -8 546 514 ;
+C 61 ; WX 584 ; N equal ; B 40 87 544 419 ;
+C 62 ; WX 584 ; N greater ; B 38 -8 546 514 ;
+C 63 ; WX 611 ; N question ; B 60 0 556 727 ;
+C 64 ; WX 975 ; N at ; B 118 -19 856 737 ;
+C 65 ; WX 722 ; N A ; B 20 0 702 718 ;
+C 66 ; WX 722 ; N B ; B 76 0 669 718 ;
+C 67 ; WX 722 ; N C ; B 44 -19 684 737 ;
+C 68 ; WX 722 ; N D ; B 76 0 685 718 ;
+C 69 ; WX 667 ; N E ; B 76 0 621 718 ;
+C 70 ; WX 611 ; N F ; B 76 0 587 718 ;
+C 71 ; WX 778 ; N G ; B 44 -19 713 737 ;
+C 72 ; WX 722 ; N H ; B 71 0 651 718 ;
+C 73 ; WX 278 ; N I ; B 64 0 214 718 ;
+C 74 ; WX 556 ; N J ; B 22 -18 484 718 ;
+C 75 ; WX 722 ; N K ; B 87 0 722 718 ;
+C 76 ; WX 611 ; N L ; B 76 0 583 718 ;
+C 77 ; WX 833 ; N M ; B 69 0 765 718 ;
+C 78 ; WX 722 ; N N ; B 69 0 654 718 ;
+C 79 ; WX 778 ; N O ; B 44 -19 734 737 ;
+C 80 ; WX 667 ; N P ; B 76 0 627 718 ;
+C 81 ; WX 778 ; N Q ; B 44 -52 737 737 ;
+C 82 ; WX 722 ; N R ; B 76 0 677 718 ;
+C 83 ; WX 667 ; N S ; B 39 -19 629 737 ;
+C 84 ; WX 611 ; N T ; B 14 0 598 718 ;
+C 85 ; WX 722 ; N U ; B 72 -19 651 718 ;
+C 86 ; WX 667 ; N V ; B 19 0 648 718 ;
+C 87 ; WX 944 ; N W ; B 16 0 929 718 ;
+C 88 ; WX 667 ; N X ; B 14 0 653 718 ;
+C 89 ; WX 667 ; N Y ; B 15 0 653 718 ;
+C 90 ; WX 611 ; N Z ; B 25 0 586 718 ;
+C 91 ; WX 333 ; N bracketleft ; B 63 -196 309 722 ;
+C 92 ; WX 278 ; N backslash ; B -33 -19 311 737 ;
+C 93 ; WX 333 ; N bracketright ; B 24 -196 270 722 ;
+C 94 ; WX 584 ; N asciicircum ; B 62 323 522 698 ;
+C 95 ; WX 556 ; N underscore ; B 0 -125 556 -75 ;
+C 96 ; WX 278 ; N quoteleft ; B 69 454 209 727 ;
+C 97 ; WX 556 ; N a ; B 29 -14 527 546 ;
+C 98 ; WX 611 ; N b ; B 61 -14 578 718 ;
+C 99 ; WX 556 ; N c ; B 34 -14 524 546 ;
+C 100 ; WX 611 ; N d ; B 34 -14 551 718 ;
+C 101 ; WX 556 ; N e ; B 23 -14 528 546 ;
+C 102 ; WX 333 ; N f ; B 10 0 318 727 ; L i fi ; L l fl ;
+C 103 ; WX 611 ; N g ; B 40 -217 553 546 ;
+C 104 ; WX 611 ; N h ; B 65 0 546 718 ;
+C 105 ; WX 278 ; N i ; B 69 0 209 725 ;
+C 106 ; WX 278 ; N j ; B 3 -214 209 725 ;
+C 107 ; WX 556 ; N k ; B 69 0 562 718 ;
+C 108 ; WX 278 ; N l ; B 69 0 209 718 ;
+C 109 ; WX 889 ; N m ; B 64 0 826 546 ;
+C 110 ; WX 611 ; N n ; B 65 0 546 546 ;
+C 111 ; WX 611 ; N o ; B 34 -14 578 546 ;
+C 112 ; WX 611 ; N p ; B 62 -207 578 546 ;
+C 113 ; WX 611 ; N q ; B 34 -207 552 546 ;
+C 114 ; WX 389 ; N r ; B 64 0 373 546 ;
+C 115 ; WX 556 ; N s ; B 30 -14 519 546 ;
+C 116 ; WX 333 ; N t ; B 10 -6 309 676 ;
+C 117 ; WX 611 ; N u ; B 66 -14 545 532 ;
+C 118 ; WX 556 ; N v ; B 13 0 543 532 ;
+C 119 ; WX 778 ; N w ; B 10 0 769 532 ;
+C 120 ; WX 556 ; N x ; B 15 0 541 532 ;
+C 121 ; WX 556 ; N y ; B 10 -214 539 532 ;
+C 122 ; WX 500 ; N z ; B 20 0 480 532 ;
+C 123 ; WX 389 ; N braceleft ; B 48 -196 365 722 ;
+C 124 ; WX 280 ; N bar ; B 84 -225 196 775 ;
+C 125 ; WX 389 ; N braceright ; B 24 -196 341 722 ;
+C 126 ; WX 584 ; N asciitilde ; B 61 163 523 343 ;
+C 161 ; WX 333 ; N exclamdown ; B 90 -186 244 532 ;
+C 162 ; WX 556 ; N cent ; B 34 -118 524 628 ;
+C 163 ; WX 556 ; N sterling ; B 28 -16 541 718 ;
+C 164 ; WX 167 ; N fraction ; B -170 -19 336 710 ;
+C 165 ; WX 556 ; N yen ; B -9 0 565 698 ;
+C 166 ; WX 556 ; N florin ; B -10 -210 516 737 ;
+C 167 ; WX 556 ; N section ; B 34 -184 522 727 ;
+C 168 ; WX 556 ; N currency ; B -3 76 559 636 ;
+C 169 ; WX 238 ; N quotesingle ; B 70 447 168 718 ;
+C 170 ; WX 500 ; N quotedblleft ; B 64 454 436 727 ;
+C 171 ; WX 556 ; N guillemotleft ; B 88 76 468 484 ;
+C 172 ; WX 333 ; N guilsinglleft ; B 83 76 250 484 ;
+C 173 ; WX 333 ; N guilsinglright ; B 83 76 250 484 ;
+C 174 ; WX 611 ; N fi ; B 10 0 542 727 ;
+C 175 ; WX 611 ; N fl ; B 10 0 542 727 ;
+C 177 ; WX 556 ; N endash ; B 0 227 556 333 ;
+C 178 ; WX 556 ; N dagger ; B 36 -171 520 718 ;
+C 179 ; WX 556 ; N daggerdbl ; B 36 -171 520 718 ;
+C 180 ; WX 278 ; N periodcentered ; B 58 172 220 334 ;
+C 182 ; WX 556 ; N paragraph ; B -8 -191 539 700 ;
+C 183 ; WX 350 ; N bullet ; B 10 194 340 524 ;
+C 184 ; WX 278 ; N quotesinglbase ; B 69 -146 209 127 ;
+C 185 ; WX 500 ; N quotedblbase ; B 64 -146 436 127 ;
+C 186 ; WX 500 ; N quotedblright ; B 64 445 436 718 ;
+C 187 ; WX 556 ; N guillemotright ; B 88 76 468 484 ;
+C 188 ; WX 1000 ; N ellipsis ; B 92 0 908 146 ;
+C 189 ; WX 1000 ; N perthousand ; B -3 -19 1003 710 ;
+C 191 ; WX 611 ; N questiondown ; B 55 -195 551 532 ;
+C 193 ; WX 333 ; N grave ; B -23 604 225 750 ;
+C 194 ; WX 333 ; N acute ; B 108 604 356 750 ;
+C 195 ; WX 333 ; N circumflex ; B -10 604 343 750 ;
+C 196 ; WX 333 ; N tilde ; B -17 610 350 737 ;
+C 197 ; WX 333 ; N macron ; B -6 604 339 678 ;
+C 198 ; WX 333 ; N breve ; B -2 604 335 750 ;
+C 199 ; WX 333 ; N dotaccent ; B 104 614 230 729 ;
+C 200 ; WX 333 ; N dieresis ; B 6 614 327 729 ;
+C 202 ; WX 333 ; N ring ; B 59 568 275 776 ;
+C 203 ; WX 333 ; N cedilla ; B 6 -228 245 0 ;
+C 205 ; WX 333 ; N hungarumlaut ; B 9 604 486 750 ;
+C 206 ; WX 333 ; N ogonek ; B 71 -228 304 0 ;
+C 207 ; WX 333 ; N caron ; B -10 604 343 750 ;
+C 208 ; WX 1000 ; N emdash ; B 0 227 1000 333 ;
+C 225 ; WX 1000 ; N AE ; B 5 0 954 718 ;
+C 227 ; WX 370 ; N ordfeminine ; B 22 401 347 737 ;
+C 232 ; WX 611 ; N Lslash ; B -20 0 583 718 ;
+C 233 ; WX 778 ; N Oslash ; B 33 -27 744 745 ;
+C 234 ; WX 1000 ; N OE ; B 37 -19 961 737 ;
+C 235 ; WX 365 ; N ordmasculine ; B 6 401 360 737 ;
+C 241 ; WX 889 ; N ae ; B 29 -14 858 546 ;
+C 245 ; WX 278 ; N dotlessi ; B 69 0 209 532 ;
+C 248 ; WX 278 ; N lslash ; B -18 0 296 718 ;
+C 249 ; WX 611 ; N oslash ; B 22 -29 589 560 ;
+C 250 ; WX 944 ; N oe ; B 34 -14 912 546 ;
+C 251 ; WX 611 ; N germandbls ; B 69 -14 579 731 ;
+C -1 ; WX 278 ; N Idieresis ; B -21 0 300 915 ;
+C -1 ; WX 556 ; N eacute ; B 23 -14 528 750 ;
+C -1 ; WX 556 ; N abreve ; B 29 -14 527 750 ;
+C -1 ; WX 611 ; N uhungarumlaut ; B 66 -14 625 750 ;
+C -1 ; WX 556 ; N ecaron ; B 23 -14 528 750 ;
+C -1 ; WX 667 ; N Ydieresis ; B 15 0 653 915 ;
+C -1 ; WX 584 ; N divide ; B 40 -42 544 548 ;
+C -1 ; WX 667 ; N Yacute ; B 15 0 653 936 ;
+C -1 ; WX 722 ; N Acircumflex ; B 20 0 702 936 ;
+C -1 ; WX 556 ; N aacute ; B 29 -14 527 750 ;
+C -1 ; WX 722 ; N Ucircumflex ; B 72 -19 651 936 ;
+C -1 ; WX 556 ; N yacute ; B 10 -214 539 750 ;
+C -1 ; WX 556 ; N scommaaccent ; B 30 -228 519 546 ;
+C -1 ; WX 556 ; N ecircumflex ; B 23 -14 528 750 ;
+C -1 ; WX 722 ; N Uring ; B 72 -19 651 962 ;
+C -1 ; WX 722 ; N Udieresis ; B 72 -19 651 915 ;
+C -1 ; WX 556 ; N aogonek ; B 29 -224 545 546 ;
+C -1 ; WX 722 ; N Uacute ; B 72 -19 651 936 ;
+C -1 ; WX 611 ; N uogonek ; B 66 -228 545 532 ;
+C -1 ; WX 667 ; N Edieresis ; B 76 0 621 915 ;
+C -1 ; WX 722 ; N Dcroat ; B -5 0 685 718 ;
+C -1 ; WX 250 ; N commaaccent ; B 64 -228 199 -50 ;
+C -1 ; WX 737 ; N copyright ; B -11 -19 749 737 ;
+C -1 ; WX 667 ; N Emacron ; B 76 0 621 864 ;
+C -1 ; WX 556 ; N ccaron ; B 34 -14 524 750 ;
+C -1 ; WX 556 ; N aring ; B 29 -14 527 776 ;
+C -1 ; WX 722 ; N Ncommaaccent ; B 69 -228 654 718 ;
+C -1 ; WX 278 ; N lacute ; B 69 0 329 936 ;
+C -1 ; WX 556 ; N agrave ; B 29 -14 527 750 ;
+C -1 ; WX 611 ; N Tcommaaccent ; B 14 -228 598 718 ;
+C -1 ; WX 722 ; N Cacute ; B 44 -19 684 936 ;
+C -1 ; WX 556 ; N atilde ; B 29 -14 527 737 ;
+C -1 ; WX 667 ; N Edotaccent ; B 76 0 621 915 ;
+C -1 ; WX 556 ; N scaron ; B 30 -14 519 750 ;
+C -1 ; WX 556 ; N scedilla ; B 30 -228 519 546 ;
+C -1 ; WX 278 ; N iacute ; B 69 0 329 750 ;
+C -1 ; WX 494 ; N lozenge ; B 10 0 484 745 ;
+C -1 ; WX 722 ; N Rcaron ; B 76 0 677 936 ;
+C -1 ; WX 778 ; N Gcommaaccent ; B 44 -228 713 737 ;
+C -1 ; WX 611 ; N ucircumflex ; B 66 -14 545 750 ;
+C -1 ; WX 556 ; N acircumflex ; B 29 -14 527 750 ;
+C -1 ; WX 722 ; N Amacron ; B 20 0 702 864 ;
+C -1 ; WX 389 ; N rcaron ; B 18 0 373 750 ;
+C -1 ; WX 556 ; N ccedilla ; B 34 -228 524 546 ;
+C -1 ; WX 611 ; N Zdotaccent ; B 25 0 586 915 ;
+C -1 ; WX 667 ; N Thorn ; B 76 0 627 718 ;
+C -1 ; WX 778 ; N Omacron ; B 44 -19 734 864 ;
+C -1 ; WX 722 ; N Racute ; B 76 0 677 936 ;
+C -1 ; WX 667 ; N Sacute ; B 39 -19 629 936 ;
+C -1 ; WX 743 ; N dcaron ; B 34 -14 750 718 ;
+C -1 ; WX 722 ; N Umacron ; B 72 -19 651 864 ;
+C -1 ; WX 611 ; N uring ; B 66 -14 545 776 ;
+C -1 ; WX 333 ; N threesuperior ; B 8 271 326 710 ;
+C -1 ; WX 778 ; N Ograve ; B 44 -19 734 936 ;
+C -1 ; WX 722 ; N Agrave ; B 20 0 702 936 ;
+C -1 ; WX 722 ; N Abreve ; B 20 0 702 936 ;
+C -1 ; WX 584 ; N multiply ; B 40 1 545 505 ;
+C -1 ; WX 611 ; N uacute ; B 66 -14 545 750 ;
+C -1 ; WX 611 ; N Tcaron ; B 14 0 598 936 ;
+C -1 ; WX 494 ; N partialdiff ; B 11 -21 494 750 ;
+C -1 ; WX 556 ; N ydieresis ; B 10 -214 539 729 ;
+C -1 ; WX 722 ; N Nacute ; B 69 0 654 936 ;
+C -1 ; WX 278 ; N icircumflex ; B -37 0 316 750 ;
+C -1 ; WX 667 ; N Ecircumflex ; B 76 0 621 936 ;
+C -1 ; WX 556 ; N adieresis ; B 29 -14 527 729 ;
+C -1 ; WX 556 ; N edieresis ; B 23 -14 528 729 ;
+C -1 ; WX 556 ; N cacute ; B 34 -14 524 750 ;
+C -1 ; WX 611 ; N nacute ; B 65 0 546 750 ;
+C -1 ; WX 611 ; N umacron ; B 66 -14 545 678 ;
+C -1 ; WX 722 ; N Ncaron ; B 69 0 654 936 ;
+C -1 ; WX 278 ; N Iacute ; B 64 0 329 936 ;
+C -1 ; WX 584 ; N plusminus ; B 40 0 544 506 ;
+C -1 ; WX 280 ; N brokenbar ; B 84 -150 196 700 ;
+C -1 ; WX 737 ; N registered ; B -11 -19 748 737 ;
+C -1 ; WX 778 ; N Gbreve ; B 44 -19 713 936 ;
+C -1 ; WX 278 ; N Idotaccent ; B 64 0 214 915 ;
+C -1 ; WX 600 ; N summation ; B 14 -10 585 706 ;
+C -1 ; WX 667 ; N Egrave ; B 76 0 621 936 ;
+C -1 ; WX 389 ; N racute ; B 64 0 384 750 ;
+C -1 ; WX 611 ; N omacron ; B 34 -14 578 678 ;
+C -1 ; WX 611 ; N Zacute ; B 25 0 586 936 ;
+C -1 ; WX 611 ; N Zcaron ; B 25 0 586 936 ;
+C -1 ; WX 549 ; N greaterequal ; B 26 0 523 704 ;
+C -1 ; WX 722 ; N Eth ; B -5 0 685 718 ;
+C -1 ; WX 722 ; N Ccedilla ; B 44 -228 684 737 ;
+C -1 ; WX 278 ; N lcommaaccent ; B 69 -228 213 718 ;
+C -1 ; WX 389 ; N tcaron ; B 10 -6 421 878 ;
+C -1 ; WX 556 ; N eogonek ; B 23 -228 528 546 ;
+C -1 ; WX 722 ; N Uogonek ; B 72 -228 651 718 ;
+C -1 ; WX 722 ; N Aacute ; B 20 0 702 936 ;
+C -1 ; WX 722 ; N Adieresis ; B 20 0 702 915 ;
+C -1 ; WX 556 ; N egrave ; B 23 -14 528 750 ;
+C -1 ; WX 500 ; N zacute ; B 20 0 480 750 ;
+C -1 ; WX 278 ; N iogonek ; B 16 -224 249 725 ;
+C -1 ; WX 778 ; N Oacute ; B 44 -19 734 936 ;
+C -1 ; WX 611 ; N oacute ; B 34 -14 578 750 ;
+C -1 ; WX 556 ; N amacron ; B 29 -14 527 678 ;
+C -1 ; WX 556 ; N sacute ; B 30 -14 519 750 ;
+C -1 ; WX 278 ; N idieresis ; B -21 0 300 729 ;
+C -1 ; WX 778 ; N Ocircumflex ; B 44 -19 734 936 ;
+C -1 ; WX 722 ; N Ugrave ; B 72 -19 651 936 ;
+C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ;
+C -1 ; WX 611 ; N thorn ; B 62 -208 578 718 ;
+C -1 ; WX 333 ; N twosuperior ; B 9 283 324 710 ;
+C -1 ; WX 778 ; N Odieresis ; B 44 -19 734 915 ;
+C -1 ; WX 611 ; N mu ; B 66 -207 545 532 ;
+C -1 ; WX 278 ; N igrave ; B -50 0 209 750 ;
+C -1 ; WX 611 ; N ohungarumlaut ; B 34 -14 625 750 ;
+C -1 ; WX 667 ; N Eogonek ; B 76 -224 639 718 ;
+C -1 ; WX 611 ; N dcroat ; B 34 -14 650 718 ;
+C -1 ; WX 834 ; N threequarters ; B 16 -19 799 710 ;
+C -1 ; WX 667 ; N Scedilla ; B 39 -228 629 737 ;
+C -1 ; WX 400 ; N lcaron ; B 69 0 408 718 ;
+C -1 ; WX 722 ; N Kcommaaccent ; B 87 -228 722 718 ;
+C -1 ; WX 611 ; N Lacute ; B 76 0 583 936 ;
+C -1 ; WX 1000 ; N trademark ; B 44 306 956 718 ;
+C -1 ; WX 556 ; N edotaccent ; B 23 -14 528 729 ;
+C -1 ; WX 278 ; N Igrave ; B -50 0 214 936 ;
+C -1 ; WX 278 ; N Imacron ; B -33 0 312 864 ;
+C -1 ; WX 611 ; N Lcaron ; B 76 0 583 718 ;
+C -1 ; WX 834 ; N onehalf ; B 26 -19 794 710 ;
+C -1 ; WX 549 ; N lessequal ; B 29 0 526 704 ;
+C -1 ; WX 611 ; N ocircumflex ; B 34 -14 578 750 ;
+C -1 ; WX 611 ; N ntilde ; B 65 0 546 737 ;
+C -1 ; WX 722 ; N Uhungarumlaut ; B 72 -19 681 936 ;
+C -1 ; WX 667 ; N Eacute ; B 76 0 621 936 ;
+C -1 ; WX 556 ; N emacron ; B 23 -14 528 678 ;
+C -1 ; WX 611 ; N gbreve ; B 40 -217 553 750 ;
+C -1 ; WX 834 ; N onequarter ; B 26 -19 766 710 ;
+C -1 ; WX 667 ; N Scaron ; B 39 -19 629 936 ;
+C -1 ; WX 667 ; N Scommaaccent ; B 39 -228 629 737 ;
+C -1 ; WX 778 ; N Ohungarumlaut ; B 44 -19 734 936 ;
+C -1 ; WX 400 ; N degree ; B 57 426 343 712 ;
+C -1 ; WX 611 ; N ograve ; B 34 -14 578 750 ;
+C -1 ; WX 722 ; N Ccaron ; B 44 -19 684 936 ;
+C -1 ; WX 611 ; N ugrave ; B 66 -14 545 750 ;
+C -1 ; WX 549 ; N radical ; B 10 -46 512 850 ;
+C -1 ; WX 722 ; N Dcaron ; B 76 0 685 936 ;
+C -1 ; WX 389 ; N rcommaaccent ; B 64 -228 373 546 ;
+C -1 ; WX 722 ; N Ntilde ; B 69 0 654 923 ;
+C -1 ; WX 611 ; N otilde ; B 34 -14 578 737 ;
+C -1 ; WX 722 ; N Rcommaaccent ; B 76 -228 677 718 ;
+C -1 ; WX 611 ; N Lcommaaccent ; B 76 -228 583 718 ;
+C -1 ; WX 722 ; N Atilde ; B 20 0 702 923 ;
+C -1 ; WX 722 ; N Aogonek ; B 20 -224 742 718 ;
+C -1 ; WX 722 ; N Aring ; B 20 0 702 962 ;
+C -1 ; WX 778 ; N Otilde ; B 44 -19 734 923 ;
+C -1 ; WX 500 ; N zdotaccent ; B 20 0 480 729 ;
+C -1 ; WX 667 ; N Ecaron ; B 76 0 621 936 ;
+C -1 ; WX 278 ; N Iogonek ; B -11 -228 222 718 ;
+C -1 ; WX 556 ; N kcommaaccent ; B 69 -228 562 718 ;
+C -1 ; WX 584 ; N minus ; B 40 197 544 309 ;
+C -1 ; WX 278 ; N Icircumflex ; B -37 0 316 936 ;
+C -1 ; WX 611 ; N ncaron ; B 65 0 546 750 ;
+C -1 ; WX 333 ; N tcommaaccent ; B 10 -228 309 676 ;
+C -1 ; WX 584 ; N logicalnot ; B 40 108 544 419 ;
+C -1 ; WX 611 ; N odieresis ; B 34 -14 578 729 ;
+C -1 ; WX 611 ; N udieresis ; B 66 -14 545 729 ;
+C -1 ; WX 549 ; N notequal ; B 15 -49 540 570 ;
+C -1 ; WX 611 ; N gcommaaccent ; B 40 -217 553 850 ;
+C -1 ; WX 611 ; N eth ; B 34 -14 578 737 ;
+C -1 ; WX 500 ; N zcaron ; B 20 0 480 750 ;
+C -1 ; WX 611 ; N ncommaaccent ; B 65 -228 546 546 ;
+C -1 ; WX 333 ; N onesuperior ; B 26 283 237 710 ;
+C -1 ; WX 278 ; N imacron ; B -8 0 285 678 ;
+C -1 ; WX 556 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+StartKernData
+StartKernPairs 2481
+KPX A C -40
+KPX A Cacute -40
+KPX A Ccaron -40
+KPX A Ccedilla -40
+KPX A G -50
+KPX A Gbreve -50
+KPX A Gcommaaccent -50
+KPX A O -40
+KPX A Oacute -40
+KPX A Ocircumflex -40
+KPX A Odieresis -40
+KPX A Ograve -40
+KPX A Ohungarumlaut -40
+KPX A Omacron -40
+KPX A Oslash -40
+KPX A Otilde -40
+KPX A Q -40
+KPX A T -90
+KPX A Tcaron -90
+KPX A Tcommaaccent -90
+KPX A U -50
+KPX A Uacute -50
+KPX A Ucircumflex -50
+KPX A Udieresis -50
+KPX A Ugrave -50
+KPX A Uhungarumlaut -50
+KPX A Umacron -50
+KPX A Uogonek -50
+KPX A Uring -50
+KPX A V -80
+KPX A W -60
+KPX A Y -110
+KPX A Yacute -110
+KPX A Ydieresis -110
+KPX A u -30
+KPX A uacute -30
+KPX A ucircumflex -30
+KPX A udieresis -30
+KPX A ugrave -30
+KPX A uhungarumlaut -30
+KPX A umacron -30
+KPX A uogonek -30
+KPX A uring -30
+KPX A v -40
+KPX A w -30
+KPX A y -30
+KPX A yacute -30
+KPX A ydieresis -30
+KPX Aacute C -40
+KPX Aacute Cacute -40
+KPX Aacute Ccaron -40
+KPX Aacute Ccedilla -40
+KPX Aacute G -50
+KPX Aacute Gbreve -50
+KPX Aacute Gcommaaccent -50
+KPX Aacute O -40
+KPX Aacute Oacute -40
+KPX Aacute Ocircumflex -40
+KPX Aacute Odieresis -40
+KPX Aacute Ograve -40
+KPX Aacute Ohungarumlaut -40
+KPX Aacute Omacron -40
+KPX Aacute Oslash -40
+KPX Aacute Otilde -40
+KPX Aacute Q -40
+KPX Aacute T -90
+KPX Aacute Tcaron -90
+KPX Aacute Tcommaaccent -90
+KPX Aacute U -50
+KPX Aacute Uacute -50
+KPX Aacute Ucircumflex -50
+KPX Aacute Udieresis -50
+KPX Aacute Ugrave -50
+KPX Aacute Uhungarumlaut -50
+KPX Aacute Umacron -50
+KPX Aacute Uogonek -50
+KPX Aacute Uring -50
+KPX Aacute V -80
+KPX Aacute W -60
+KPX Aacute Y -110
+KPX Aacute Yacute -110
+KPX Aacute Ydieresis -110
+KPX Aacute u -30
+KPX Aacute uacute -30
+KPX Aacute ucircumflex -30
+KPX Aacute udieresis -30
+KPX Aacute ugrave -30
+KPX Aacute uhungarumlaut -30
+KPX Aacute umacron -30
+KPX Aacute uogonek -30
+KPX Aacute uring -30
+KPX Aacute v -40
+KPX Aacute w -30
+KPX Aacute y -30
+KPX Aacute yacute -30
+KPX Aacute ydieresis -30
+KPX Abreve C -40
+KPX Abreve Cacute -40
+KPX Abreve Ccaron -40
+KPX Abreve Ccedilla -40
+KPX Abreve G -50
+KPX Abreve Gbreve -50
+KPX Abreve Gcommaaccent -50
+KPX Abreve O -40
+KPX Abreve Oacute -40
+KPX Abreve Ocircumflex -40
+KPX Abreve Odieresis -40
+KPX Abreve Ograve -40
+KPX Abreve Ohungarumlaut -40
+KPX Abreve Omacron -40
+KPX Abreve Oslash -40
+KPX Abreve Otilde -40
+KPX Abreve Q -40
+KPX Abreve T -90
+KPX Abreve Tcaron -90
+KPX Abreve Tcommaaccent -90
+KPX Abreve U -50
+KPX Abreve Uacute -50
+KPX Abreve Ucircumflex -50
+KPX Abreve Udieresis -50
+KPX Abreve Ugrave -50
+KPX Abreve Uhungarumlaut -50
+KPX Abreve Umacron -50
+KPX Abreve Uogonek -50
+KPX Abreve Uring -50
+KPX Abreve V -80
+KPX Abreve W -60
+KPX Abreve Y -110
+KPX Abreve Yacute -110
+KPX Abreve Ydieresis -110
+KPX Abreve u -30
+KPX Abreve uacute -30
+KPX Abreve ucircumflex -30
+KPX Abreve udieresis -30
+KPX Abreve ugrave -30
+KPX Abreve uhungarumlaut -30
+KPX Abreve umacron -30
+KPX Abreve uogonek -30
+KPX Abreve uring -30
+KPX Abreve v -40
+KPX Abreve w -30
+KPX Abreve y -30
+KPX Abreve yacute -30
+KPX Abreve ydieresis -30
+KPX Acircumflex C -40
+KPX Acircumflex Cacute -40
+KPX Acircumflex Ccaron -40
+KPX Acircumflex Ccedilla -40
+KPX Acircumflex G -50
+KPX Acircumflex Gbreve -50
+KPX Acircumflex Gcommaaccent -50
+KPX Acircumflex O -40
+KPX Acircumflex Oacute -40
+KPX Acircumflex Ocircumflex -40
+KPX Acircumflex Odieresis -40
+KPX Acircumflex Ograve -40
+KPX Acircumflex Ohungarumlaut -40
+KPX Acircumflex Omacron -40
+KPX Acircumflex Oslash -40
+KPX Acircumflex Otilde -40
+KPX Acircumflex Q -40
+KPX Acircumflex T -90
+KPX Acircumflex Tcaron -90
+KPX Acircumflex Tcommaaccent -90
+KPX Acircumflex U -50
+KPX Acircumflex Uacute -50
+KPX Acircumflex Ucircumflex -50
+KPX Acircumflex Udieresis -50
+KPX Acircumflex Ugrave -50
+KPX Acircumflex Uhungarumlaut -50
+KPX Acircumflex Umacron -50
+KPX Acircumflex Uogonek -50
+KPX Acircumflex Uring -50
+KPX Acircumflex V -80
+KPX Acircumflex W -60
+KPX Acircumflex Y -110
+KPX Acircumflex Yacute -110
+KPX Acircumflex Ydieresis -110
+KPX Acircumflex u -30
+KPX Acircumflex uacute -30
+KPX Acircumflex ucircumflex -30
+KPX Acircumflex udieresis -30
+KPX Acircumflex ugrave -30
+KPX Acircumflex uhungarumlaut -30
+KPX Acircumflex umacron -30
+KPX Acircumflex uogonek -30
+KPX Acircumflex uring -30
+KPX Acircumflex v -40
+KPX Acircumflex w -30
+KPX Acircumflex y -30
+KPX Acircumflex yacute -30
+KPX Acircumflex ydieresis -30
+KPX Adieresis C -40
+KPX Adieresis Cacute -40
+KPX Adieresis Ccaron -40
+KPX Adieresis Ccedilla -40
+KPX Adieresis G -50
+KPX Adieresis Gbreve -50
+KPX Adieresis Gcommaaccent -50
+KPX Adieresis O -40
+KPX Adieresis Oacute -40
+KPX Adieresis Ocircumflex -40
+KPX Adieresis Odieresis -40
+KPX Adieresis Ograve -40
+KPX Adieresis Ohungarumlaut -40
+KPX Adieresis Omacron -40
+KPX Adieresis Oslash -40
+KPX Adieresis Otilde -40
+KPX Adieresis Q -40
+KPX Adieresis T -90
+KPX Adieresis Tcaron -90
+KPX Adieresis Tcommaaccent -90
+KPX Adieresis U -50
+KPX Adieresis Uacute -50
+KPX Adieresis Ucircumflex -50
+KPX Adieresis Udieresis -50
+KPX Adieresis Ugrave -50
+KPX Adieresis Uhungarumlaut -50
+KPX Adieresis Umacron -50
+KPX Adieresis Uogonek -50
+KPX Adieresis Uring -50
+KPX Adieresis V -80
+KPX Adieresis W -60
+KPX Adieresis Y -110
+KPX Adieresis Yacute -110
+KPX Adieresis Ydieresis -110
+KPX Adieresis u -30
+KPX Adieresis uacute -30
+KPX Adieresis ucircumflex -30
+KPX Adieresis udieresis -30
+KPX Adieresis ugrave -30
+KPX Adieresis uhungarumlaut -30
+KPX Adieresis umacron -30
+KPX Adieresis uogonek -30
+KPX Adieresis uring -30
+KPX Adieresis v -40
+KPX Adieresis w -30
+KPX Adieresis y -30
+KPX Adieresis yacute -30
+KPX Adieresis ydieresis -30
+KPX Agrave C -40
+KPX Agrave Cacute -40
+KPX Agrave Ccaron -40
+KPX Agrave Ccedilla -40
+KPX Agrave G -50
+KPX Agrave Gbreve -50
+KPX Agrave Gcommaaccent -50
+KPX Agrave O -40
+KPX Agrave Oacute -40
+KPX Agrave Ocircumflex -40
+KPX Agrave Odieresis -40
+KPX Agrave Ograve -40
+KPX Agrave Ohungarumlaut -40
+KPX Agrave Omacron -40
+KPX Agrave Oslash -40
+KPX Agrave Otilde -40
+KPX Agrave Q -40
+KPX Agrave T -90
+KPX Agrave Tcaron -90
+KPX Agrave Tcommaaccent -90
+KPX Agrave U -50
+KPX Agrave Uacute -50
+KPX Agrave Ucircumflex -50
+KPX Agrave Udieresis -50
+KPX Agrave Ugrave -50
+KPX Agrave Uhungarumlaut -50
+KPX Agrave Umacron -50
+KPX Agrave Uogonek -50
+KPX Agrave Uring -50
+KPX Agrave V -80
+KPX Agrave W -60
+KPX Agrave Y -110
+KPX Agrave Yacute -110
+KPX Agrave Ydieresis -110
+KPX Agrave u -30
+KPX Agrave uacute -30
+KPX Agrave ucircumflex -30
+KPX Agrave udieresis -30
+KPX Agrave ugrave -30
+KPX Agrave uhungarumlaut -30
+KPX Agrave umacron -30
+KPX Agrave uogonek -30
+KPX Agrave uring -30
+KPX Agrave v -40
+KPX Agrave w -30
+KPX Agrave y -30
+KPX Agrave yacute -30
+KPX Agrave ydieresis -30
+KPX Amacron C -40
+KPX Amacron Cacute -40
+KPX Amacron Ccaron -40
+KPX Amacron Ccedilla -40
+KPX Amacron G -50
+KPX Amacron Gbreve -50
+KPX Amacron Gcommaaccent -50
+KPX Amacron O -40
+KPX Amacron Oacute -40
+KPX Amacron Ocircumflex -40
+KPX Amacron Odieresis -40
+KPX Amacron Ograve -40
+KPX Amacron Ohungarumlaut -40
+KPX Amacron Omacron -40
+KPX Amacron Oslash -40
+KPX Amacron Otilde -40
+KPX Amacron Q -40
+KPX Amacron T -90
+KPX Amacron Tcaron -90
+KPX Amacron Tcommaaccent -90
+KPX Amacron U -50
+KPX Amacron Uacute -50
+KPX Amacron Ucircumflex -50
+KPX Amacron Udieresis -50
+KPX Amacron Ugrave -50
+KPX Amacron Uhungarumlaut -50
+KPX Amacron Umacron -50
+KPX Amacron Uogonek -50
+KPX Amacron Uring -50
+KPX Amacron V -80
+KPX Amacron W -60
+KPX Amacron Y -110
+KPX Amacron Yacute -110
+KPX Amacron Ydieresis -110
+KPX Amacron u -30
+KPX Amacron uacute -30
+KPX Amacron ucircumflex -30
+KPX Amacron udieresis -30
+KPX Amacron ugrave -30
+KPX Amacron uhungarumlaut -30
+KPX Amacron umacron -30
+KPX Amacron uogonek -30
+KPX Amacron uring -30
+KPX Amacron v -40
+KPX Amacron w -30
+KPX Amacron y -30
+KPX Amacron yacute -30
+KPX Amacron ydieresis -30
+KPX Aogonek C -40
+KPX Aogonek Cacute -40
+KPX Aogonek Ccaron -40
+KPX Aogonek Ccedilla -40
+KPX Aogonek G -50
+KPX Aogonek Gbreve -50
+KPX Aogonek Gcommaaccent -50
+KPX Aogonek O -40
+KPX Aogonek Oacute -40
+KPX Aogonek Ocircumflex -40
+KPX Aogonek Odieresis -40
+KPX Aogonek Ograve -40
+KPX Aogonek Ohungarumlaut -40
+KPX Aogonek Omacron -40
+KPX Aogonek Oslash -40
+KPX Aogonek Otilde -40
+KPX Aogonek Q -40
+KPX Aogonek T -90
+KPX Aogonek Tcaron -90
+KPX Aogonek Tcommaaccent -90
+KPX Aogonek U -50
+KPX Aogonek Uacute -50
+KPX Aogonek Ucircumflex -50
+KPX Aogonek Udieresis -50
+KPX Aogonek Ugrave -50
+KPX Aogonek Uhungarumlaut -50
+KPX Aogonek Umacron -50
+KPX Aogonek Uogonek -50
+KPX Aogonek Uring -50
+KPX Aogonek V -80
+KPX Aogonek W -60
+KPX Aogonek Y -110
+KPX Aogonek Yacute -110
+KPX Aogonek Ydieresis -110
+KPX Aogonek u -30
+KPX Aogonek uacute -30
+KPX Aogonek ucircumflex -30
+KPX Aogonek udieresis -30
+KPX Aogonek ugrave -30
+KPX Aogonek uhungarumlaut -30
+KPX Aogonek umacron -30
+KPX Aogonek uogonek -30
+KPX Aogonek uring -30
+KPX Aogonek v -40
+KPX Aogonek w -30
+KPX Aogonek y -30
+KPX Aogonek yacute -30
+KPX Aogonek ydieresis -30
+KPX Aring C -40
+KPX Aring Cacute -40
+KPX Aring Ccaron -40
+KPX Aring Ccedilla -40
+KPX Aring G -50
+KPX Aring Gbreve -50
+KPX Aring Gcommaaccent -50
+KPX Aring O -40
+KPX Aring Oacute -40
+KPX Aring Ocircumflex -40
+KPX Aring Odieresis -40
+KPX Aring Ograve -40
+KPX Aring Ohungarumlaut -40
+KPX Aring Omacron -40
+KPX Aring Oslash -40
+KPX Aring Otilde -40
+KPX Aring Q -40
+KPX Aring T -90
+KPX Aring Tcaron -90
+KPX Aring Tcommaaccent -90
+KPX Aring U -50
+KPX Aring Uacute -50
+KPX Aring Ucircumflex -50
+KPX Aring Udieresis -50
+KPX Aring Ugrave -50
+KPX Aring Uhungarumlaut -50
+KPX Aring Umacron -50
+KPX Aring Uogonek -50
+KPX Aring Uring -50
+KPX Aring V -80
+KPX Aring W -60
+KPX Aring Y -110
+KPX Aring Yacute -110
+KPX Aring Ydieresis -110
+KPX Aring u -30
+KPX Aring uacute -30
+KPX Aring ucircumflex -30
+KPX Aring udieresis -30
+KPX Aring ugrave -30
+KPX Aring uhungarumlaut -30
+KPX Aring umacron -30
+KPX Aring uogonek -30
+KPX Aring uring -30
+KPX Aring v -40
+KPX Aring w -30
+KPX Aring y -30
+KPX Aring yacute -30
+KPX Aring ydieresis -30
+KPX Atilde C -40
+KPX Atilde Cacute -40
+KPX Atilde Ccaron -40
+KPX Atilde Ccedilla -40
+KPX Atilde G -50
+KPX Atilde Gbreve -50
+KPX Atilde Gcommaaccent -50
+KPX Atilde O -40
+KPX Atilde Oacute -40
+KPX Atilde Ocircumflex -40
+KPX Atilde Odieresis -40
+KPX Atilde Ograve -40
+KPX Atilde Ohungarumlaut -40
+KPX Atilde Omacron -40
+KPX Atilde Oslash -40
+KPX Atilde Otilde -40
+KPX Atilde Q -40
+KPX Atilde T -90
+KPX Atilde Tcaron -90
+KPX Atilde Tcommaaccent -90
+KPX Atilde U -50
+KPX Atilde Uacute -50
+KPX Atilde Ucircumflex -50
+KPX Atilde Udieresis -50
+KPX Atilde Ugrave -50
+KPX Atilde Uhungarumlaut -50
+KPX Atilde Umacron -50
+KPX Atilde Uogonek -50
+KPX Atilde Uring -50
+KPX Atilde V -80
+KPX Atilde W -60
+KPX Atilde Y -110
+KPX Atilde Yacute -110
+KPX Atilde Ydieresis -110
+KPX Atilde u -30
+KPX Atilde uacute -30
+KPX Atilde ucircumflex -30
+KPX Atilde udieresis -30
+KPX Atilde ugrave -30
+KPX Atilde uhungarumlaut -30
+KPX Atilde umacron -30
+KPX Atilde uogonek -30
+KPX Atilde uring -30
+KPX Atilde v -40
+KPX Atilde w -30
+KPX Atilde y -30
+KPX Atilde yacute -30
+KPX Atilde ydieresis -30
+KPX B A -30
+KPX B Aacute -30
+KPX B Abreve -30
+KPX B Acircumflex -30
+KPX B Adieresis -30
+KPX B Agrave -30
+KPX B Amacron -30
+KPX B Aogonek -30
+KPX B Aring -30
+KPX B Atilde -30
+KPX B U -10
+KPX B Uacute -10
+KPX B Ucircumflex -10
+KPX B Udieresis -10
+KPX B Ugrave -10
+KPX B Uhungarumlaut -10
+KPX B Umacron -10
+KPX B Uogonek -10
+KPX B Uring -10
+KPX D A -40
+KPX D Aacute -40
+KPX D Abreve -40
+KPX D Acircumflex -40
+KPX D Adieresis -40
+KPX D Agrave -40
+KPX D Amacron -40
+KPX D Aogonek -40
+KPX D Aring -40
+KPX D Atilde -40
+KPX D V -40
+KPX D W -40
+KPX D Y -70
+KPX D Yacute -70
+KPX D Ydieresis -70
+KPX D comma -30
+KPX D period -30
+KPX Dcaron A -40
+KPX Dcaron Aacute -40
+KPX Dcaron Abreve -40
+KPX Dcaron Acircumflex -40
+KPX Dcaron Adieresis -40
+KPX Dcaron Agrave -40
+KPX Dcaron Amacron -40
+KPX Dcaron Aogonek -40
+KPX Dcaron Aring -40
+KPX Dcaron Atilde -40
+KPX Dcaron V -40
+KPX Dcaron W -40
+KPX Dcaron Y -70
+KPX Dcaron Yacute -70
+KPX Dcaron Ydieresis -70
+KPX Dcaron comma -30
+KPX Dcaron period -30
+KPX Dcroat A -40
+KPX Dcroat Aacute -40
+KPX Dcroat Abreve -40
+KPX Dcroat Acircumflex -40
+KPX Dcroat Adieresis -40
+KPX Dcroat Agrave -40
+KPX Dcroat Amacron -40
+KPX Dcroat Aogonek -40
+KPX Dcroat Aring -40
+KPX Dcroat Atilde -40
+KPX Dcroat V -40
+KPX Dcroat W -40
+KPX Dcroat Y -70
+KPX Dcroat Yacute -70
+KPX Dcroat Ydieresis -70
+KPX Dcroat comma -30
+KPX Dcroat period -30
+KPX F A -80
+KPX F Aacute -80
+KPX F Abreve -80
+KPX F Acircumflex -80
+KPX F Adieresis -80
+KPX F Agrave -80
+KPX F Amacron -80
+KPX F Aogonek -80
+KPX F Aring -80
+KPX F Atilde -80
+KPX F a -20
+KPX F aacute -20
+KPX F abreve -20
+KPX F acircumflex -20
+KPX F adieresis -20
+KPX F agrave -20
+KPX F amacron -20
+KPX F aogonek -20
+KPX F aring -20
+KPX F atilde -20
+KPX F comma -100
+KPX F period -100
+KPX J A -20
+KPX J Aacute -20
+KPX J Abreve -20
+KPX J Acircumflex -20
+KPX J Adieresis -20
+KPX J Agrave -20
+KPX J Amacron -20
+KPX J Aogonek -20
+KPX J Aring -20
+KPX J Atilde -20
+KPX J comma -20
+KPX J period -20
+KPX J u -20
+KPX J uacute -20
+KPX J ucircumflex -20
+KPX J udieresis -20
+KPX J ugrave -20
+KPX J uhungarumlaut -20
+KPX J umacron -20
+KPX J uogonek -20
+KPX J uring -20
+KPX K O -30
+KPX K Oacute -30
+KPX K Ocircumflex -30
+KPX K Odieresis -30
+KPX K Ograve -30
+KPX K Ohungarumlaut -30
+KPX K Omacron -30
+KPX K Oslash -30
+KPX K Otilde -30
+KPX K e -15
+KPX K eacute -15
+KPX K ecaron -15
+KPX K ecircumflex -15
+KPX K edieresis -15
+KPX K edotaccent -15
+KPX K egrave -15
+KPX K emacron -15
+KPX K eogonek -15
+KPX K o -35
+KPX K oacute -35
+KPX K ocircumflex -35
+KPX K odieresis -35
+KPX K ograve -35
+KPX K ohungarumlaut -35
+KPX K omacron -35
+KPX K oslash -35
+KPX K otilde -35
+KPX K u -30
+KPX K uacute -30
+KPX K ucircumflex -30
+KPX K udieresis -30
+KPX K ugrave -30
+KPX K uhungarumlaut -30
+KPX K umacron -30
+KPX K uogonek -30
+KPX K uring -30
+KPX K y -40
+KPX K yacute -40
+KPX K ydieresis -40
+KPX Kcommaaccent O -30
+KPX Kcommaaccent Oacute -30
+KPX Kcommaaccent Ocircumflex -30
+KPX Kcommaaccent Odieresis -30
+KPX Kcommaaccent Ograve -30
+KPX Kcommaaccent Ohungarumlaut -30
+KPX Kcommaaccent Omacron -30
+KPX Kcommaaccent Oslash -30
+KPX Kcommaaccent Otilde -30
+KPX Kcommaaccent e -15
+KPX Kcommaaccent eacute -15
+KPX Kcommaaccent ecaron -15
+KPX Kcommaaccent ecircumflex -15
+KPX Kcommaaccent edieresis -15
+KPX Kcommaaccent edotaccent -15
+KPX Kcommaaccent egrave -15
+KPX Kcommaaccent emacron -15
+KPX Kcommaaccent eogonek -15
+KPX Kcommaaccent o -35
+KPX Kcommaaccent oacute -35
+KPX Kcommaaccent ocircumflex -35
+KPX Kcommaaccent odieresis -35
+KPX Kcommaaccent ograve -35
+KPX Kcommaaccent ohungarumlaut -35
+KPX Kcommaaccent omacron -35
+KPX Kcommaaccent oslash -35
+KPX Kcommaaccent otilde -35
+KPX Kcommaaccent u -30
+KPX Kcommaaccent uacute -30
+KPX Kcommaaccent ucircumflex -30
+KPX Kcommaaccent udieresis -30
+KPX Kcommaaccent ugrave -30
+KPX Kcommaaccent uhungarumlaut -30
+KPX Kcommaaccent umacron -30
+KPX Kcommaaccent uogonek -30
+KPX Kcommaaccent uring -30
+KPX Kcommaaccent y -40
+KPX Kcommaaccent yacute -40
+KPX Kcommaaccent ydieresis -40
+KPX L T -90
+KPX L Tcaron -90
+KPX L Tcommaaccent -90
+KPX L V -110
+KPX L W -80
+KPX L Y -120
+KPX L Yacute -120
+KPX L Ydieresis -120
+KPX L quotedblright -140
+KPX L quoteright -140
+KPX L y -30
+KPX L yacute -30
+KPX L ydieresis -30
+KPX Lacute T -90
+KPX Lacute Tcaron -90
+KPX Lacute Tcommaaccent -90
+KPX Lacute V -110
+KPX Lacute W -80
+KPX Lacute Y -120
+KPX Lacute Yacute -120
+KPX Lacute Ydieresis -120
+KPX Lacute quotedblright -140
+KPX Lacute quoteright -140
+KPX Lacute y -30
+KPX Lacute yacute -30
+KPX Lacute ydieresis -30
+KPX Lcommaaccent T -90
+KPX Lcommaaccent Tcaron -90
+KPX Lcommaaccent Tcommaaccent -90
+KPX Lcommaaccent V -110
+KPX Lcommaaccent W -80
+KPX Lcommaaccent Y -120
+KPX Lcommaaccent Yacute -120
+KPX Lcommaaccent Ydieresis -120
+KPX Lcommaaccent quotedblright -140
+KPX Lcommaaccent quoteright -140
+KPX Lcommaaccent y -30
+KPX Lcommaaccent yacute -30
+KPX Lcommaaccent ydieresis -30
+KPX Lslash T -90
+KPX Lslash Tcaron -90
+KPX Lslash Tcommaaccent -90
+KPX Lslash V -110
+KPX Lslash W -80
+KPX Lslash Y -120
+KPX Lslash Yacute -120
+KPX Lslash Ydieresis -120
+KPX Lslash quotedblright -140
+KPX Lslash quoteright -140
+KPX Lslash y -30
+KPX Lslash yacute -30
+KPX Lslash ydieresis -30
+KPX O A -50
+KPX O Aacute -50
+KPX O Abreve -50
+KPX O Acircumflex -50
+KPX O Adieresis -50
+KPX O Agrave -50
+KPX O Amacron -50
+KPX O Aogonek -50
+KPX O Aring -50
+KPX O Atilde -50
+KPX O T -40
+KPX O Tcaron -40
+KPX O Tcommaaccent -40
+KPX O V -50
+KPX O W -50
+KPX O X -50
+KPX O Y -70
+KPX O Yacute -70
+KPX O Ydieresis -70
+KPX O comma -40
+KPX O period -40
+KPX Oacute A -50
+KPX Oacute Aacute -50
+KPX Oacute Abreve -50
+KPX Oacute Acircumflex -50
+KPX Oacute Adieresis -50
+KPX Oacute Agrave -50
+KPX Oacute Amacron -50
+KPX Oacute Aogonek -50
+KPX Oacute Aring -50
+KPX Oacute Atilde -50
+KPX Oacute T -40
+KPX Oacute Tcaron -40
+KPX Oacute Tcommaaccent -40
+KPX Oacute V -50
+KPX Oacute W -50
+KPX Oacute X -50
+KPX Oacute Y -70
+KPX Oacute Yacute -70
+KPX Oacute Ydieresis -70
+KPX Oacute comma -40
+KPX Oacute period -40
+KPX Ocircumflex A -50
+KPX Ocircumflex Aacute -50
+KPX Ocircumflex Abreve -50
+KPX Ocircumflex Acircumflex -50
+KPX Ocircumflex Adieresis -50
+KPX Ocircumflex Agrave -50
+KPX Ocircumflex Amacron -50
+KPX Ocircumflex Aogonek -50
+KPX Ocircumflex Aring -50
+KPX Ocircumflex Atilde -50
+KPX Ocircumflex T -40
+KPX Ocircumflex Tcaron -40
+KPX Ocircumflex Tcommaaccent -40
+KPX Ocircumflex V -50
+KPX Ocircumflex W -50
+KPX Ocircumflex X -50
+KPX Ocircumflex Y -70
+KPX Ocircumflex Yacute -70
+KPX Ocircumflex Ydieresis -70
+KPX Ocircumflex comma -40
+KPX Ocircumflex period -40
+KPX Odieresis A -50
+KPX Odieresis Aacute -50
+KPX Odieresis Abreve -50
+KPX Odieresis Acircumflex -50
+KPX Odieresis Adieresis -50
+KPX Odieresis Agrave -50
+KPX Odieresis Amacron -50
+KPX Odieresis Aogonek -50
+KPX Odieresis Aring -50
+KPX Odieresis Atilde -50
+KPX Odieresis T -40
+KPX Odieresis Tcaron -40
+KPX Odieresis Tcommaaccent -40
+KPX Odieresis V -50
+KPX Odieresis W -50
+KPX Odieresis X -50
+KPX Odieresis Y -70
+KPX Odieresis Yacute -70
+KPX Odieresis Ydieresis -70
+KPX Odieresis comma -40
+KPX Odieresis period -40
+KPX Ograve A -50
+KPX Ograve Aacute -50
+KPX Ograve Abreve -50
+KPX Ograve Acircumflex -50
+KPX Ograve Adieresis -50
+KPX Ograve Agrave -50
+KPX Ograve Amacron -50
+KPX Ograve Aogonek -50
+KPX Ograve Aring -50
+KPX Ograve Atilde -50
+KPX Ograve T -40
+KPX Ograve Tcaron -40
+KPX Ograve Tcommaaccent -40
+KPX Ograve V -50
+KPX Ograve W -50
+KPX Ograve X -50
+KPX Ograve Y -70
+KPX Ograve Yacute -70
+KPX Ograve Ydieresis -70
+KPX Ograve comma -40
+KPX Ograve period -40
+KPX Ohungarumlaut A -50
+KPX Ohungarumlaut Aacute -50
+KPX Ohungarumlaut Abreve -50
+KPX Ohungarumlaut Acircumflex -50
+KPX Ohungarumlaut Adieresis -50
+KPX Ohungarumlaut Agrave -50
+KPX Ohungarumlaut Amacron -50
+KPX Ohungarumlaut Aogonek -50
+KPX Ohungarumlaut Aring -50
+KPX Ohungarumlaut Atilde -50
+KPX Ohungarumlaut T -40
+KPX Ohungarumlaut Tcaron -40
+KPX Ohungarumlaut Tcommaaccent -40
+KPX Ohungarumlaut V -50
+KPX Ohungarumlaut W -50
+KPX Ohungarumlaut X -50
+KPX Ohungarumlaut Y -70
+KPX Ohungarumlaut Yacute -70
+KPX Ohungarumlaut Ydieresis -70
+KPX Ohungarumlaut comma -40
+KPX Ohungarumlaut period -40
+KPX Omacron A -50
+KPX Omacron Aacute -50
+KPX Omacron Abreve -50
+KPX Omacron Acircumflex -50
+KPX Omacron Adieresis -50
+KPX Omacron Agrave -50
+KPX Omacron Amacron -50
+KPX Omacron Aogonek -50
+KPX Omacron Aring -50
+KPX Omacron Atilde -50
+KPX Omacron T -40
+KPX Omacron Tcaron -40
+KPX Omacron Tcommaaccent -40
+KPX Omacron V -50
+KPX Omacron W -50
+KPX Omacron X -50
+KPX Omacron Y -70
+KPX Omacron Yacute -70
+KPX Omacron Ydieresis -70
+KPX Omacron comma -40
+KPX Omacron period -40
+KPX Oslash A -50
+KPX Oslash Aacute -50
+KPX Oslash Abreve -50
+KPX Oslash Acircumflex -50
+KPX Oslash Adieresis -50
+KPX Oslash Agrave -50
+KPX Oslash Amacron -50
+KPX Oslash Aogonek -50
+KPX Oslash Aring -50
+KPX Oslash Atilde -50
+KPX Oslash T -40
+KPX Oslash Tcaron -40
+KPX Oslash Tcommaaccent -40
+KPX Oslash V -50
+KPX Oslash W -50
+KPX Oslash X -50
+KPX Oslash Y -70
+KPX Oslash Yacute -70
+KPX Oslash Ydieresis -70
+KPX Oslash comma -40
+KPX Oslash period -40
+KPX Otilde A -50
+KPX Otilde Aacute -50
+KPX Otilde Abreve -50
+KPX Otilde Acircumflex -50
+KPX Otilde Adieresis -50
+KPX Otilde Agrave -50
+KPX Otilde Amacron -50
+KPX Otilde Aogonek -50
+KPX Otilde Aring -50
+KPX Otilde Atilde -50
+KPX Otilde T -40
+KPX Otilde Tcaron -40
+KPX Otilde Tcommaaccent -40
+KPX Otilde V -50
+KPX Otilde W -50
+KPX Otilde X -50
+KPX Otilde Y -70
+KPX Otilde Yacute -70
+KPX Otilde Ydieresis -70
+KPX Otilde comma -40
+KPX Otilde period -40
+KPX P A -100
+KPX P Aacute -100
+KPX P Abreve -100
+KPX P Acircumflex -100
+KPX P Adieresis -100
+KPX P Agrave -100
+KPX P Amacron -100
+KPX P Aogonek -100
+KPX P Aring -100
+KPX P Atilde -100
+KPX P a -30
+KPX P aacute -30
+KPX P abreve -30
+KPX P acircumflex -30
+KPX P adieresis -30
+KPX P agrave -30
+KPX P amacron -30
+KPX P aogonek -30
+KPX P aring -30
+KPX P atilde -30
+KPX P comma -120
+KPX P e -30
+KPX P eacute -30
+KPX P ecaron -30
+KPX P ecircumflex -30
+KPX P edieresis -30
+KPX P edotaccent -30
+KPX P egrave -30
+KPX P emacron -30
+KPX P eogonek -30
+KPX P o -40
+KPX P oacute -40
+KPX P ocircumflex -40
+KPX P odieresis -40
+KPX P ograve -40
+KPX P ohungarumlaut -40
+KPX P omacron -40
+KPX P oslash -40
+KPX P otilde -40
+KPX P period -120
+KPX Q U -10
+KPX Q Uacute -10
+KPX Q Ucircumflex -10
+KPX Q Udieresis -10
+KPX Q Ugrave -10
+KPX Q Uhungarumlaut -10
+KPX Q Umacron -10
+KPX Q Uogonek -10
+KPX Q Uring -10
+KPX Q comma 20
+KPX Q period 20
+KPX R O -20
+KPX R Oacute -20
+KPX R Ocircumflex -20
+KPX R Odieresis -20
+KPX R Ograve -20
+KPX R Ohungarumlaut -20
+KPX R Omacron -20
+KPX R Oslash -20
+KPX R Otilde -20
+KPX R T -20
+KPX R Tcaron -20
+KPX R Tcommaaccent -20
+KPX R U -20
+KPX R Uacute -20
+KPX R Ucircumflex -20
+KPX R Udieresis -20
+KPX R Ugrave -20
+KPX R Uhungarumlaut -20
+KPX R Umacron -20
+KPX R Uogonek -20
+KPX R Uring -20
+KPX R V -50
+KPX R W -40
+KPX R Y -50
+KPX R Yacute -50
+KPX R Ydieresis -50
+KPX Racute O -20
+KPX Racute Oacute -20
+KPX Racute Ocircumflex -20
+KPX Racute Odieresis -20
+KPX Racute Ograve -20
+KPX Racute Ohungarumlaut -20
+KPX Racute Omacron -20
+KPX Racute Oslash -20
+KPX Racute Otilde -20
+KPX Racute T -20
+KPX Racute Tcaron -20
+KPX Racute Tcommaaccent -20
+KPX Racute U -20
+KPX Racute Uacute -20
+KPX Racute Ucircumflex -20
+KPX Racute Udieresis -20
+KPX Racute Ugrave -20
+KPX Racute Uhungarumlaut -20
+KPX Racute Umacron -20
+KPX Racute Uogonek -20
+KPX Racute Uring -20
+KPX Racute V -50
+KPX Racute W -40
+KPX Racute Y -50
+KPX Racute Yacute -50
+KPX Racute Ydieresis -50
+KPX Rcaron O -20
+KPX Rcaron Oacute -20
+KPX Rcaron Ocircumflex -20
+KPX Rcaron Odieresis -20
+KPX Rcaron Ograve -20
+KPX Rcaron Ohungarumlaut -20
+KPX Rcaron Omacron -20
+KPX Rcaron Oslash -20
+KPX Rcaron Otilde -20
+KPX Rcaron T -20
+KPX Rcaron Tcaron -20
+KPX Rcaron Tcommaaccent -20
+KPX Rcaron U -20
+KPX Rcaron Uacute -20
+KPX Rcaron Ucircumflex -20
+KPX Rcaron Udieresis -20
+KPX Rcaron Ugrave -20
+KPX Rcaron Uhungarumlaut -20
+KPX Rcaron Umacron -20
+KPX Rcaron Uogonek -20
+KPX Rcaron Uring -20
+KPX Rcaron V -50
+KPX Rcaron W -40
+KPX Rcaron Y -50
+KPX Rcaron Yacute -50
+KPX Rcaron Ydieresis -50
+KPX Rcommaaccent O -20
+KPX Rcommaaccent Oacute -20
+KPX Rcommaaccent Ocircumflex -20
+KPX Rcommaaccent Odieresis -20
+KPX Rcommaaccent Ograve -20
+KPX Rcommaaccent Ohungarumlaut -20
+KPX Rcommaaccent Omacron -20
+KPX Rcommaaccent Oslash -20
+KPX Rcommaaccent Otilde -20
+KPX Rcommaaccent T -20
+KPX Rcommaaccent Tcaron -20
+KPX Rcommaaccent Tcommaaccent -20
+KPX Rcommaaccent U -20
+KPX Rcommaaccent Uacute -20
+KPX Rcommaaccent Ucircumflex -20
+KPX Rcommaaccent Udieresis -20
+KPX Rcommaaccent Ugrave -20
+KPX Rcommaaccent Uhungarumlaut -20
+KPX Rcommaaccent Umacron -20
+KPX Rcommaaccent Uogonek -20
+KPX Rcommaaccent Uring -20
+KPX Rcommaaccent V -50
+KPX Rcommaaccent W -40
+KPX Rcommaaccent Y -50
+KPX Rcommaaccent Yacute -50
+KPX Rcommaaccent Ydieresis -50
+KPX T A -90
+KPX T Aacute -90
+KPX T Abreve -90
+KPX T Acircumflex -90
+KPX T Adieresis -90
+KPX T Agrave -90
+KPX T Amacron -90
+KPX T Aogonek -90
+KPX T Aring -90
+KPX T Atilde -90
+KPX T O -40
+KPX T Oacute -40
+KPX T Ocircumflex -40
+KPX T Odieresis -40
+KPX T Ograve -40
+KPX T Ohungarumlaut -40
+KPX T Omacron -40
+KPX T Oslash -40
+KPX T Otilde -40
+KPX T a -80
+KPX T aacute -80
+KPX T abreve -80
+KPX T acircumflex -80
+KPX T adieresis -80
+KPX T agrave -80
+KPX T amacron -80
+KPX T aogonek -80
+KPX T aring -80
+KPX T atilde -80
+KPX T colon -40
+KPX T comma -80
+KPX T e -60
+KPX T eacute -60
+KPX T ecaron -60
+KPX T ecircumflex -60
+KPX T edieresis -60
+KPX T edotaccent -60
+KPX T egrave -60
+KPX T emacron -60
+KPX T eogonek -60
+KPX T hyphen -120
+KPX T o -80
+KPX T oacute -80
+KPX T ocircumflex -80
+KPX T odieresis -80
+KPX T ograve -80
+KPX T ohungarumlaut -80
+KPX T omacron -80
+KPX T oslash -80
+KPX T otilde -80
+KPX T period -80
+KPX T r -80
+KPX T racute -80
+KPX T rcommaaccent -80
+KPX T semicolon -40
+KPX T u -90
+KPX T uacute -90
+KPX T ucircumflex -90
+KPX T udieresis -90
+KPX T ugrave -90
+KPX T uhungarumlaut -90
+KPX T umacron -90
+KPX T uogonek -90
+KPX T uring -90
+KPX T w -60
+KPX T y -60
+KPX T yacute -60
+KPX T ydieresis -60
+KPX Tcaron A -90
+KPX Tcaron Aacute -90
+KPX Tcaron Abreve -90
+KPX Tcaron Acircumflex -90
+KPX Tcaron Adieresis -90
+KPX Tcaron Agrave -90
+KPX Tcaron Amacron -90
+KPX Tcaron Aogonek -90
+KPX Tcaron Aring -90
+KPX Tcaron Atilde -90
+KPX Tcaron O -40
+KPX Tcaron Oacute -40
+KPX Tcaron Ocircumflex -40
+KPX Tcaron Odieresis -40
+KPX Tcaron Ograve -40
+KPX Tcaron Ohungarumlaut -40
+KPX Tcaron Omacron -40
+KPX Tcaron Oslash -40
+KPX Tcaron Otilde -40
+KPX Tcaron a -80
+KPX Tcaron aacute -80
+KPX Tcaron abreve -80
+KPX Tcaron acircumflex -80
+KPX Tcaron adieresis -80
+KPX Tcaron agrave -80
+KPX Tcaron amacron -80
+KPX Tcaron aogonek -80
+KPX Tcaron aring -80
+KPX Tcaron atilde -80
+KPX Tcaron colon -40
+KPX Tcaron comma -80
+KPX Tcaron e -60
+KPX Tcaron eacute -60
+KPX Tcaron ecaron -60
+KPX Tcaron ecircumflex -60
+KPX Tcaron edieresis -60
+KPX Tcaron edotaccent -60
+KPX Tcaron egrave -60
+KPX Tcaron emacron -60
+KPX Tcaron eogonek -60
+KPX Tcaron hyphen -120
+KPX Tcaron o -80
+KPX Tcaron oacute -80
+KPX Tcaron ocircumflex -80
+KPX Tcaron odieresis -80
+KPX Tcaron ograve -80
+KPX Tcaron ohungarumlaut -80
+KPX Tcaron omacron -80
+KPX Tcaron oslash -80
+KPX Tcaron otilde -80
+KPX Tcaron period -80
+KPX Tcaron r -80
+KPX Tcaron racute -80
+KPX Tcaron rcommaaccent -80
+KPX Tcaron semicolon -40
+KPX Tcaron u -90
+KPX Tcaron uacute -90
+KPX Tcaron ucircumflex -90
+KPX Tcaron udieresis -90
+KPX Tcaron ugrave -90
+KPX Tcaron uhungarumlaut -90
+KPX Tcaron umacron -90
+KPX Tcaron uogonek -90
+KPX Tcaron uring -90
+KPX Tcaron w -60
+KPX Tcaron y -60
+KPX Tcaron yacute -60
+KPX Tcaron ydieresis -60
+KPX Tcommaaccent A -90
+KPX Tcommaaccent Aacute -90
+KPX Tcommaaccent Abreve -90
+KPX Tcommaaccent Acircumflex -90
+KPX Tcommaaccent Adieresis -90
+KPX Tcommaaccent Agrave -90
+KPX Tcommaaccent Amacron -90
+KPX Tcommaaccent Aogonek -90
+KPX Tcommaaccent Aring -90
+KPX Tcommaaccent Atilde -90
+KPX Tcommaaccent O -40
+KPX Tcommaaccent Oacute -40
+KPX Tcommaaccent Ocircumflex -40
+KPX Tcommaaccent Odieresis -40
+KPX Tcommaaccent Ograve -40
+KPX Tcommaaccent Ohungarumlaut -40
+KPX Tcommaaccent Omacron -40
+KPX Tcommaaccent Oslash -40
+KPX Tcommaaccent Otilde -40
+KPX Tcommaaccent a -80
+KPX Tcommaaccent aacute -80
+KPX Tcommaaccent abreve -80
+KPX Tcommaaccent acircumflex -80
+KPX Tcommaaccent adieresis -80
+KPX Tcommaaccent agrave -80
+KPX Tcommaaccent amacron -80
+KPX Tcommaaccent aogonek -80
+KPX Tcommaaccent aring -80
+KPX Tcommaaccent atilde -80
+KPX Tcommaaccent colon -40
+KPX Tcommaaccent comma -80
+KPX Tcommaaccent e -60
+KPX Tcommaaccent eacute -60
+KPX Tcommaaccent ecaron -60
+KPX Tcommaaccent ecircumflex -60
+KPX Tcommaaccent edieresis -60
+KPX Tcommaaccent edotaccent -60
+KPX Tcommaaccent egrave -60
+KPX Tcommaaccent emacron -60
+KPX Tcommaaccent eogonek -60
+KPX Tcommaaccent hyphen -120
+KPX Tcommaaccent o -80
+KPX Tcommaaccent oacute -80
+KPX Tcommaaccent ocircumflex -80
+KPX Tcommaaccent odieresis -80
+KPX Tcommaaccent ograve -80
+KPX Tcommaaccent ohungarumlaut -80
+KPX Tcommaaccent omacron -80
+KPX Tcommaaccent oslash -80
+KPX Tcommaaccent otilde -80
+KPX Tcommaaccent period -80
+KPX Tcommaaccent r -80
+KPX Tcommaaccent racute -80
+KPX Tcommaaccent rcommaaccent -80
+KPX Tcommaaccent semicolon -40
+KPX Tcommaaccent u -90
+KPX Tcommaaccent uacute -90
+KPX Tcommaaccent ucircumflex -90
+KPX Tcommaaccent udieresis -90
+KPX Tcommaaccent ugrave -90
+KPX Tcommaaccent uhungarumlaut -90
+KPX Tcommaaccent umacron -90
+KPX Tcommaaccent uogonek -90
+KPX Tcommaaccent uring -90
+KPX Tcommaaccent w -60
+KPX Tcommaaccent y -60
+KPX Tcommaaccent yacute -60
+KPX Tcommaaccent ydieresis -60
+KPX U A -50
+KPX U Aacute -50
+KPX U Abreve -50
+KPX U Acircumflex -50
+KPX U Adieresis -50
+KPX U Agrave -50
+KPX U Amacron -50
+KPX U Aogonek -50
+KPX U Aring -50
+KPX U Atilde -50
+KPX U comma -30
+KPX U period -30
+KPX Uacute A -50
+KPX Uacute Aacute -50
+KPX Uacute Abreve -50
+KPX Uacute Acircumflex -50
+KPX Uacute Adieresis -50
+KPX Uacute Agrave -50
+KPX Uacute Amacron -50
+KPX Uacute Aogonek -50
+KPX Uacute Aring -50
+KPX Uacute Atilde -50
+KPX Uacute comma -30
+KPX Uacute period -30
+KPX Ucircumflex A -50
+KPX Ucircumflex Aacute -50
+KPX Ucircumflex Abreve -50
+KPX Ucircumflex Acircumflex -50
+KPX Ucircumflex Adieresis -50
+KPX Ucircumflex Agrave -50
+KPX Ucircumflex Amacron -50
+KPX Ucircumflex Aogonek -50
+KPX Ucircumflex Aring -50
+KPX Ucircumflex Atilde -50
+KPX Ucircumflex comma -30
+KPX Ucircumflex period -30
+KPX Udieresis A -50
+KPX Udieresis Aacute -50
+KPX Udieresis Abreve -50
+KPX Udieresis Acircumflex -50
+KPX Udieresis Adieresis -50
+KPX Udieresis Agrave -50
+KPX Udieresis Amacron -50
+KPX Udieresis Aogonek -50
+KPX Udieresis Aring -50
+KPX Udieresis Atilde -50
+KPX Udieresis comma -30
+KPX Udieresis period -30
+KPX Ugrave A -50
+KPX Ugrave Aacute -50
+KPX Ugrave Abreve -50
+KPX Ugrave Acircumflex -50
+KPX Ugrave Adieresis -50
+KPX Ugrave Agrave -50
+KPX Ugrave Amacron -50
+KPX Ugrave Aogonek -50
+KPX Ugrave Aring -50
+KPX Ugrave Atilde -50
+KPX Ugrave comma -30
+KPX Ugrave period -30
+KPX Uhungarumlaut A -50
+KPX Uhungarumlaut Aacute -50
+KPX Uhungarumlaut Abreve -50
+KPX Uhungarumlaut Acircumflex -50
+KPX Uhungarumlaut Adieresis -50
+KPX Uhungarumlaut Agrave -50
+KPX Uhungarumlaut Amacron -50
+KPX Uhungarumlaut Aogonek -50
+KPX Uhungarumlaut Aring -50
+KPX Uhungarumlaut Atilde -50
+KPX Uhungarumlaut comma -30
+KPX Uhungarumlaut period -30
+KPX Umacron A -50
+KPX Umacron Aacute -50
+KPX Umacron Abreve -50
+KPX Umacron Acircumflex -50
+KPX Umacron Adieresis -50
+KPX Umacron Agrave -50
+KPX Umacron Amacron -50
+KPX Umacron Aogonek -50
+KPX Umacron Aring -50
+KPX Umacron Atilde -50
+KPX Umacron comma -30
+KPX Umacron period -30
+KPX Uogonek A -50
+KPX Uogonek Aacute -50
+KPX Uogonek Abreve -50
+KPX Uogonek Acircumflex -50
+KPX Uogonek Adieresis -50
+KPX Uogonek Agrave -50
+KPX Uogonek Amacron -50
+KPX Uogonek Aogonek -50
+KPX Uogonek Aring -50
+KPX Uogonek Atilde -50
+KPX Uogonek comma -30
+KPX Uogonek period -30
+KPX Uring A -50
+KPX Uring Aacute -50
+KPX Uring Abreve -50
+KPX Uring Acircumflex -50
+KPX Uring Adieresis -50
+KPX Uring Agrave -50
+KPX Uring Amacron -50
+KPX Uring Aogonek -50
+KPX Uring Aring -50
+KPX Uring Atilde -50
+KPX Uring comma -30
+KPX Uring period -30
+KPX V A -80
+KPX V Aacute -80
+KPX V Abreve -80
+KPX V Acircumflex -80
+KPX V Adieresis -80
+KPX V Agrave -80
+KPX V Amacron -80
+KPX V Aogonek -80
+KPX V Aring -80
+KPX V Atilde -80
+KPX V G -50
+KPX V Gbreve -50
+KPX V Gcommaaccent -50
+KPX V O -50
+KPX V Oacute -50
+KPX V Ocircumflex -50
+KPX V Odieresis -50
+KPX V Ograve -50
+KPX V Ohungarumlaut -50
+KPX V Omacron -50
+KPX V Oslash -50
+KPX V Otilde -50
+KPX V a -60
+KPX V aacute -60
+KPX V abreve -60
+KPX V acircumflex -60
+KPX V adieresis -60
+KPX V agrave -60
+KPX V amacron -60
+KPX V aogonek -60
+KPX V aring -60
+KPX V atilde -60
+KPX V colon -40
+KPX V comma -120
+KPX V e -50
+KPX V eacute -50
+KPX V ecaron -50
+KPX V ecircumflex -50
+KPX V edieresis -50
+KPX V edotaccent -50
+KPX V egrave -50
+KPX V emacron -50
+KPX V eogonek -50
+KPX V hyphen -80
+KPX V o -90
+KPX V oacute -90
+KPX V ocircumflex -90
+KPX V odieresis -90
+KPX V ograve -90
+KPX V ohungarumlaut -90
+KPX V omacron -90
+KPX V oslash -90
+KPX V otilde -90
+KPX V period -120
+KPX V semicolon -40
+KPX V u -60
+KPX V uacute -60
+KPX V ucircumflex -60
+KPX V udieresis -60
+KPX V ugrave -60
+KPX V uhungarumlaut -60
+KPX V umacron -60
+KPX V uogonek -60
+KPX V uring -60
+KPX W A -60
+KPX W Aacute -60
+KPX W Abreve -60
+KPX W Acircumflex -60
+KPX W Adieresis -60
+KPX W Agrave -60
+KPX W Amacron -60
+KPX W Aogonek -60
+KPX W Aring -60
+KPX W Atilde -60
+KPX W O -20
+KPX W Oacute -20
+KPX W Ocircumflex -20
+KPX W Odieresis -20
+KPX W Ograve -20
+KPX W Ohungarumlaut -20
+KPX W Omacron -20
+KPX W Oslash -20
+KPX W Otilde -20
+KPX W a -40
+KPX W aacute -40
+KPX W abreve -40
+KPX W acircumflex -40
+KPX W adieresis -40
+KPX W agrave -40
+KPX W amacron -40
+KPX W aogonek -40
+KPX W aring -40
+KPX W atilde -40
+KPX W colon -10
+KPX W comma -80
+KPX W e -35
+KPX W eacute -35
+KPX W ecaron -35
+KPX W ecircumflex -35
+KPX W edieresis -35
+KPX W edotaccent -35
+KPX W egrave -35
+KPX W emacron -35
+KPX W eogonek -35
+KPX W hyphen -40
+KPX W o -60
+KPX W oacute -60
+KPX W ocircumflex -60
+KPX W odieresis -60
+KPX W ograve -60
+KPX W ohungarumlaut -60
+KPX W omacron -60
+KPX W oslash -60
+KPX W otilde -60
+KPX W period -80
+KPX W semicolon -10
+KPX W u -45
+KPX W uacute -45
+KPX W ucircumflex -45
+KPX W udieresis -45
+KPX W ugrave -45
+KPX W uhungarumlaut -45
+KPX W umacron -45
+KPX W uogonek -45
+KPX W uring -45
+KPX W y -20
+KPX W yacute -20
+KPX W ydieresis -20
+KPX Y A -110
+KPX Y Aacute -110
+KPX Y Abreve -110
+KPX Y Acircumflex -110
+KPX Y Adieresis -110
+KPX Y Agrave -110
+KPX Y Amacron -110
+KPX Y Aogonek -110
+KPX Y Aring -110
+KPX Y Atilde -110
+KPX Y O -70
+KPX Y Oacute -70
+KPX Y Ocircumflex -70
+KPX Y Odieresis -70
+KPX Y Ograve -70
+KPX Y Ohungarumlaut -70
+KPX Y Omacron -70
+KPX Y Oslash -70
+KPX Y Otilde -70
+KPX Y a -90
+KPX Y aacute -90
+KPX Y abreve -90
+KPX Y acircumflex -90
+KPX Y adieresis -90
+KPX Y agrave -90
+KPX Y amacron -90
+KPX Y aogonek -90
+KPX Y aring -90
+KPX Y atilde -90
+KPX Y colon -50
+KPX Y comma -100
+KPX Y e -80
+KPX Y eacute -80
+KPX Y ecaron -80
+KPX Y ecircumflex -80
+KPX Y edieresis -80
+KPX Y edotaccent -80
+KPX Y egrave -80
+KPX Y emacron -80
+KPX Y eogonek -80
+KPX Y o -100
+KPX Y oacute -100
+KPX Y ocircumflex -100
+KPX Y odieresis -100
+KPX Y ograve -100
+KPX Y ohungarumlaut -100
+KPX Y omacron -100
+KPX Y oslash -100
+KPX Y otilde -100
+KPX Y period -100
+KPX Y semicolon -50
+KPX Y u -100
+KPX Y uacute -100
+KPX Y ucircumflex -100
+KPX Y udieresis -100
+KPX Y ugrave -100
+KPX Y uhungarumlaut -100
+KPX Y umacron -100
+KPX Y uogonek -100
+KPX Y uring -100
+KPX Yacute A -110
+KPX Yacute Aacute -110
+KPX Yacute Abreve -110
+KPX Yacute Acircumflex -110
+KPX Yacute Adieresis -110
+KPX Yacute Agrave -110
+KPX Yacute Amacron -110
+KPX Yacute Aogonek -110
+KPX Yacute Aring -110
+KPX Yacute Atilde -110
+KPX Yacute O -70
+KPX Yacute Oacute -70
+KPX Yacute Ocircumflex -70
+KPX Yacute Odieresis -70
+KPX Yacute Ograve -70
+KPX Yacute Ohungarumlaut -70
+KPX Yacute Omacron -70
+KPX Yacute Oslash -70
+KPX Yacute Otilde -70
+KPX Yacute a -90
+KPX Yacute aacute -90
+KPX Yacute abreve -90
+KPX Yacute acircumflex -90
+KPX Yacute adieresis -90
+KPX Yacute agrave -90
+KPX Yacute amacron -90
+KPX Yacute aogonek -90
+KPX Yacute aring -90
+KPX Yacute atilde -90
+KPX Yacute colon -50
+KPX Yacute comma -100
+KPX Yacute e -80
+KPX Yacute eacute -80
+KPX Yacute ecaron -80
+KPX Yacute ecircumflex -80
+KPX Yacute edieresis -80
+KPX Yacute edotaccent -80
+KPX Yacute egrave -80
+KPX Yacute emacron -80
+KPX Yacute eogonek -80
+KPX Yacute o -100
+KPX Yacute oacute -100
+KPX Yacute ocircumflex -100
+KPX Yacute odieresis -100
+KPX Yacute ograve -100
+KPX Yacute ohungarumlaut -100
+KPX Yacute omacron -100
+KPX Yacute oslash -100
+KPX Yacute otilde -100
+KPX Yacute period -100
+KPX Yacute semicolon -50
+KPX Yacute u -100
+KPX Yacute uacute -100
+KPX Yacute ucircumflex -100
+KPX Yacute udieresis -100
+KPX Yacute ugrave -100
+KPX Yacute uhungarumlaut -100
+KPX Yacute umacron -100
+KPX Yacute uogonek -100
+KPX Yacute uring -100
+KPX Ydieresis A -110
+KPX Ydieresis Aacute -110
+KPX Ydieresis Abreve -110
+KPX Ydieresis Acircumflex -110
+KPX Ydieresis Adieresis -110
+KPX Ydieresis Agrave -110
+KPX Ydieresis Amacron -110
+KPX Ydieresis Aogonek -110
+KPX Ydieresis Aring -110
+KPX Ydieresis Atilde -110
+KPX Ydieresis O -70
+KPX Ydieresis Oacute -70
+KPX Ydieresis Ocircumflex -70
+KPX Ydieresis Odieresis -70
+KPX Ydieresis Ograve -70
+KPX Ydieresis Ohungarumlaut -70
+KPX Ydieresis Omacron -70
+KPX Ydieresis Oslash -70
+KPX Ydieresis Otilde -70
+KPX Ydieresis a -90
+KPX Ydieresis aacute -90
+KPX Ydieresis abreve -90
+KPX Ydieresis acircumflex -90
+KPX Ydieresis adieresis -90
+KPX Ydieresis agrave -90
+KPX Ydieresis amacron -90
+KPX Ydieresis aogonek -90
+KPX Ydieresis aring -90
+KPX Ydieresis atilde -90
+KPX Ydieresis colon -50
+KPX Ydieresis comma -100
+KPX Ydieresis e -80
+KPX Ydieresis eacute -80
+KPX Ydieresis ecaron -80
+KPX Ydieresis ecircumflex -80
+KPX Ydieresis edieresis -80
+KPX Ydieresis edotaccent -80
+KPX Ydieresis egrave -80
+KPX Ydieresis emacron -80
+KPX Ydieresis eogonek -80
+KPX Ydieresis o -100
+KPX Ydieresis oacute -100
+KPX Ydieresis ocircumflex -100
+KPX Ydieresis odieresis -100
+KPX Ydieresis ograve -100
+KPX Ydieresis ohungarumlaut -100
+KPX Ydieresis omacron -100
+KPX Ydieresis oslash -100
+KPX Ydieresis otilde -100
+KPX Ydieresis period -100
+KPX Ydieresis semicolon -50
+KPX Ydieresis u -100
+KPX Ydieresis uacute -100
+KPX Ydieresis ucircumflex -100
+KPX Ydieresis udieresis -100
+KPX Ydieresis ugrave -100
+KPX Ydieresis uhungarumlaut -100
+KPX Ydieresis umacron -100
+KPX Ydieresis uogonek -100
+KPX Ydieresis uring -100
+KPX a g -10
+KPX a gbreve -10
+KPX a gcommaaccent -10
+KPX a v -15
+KPX a w -15
+KPX a y -20
+KPX a yacute -20
+KPX a ydieresis -20
+KPX aacute g -10
+KPX aacute gbreve -10
+KPX aacute gcommaaccent -10
+KPX aacute v -15
+KPX aacute w -15
+KPX aacute y -20
+KPX aacute yacute -20
+KPX aacute ydieresis -20
+KPX abreve g -10
+KPX abreve gbreve -10
+KPX abreve gcommaaccent -10
+KPX abreve v -15
+KPX abreve w -15
+KPX abreve y -20
+KPX abreve yacute -20
+KPX abreve ydieresis -20
+KPX acircumflex g -10
+KPX acircumflex gbreve -10
+KPX acircumflex gcommaaccent -10
+KPX acircumflex v -15
+KPX acircumflex w -15
+KPX acircumflex y -20
+KPX acircumflex yacute -20
+KPX acircumflex ydieresis -20
+KPX adieresis g -10
+KPX adieresis gbreve -10
+KPX adieresis gcommaaccent -10
+KPX adieresis v -15
+KPX adieresis w -15
+KPX adieresis y -20
+KPX adieresis yacute -20
+KPX adieresis ydieresis -20
+KPX agrave g -10
+KPX agrave gbreve -10
+KPX agrave gcommaaccent -10
+KPX agrave v -15
+KPX agrave w -15
+KPX agrave y -20
+KPX agrave yacute -20
+KPX agrave ydieresis -20
+KPX amacron g -10
+KPX amacron gbreve -10
+KPX amacron gcommaaccent -10
+KPX amacron v -15
+KPX amacron w -15
+KPX amacron y -20
+KPX amacron yacute -20
+KPX amacron ydieresis -20
+KPX aogonek g -10
+KPX aogonek gbreve -10
+KPX aogonek gcommaaccent -10
+KPX aogonek v -15
+KPX aogonek w -15
+KPX aogonek y -20
+KPX aogonek yacute -20
+KPX aogonek ydieresis -20
+KPX aring g -10
+KPX aring gbreve -10
+KPX aring gcommaaccent -10
+KPX aring v -15
+KPX aring w -15
+KPX aring y -20
+KPX aring yacute -20
+KPX aring ydieresis -20
+KPX atilde g -10
+KPX atilde gbreve -10
+KPX atilde gcommaaccent -10
+KPX atilde v -15
+KPX atilde w -15
+KPX atilde y -20
+KPX atilde yacute -20
+KPX atilde ydieresis -20
+KPX b l -10
+KPX b lacute -10
+KPX b lcommaaccent -10
+KPX b lslash -10
+KPX b u -20
+KPX b uacute -20
+KPX b ucircumflex -20
+KPX b udieresis -20
+KPX b ugrave -20
+KPX b uhungarumlaut -20
+KPX b umacron -20
+KPX b uogonek -20
+KPX b uring -20
+KPX b v -20
+KPX b y -20
+KPX b yacute -20
+KPX b ydieresis -20
+KPX c h -10
+KPX c k -20
+KPX c kcommaaccent -20
+KPX c l -20
+KPX c lacute -20
+KPX c lcommaaccent -20
+KPX c lslash -20
+KPX c y -10
+KPX c yacute -10
+KPX c ydieresis -10
+KPX cacute h -10
+KPX cacute k -20
+KPX cacute kcommaaccent -20
+KPX cacute l -20
+KPX cacute lacute -20
+KPX cacute lcommaaccent -20
+KPX cacute lslash -20
+KPX cacute y -10
+KPX cacute yacute -10
+KPX cacute ydieresis -10
+KPX ccaron h -10
+KPX ccaron k -20
+KPX ccaron kcommaaccent -20
+KPX ccaron l -20
+KPX ccaron lacute -20
+KPX ccaron lcommaaccent -20
+KPX ccaron lslash -20
+KPX ccaron y -10
+KPX ccaron yacute -10
+KPX ccaron ydieresis -10
+KPX ccedilla h -10
+KPX ccedilla k -20
+KPX ccedilla kcommaaccent -20
+KPX ccedilla l -20
+KPX ccedilla lacute -20
+KPX ccedilla lcommaaccent -20
+KPX ccedilla lslash -20
+KPX ccedilla y -10
+KPX ccedilla yacute -10
+KPX ccedilla ydieresis -10
+KPX colon space -40
+KPX comma quotedblright -120
+KPX comma quoteright -120
+KPX comma space -40
+KPX d d -10
+KPX d dcroat -10
+KPX d v -15
+KPX d w -15
+KPX d y -15
+KPX d yacute -15
+KPX d ydieresis -15
+KPX dcroat d -10
+KPX dcroat dcroat -10
+KPX dcroat v -15
+KPX dcroat w -15
+KPX dcroat y -15
+KPX dcroat yacute -15
+KPX dcroat ydieresis -15
+KPX e comma 10
+KPX e period 20
+KPX e v -15
+KPX e w -15
+KPX e x -15
+KPX e y -15
+KPX e yacute -15
+KPX e ydieresis -15
+KPX eacute comma 10
+KPX eacute period 20
+KPX eacute v -15
+KPX eacute w -15
+KPX eacute x -15
+KPX eacute y -15
+KPX eacute yacute -15
+KPX eacute ydieresis -15
+KPX ecaron comma 10
+KPX ecaron period 20
+KPX ecaron v -15
+KPX ecaron w -15
+KPX ecaron x -15
+KPX ecaron y -15
+KPX ecaron yacute -15
+KPX ecaron ydieresis -15
+KPX ecircumflex comma 10
+KPX ecircumflex period 20
+KPX ecircumflex v -15
+KPX ecircumflex w -15
+KPX ecircumflex x -15
+KPX ecircumflex y -15
+KPX ecircumflex yacute -15
+KPX ecircumflex ydieresis -15
+KPX edieresis comma 10
+KPX edieresis period 20
+KPX edieresis v -15
+KPX edieresis w -15
+KPX edieresis x -15
+KPX edieresis y -15
+KPX edieresis yacute -15
+KPX edieresis ydieresis -15
+KPX edotaccent comma 10
+KPX edotaccent period 20
+KPX edotaccent v -15
+KPX edotaccent w -15
+KPX edotaccent x -15
+KPX edotaccent y -15
+KPX edotaccent yacute -15
+KPX edotaccent ydieresis -15
+KPX egrave comma 10
+KPX egrave period 20
+KPX egrave v -15
+KPX egrave w -15
+KPX egrave x -15
+KPX egrave y -15
+KPX egrave yacute -15
+KPX egrave ydieresis -15
+KPX emacron comma 10
+KPX emacron period 20
+KPX emacron v -15
+KPX emacron w -15
+KPX emacron x -15
+KPX emacron y -15
+KPX emacron yacute -15
+KPX emacron ydieresis -15
+KPX eogonek comma 10
+KPX eogonek period 20
+KPX eogonek v -15
+KPX eogonek w -15
+KPX eogonek x -15
+KPX eogonek y -15
+KPX eogonek yacute -15
+KPX eogonek ydieresis -15
+KPX f comma -10
+KPX f e -10
+KPX f eacute -10
+KPX f ecaron -10
+KPX f ecircumflex -10
+KPX f edieresis -10
+KPX f edotaccent -10
+KPX f egrave -10
+KPX f emacron -10
+KPX f eogonek -10
+KPX f o -20
+KPX f oacute -20
+KPX f ocircumflex -20
+KPX f odieresis -20
+KPX f ograve -20
+KPX f ohungarumlaut -20
+KPX f omacron -20
+KPX f oslash -20
+KPX f otilde -20
+KPX f period -10
+KPX f quotedblright 30
+KPX f quoteright 30
+KPX g e 10
+KPX g eacute 10
+KPX g ecaron 10
+KPX g ecircumflex 10
+KPX g edieresis 10
+KPX g edotaccent 10
+KPX g egrave 10
+KPX g emacron 10
+KPX g eogonek 10
+KPX g g -10
+KPX g gbreve -10
+KPX g gcommaaccent -10
+KPX gbreve e 10
+KPX gbreve eacute 10
+KPX gbreve ecaron 10
+KPX gbreve ecircumflex 10
+KPX gbreve edieresis 10
+KPX gbreve edotaccent 10
+KPX gbreve egrave 10
+KPX gbreve emacron 10
+KPX gbreve eogonek 10
+KPX gbreve g -10
+KPX gbreve gbreve -10
+KPX gbreve gcommaaccent -10
+KPX gcommaaccent e 10
+KPX gcommaaccent eacute 10
+KPX gcommaaccent ecaron 10
+KPX gcommaaccent ecircumflex 10
+KPX gcommaaccent edieresis 10
+KPX gcommaaccent edotaccent 10
+KPX gcommaaccent egrave 10
+KPX gcommaaccent emacron 10
+KPX gcommaaccent eogonek 10
+KPX gcommaaccent g -10
+KPX gcommaaccent gbreve -10
+KPX gcommaaccent gcommaaccent -10
+KPX h y -20
+KPX h yacute -20
+KPX h ydieresis -20
+KPX k o -15
+KPX k oacute -15
+KPX k ocircumflex -15
+KPX k odieresis -15
+KPX k ograve -15
+KPX k ohungarumlaut -15
+KPX k omacron -15
+KPX k oslash -15
+KPX k otilde -15
+KPX kcommaaccent o -15
+KPX kcommaaccent oacute -15
+KPX kcommaaccent ocircumflex -15
+KPX kcommaaccent odieresis -15
+KPX kcommaaccent ograve -15
+KPX kcommaaccent ohungarumlaut -15
+KPX kcommaaccent omacron -15
+KPX kcommaaccent oslash -15
+KPX kcommaaccent otilde -15
+KPX l w -15
+KPX l y -15
+KPX l yacute -15
+KPX l ydieresis -15
+KPX lacute w -15
+KPX lacute y -15
+KPX lacute yacute -15
+KPX lacute ydieresis -15
+KPX lcommaaccent w -15
+KPX lcommaaccent y -15
+KPX lcommaaccent yacute -15
+KPX lcommaaccent ydieresis -15
+KPX lslash w -15
+KPX lslash y -15
+KPX lslash yacute -15
+KPX lslash ydieresis -15
+KPX m u -20
+KPX m uacute -20
+KPX m ucircumflex -20
+KPX m udieresis -20
+KPX m ugrave -20
+KPX m uhungarumlaut -20
+KPX m umacron -20
+KPX m uogonek -20
+KPX m uring -20
+KPX m y -30
+KPX m yacute -30
+KPX m ydieresis -30
+KPX n u -10
+KPX n uacute -10
+KPX n ucircumflex -10
+KPX n udieresis -10
+KPX n ugrave -10
+KPX n uhungarumlaut -10
+KPX n umacron -10
+KPX n uogonek -10
+KPX n uring -10
+KPX n v -40
+KPX n y -20
+KPX n yacute -20
+KPX n ydieresis -20
+KPX nacute u -10
+KPX nacute uacute -10
+KPX nacute ucircumflex -10
+KPX nacute udieresis -10
+KPX nacute ugrave -10
+KPX nacute uhungarumlaut -10
+KPX nacute umacron -10
+KPX nacute uogonek -10
+KPX nacute uring -10
+KPX nacute v -40
+KPX nacute y -20
+KPX nacute yacute -20
+KPX nacute ydieresis -20
+KPX ncaron u -10
+KPX ncaron uacute -10
+KPX ncaron ucircumflex -10
+KPX ncaron udieresis -10
+KPX ncaron ugrave -10
+KPX ncaron uhungarumlaut -10
+KPX ncaron umacron -10
+KPX ncaron uogonek -10
+KPX ncaron uring -10
+KPX ncaron v -40
+KPX ncaron y -20
+KPX ncaron yacute -20
+KPX ncaron ydieresis -20
+KPX ncommaaccent u -10
+KPX ncommaaccent uacute -10
+KPX ncommaaccent ucircumflex -10
+KPX ncommaaccent udieresis -10
+KPX ncommaaccent ugrave -10
+KPX ncommaaccent uhungarumlaut -10
+KPX ncommaaccent umacron -10
+KPX ncommaaccent uogonek -10
+KPX ncommaaccent uring -10
+KPX ncommaaccent v -40
+KPX ncommaaccent y -20
+KPX ncommaaccent yacute -20
+KPX ncommaaccent ydieresis -20
+KPX ntilde u -10
+KPX ntilde uacute -10
+KPX ntilde ucircumflex -10
+KPX ntilde udieresis -10
+KPX ntilde ugrave -10
+KPX ntilde uhungarumlaut -10
+KPX ntilde umacron -10
+KPX ntilde uogonek -10
+KPX ntilde uring -10
+KPX ntilde v -40
+KPX ntilde y -20
+KPX ntilde yacute -20
+KPX ntilde ydieresis -20
+KPX o v -20
+KPX o w -15
+KPX o x -30
+KPX o y -20
+KPX o yacute -20
+KPX o ydieresis -20
+KPX oacute v -20
+KPX oacute w -15
+KPX oacute x -30
+KPX oacute y -20
+KPX oacute yacute -20
+KPX oacute ydieresis -20
+KPX ocircumflex v -20
+KPX ocircumflex w -15
+KPX ocircumflex x -30
+KPX ocircumflex y -20
+KPX ocircumflex yacute -20
+KPX ocircumflex ydieresis -20
+KPX odieresis v -20
+KPX odieresis w -15
+KPX odieresis x -30
+KPX odieresis y -20
+KPX odieresis yacute -20
+KPX odieresis ydieresis -20
+KPX ograve v -20
+KPX ograve w -15
+KPX ograve x -30
+KPX ograve y -20
+KPX ograve yacute -20
+KPX ograve ydieresis -20
+KPX ohungarumlaut v -20
+KPX ohungarumlaut w -15
+KPX ohungarumlaut x -30
+KPX ohungarumlaut y -20
+KPX ohungarumlaut yacute -20
+KPX ohungarumlaut ydieresis -20
+KPX omacron v -20
+KPX omacron w -15
+KPX omacron x -30
+KPX omacron y -20
+KPX omacron yacute -20
+KPX omacron ydieresis -20
+KPX oslash v -20
+KPX oslash w -15
+KPX oslash x -30
+KPX oslash y -20
+KPX oslash yacute -20
+KPX oslash ydieresis -20
+KPX otilde v -20
+KPX otilde w -15
+KPX otilde x -30
+KPX otilde y -20
+KPX otilde yacute -20
+KPX otilde ydieresis -20
+KPX p y -15
+KPX p yacute -15
+KPX p ydieresis -15
+KPX period quotedblright -120
+KPX period quoteright -120
+KPX period space -40
+KPX quotedblright space -80
+KPX quoteleft quoteleft -46
+KPX quoteright d -80
+KPX quoteright dcroat -80
+KPX quoteright l -20
+KPX quoteright lacute -20
+KPX quoteright lcommaaccent -20
+KPX quoteright lslash -20
+KPX quoteright quoteright -46
+KPX quoteright r -40
+KPX quoteright racute -40
+KPX quoteright rcaron -40
+KPX quoteright rcommaaccent -40
+KPX quoteright s -60
+KPX quoteright sacute -60
+KPX quoteright scaron -60
+KPX quoteright scedilla -60
+KPX quoteright scommaaccent -60
+KPX quoteright space -80
+KPX quoteright v -20
+KPX r c -20
+KPX r cacute -20
+KPX r ccaron -20
+KPX r ccedilla -20
+KPX r comma -60
+KPX r d -20
+KPX r dcroat -20
+KPX r g -15
+KPX r gbreve -15
+KPX r gcommaaccent -15
+KPX r hyphen -20
+KPX r o -20
+KPX r oacute -20
+KPX r ocircumflex -20
+KPX r odieresis -20
+KPX r ograve -20
+KPX r ohungarumlaut -20
+KPX r omacron -20
+KPX r oslash -20
+KPX r otilde -20
+KPX r period -60
+KPX r q -20
+KPX r s -15
+KPX r sacute -15
+KPX r scaron -15
+KPX r scedilla -15
+KPX r scommaaccent -15
+KPX r t 20
+KPX r tcommaaccent 20
+KPX r v 10
+KPX r y 10
+KPX r yacute 10
+KPX r ydieresis 10
+KPX racute c -20
+KPX racute cacute -20
+KPX racute ccaron -20
+KPX racute ccedilla -20
+KPX racute comma -60
+KPX racute d -20
+KPX racute dcroat -20
+KPX racute g -15
+KPX racute gbreve -15
+KPX racute gcommaaccent -15
+KPX racute hyphen -20
+KPX racute o -20
+KPX racute oacute -20
+KPX racute ocircumflex -20
+KPX racute odieresis -20
+KPX racute ograve -20
+KPX racute ohungarumlaut -20
+KPX racute omacron -20
+KPX racute oslash -20
+KPX racute otilde -20
+KPX racute period -60
+KPX racute q -20
+KPX racute s -15
+KPX racute sacute -15
+KPX racute scaron -15
+KPX racute scedilla -15
+KPX racute scommaaccent -15
+KPX racute t 20
+KPX racute tcommaaccent 20
+KPX racute v 10
+KPX racute y 10
+KPX racute yacute 10
+KPX racute ydieresis 10
+KPX rcaron c -20
+KPX rcaron cacute -20
+KPX rcaron ccaron -20
+KPX rcaron ccedilla -20
+KPX rcaron comma -60
+KPX rcaron d -20
+KPX rcaron dcroat -20
+KPX rcaron g -15
+KPX rcaron gbreve -15
+KPX rcaron gcommaaccent -15
+KPX rcaron hyphen -20
+KPX rcaron o -20
+KPX rcaron oacute -20
+KPX rcaron ocircumflex -20
+KPX rcaron odieresis -20
+KPX rcaron ograve -20
+KPX rcaron ohungarumlaut -20
+KPX rcaron omacron -20
+KPX rcaron oslash -20
+KPX rcaron otilde -20
+KPX rcaron period -60
+KPX rcaron q -20
+KPX rcaron s -15
+KPX rcaron sacute -15
+KPX rcaron scaron -15
+KPX rcaron scedilla -15
+KPX rcaron scommaaccent -15
+KPX rcaron t 20
+KPX rcaron tcommaaccent 20
+KPX rcaron v 10
+KPX rcaron y 10
+KPX rcaron yacute 10
+KPX rcaron ydieresis 10
+KPX rcommaaccent c -20
+KPX rcommaaccent cacute -20
+KPX rcommaaccent ccaron -20
+KPX rcommaaccent ccedilla -20
+KPX rcommaaccent comma -60
+KPX rcommaaccent d -20
+KPX rcommaaccent dcroat -20
+KPX rcommaaccent g -15
+KPX rcommaaccent gbreve -15
+KPX rcommaaccent gcommaaccent -15
+KPX rcommaaccent hyphen -20
+KPX rcommaaccent o -20
+KPX rcommaaccent oacute -20
+KPX rcommaaccent ocircumflex -20
+KPX rcommaaccent odieresis -20
+KPX rcommaaccent ograve -20
+KPX rcommaaccent ohungarumlaut -20
+KPX rcommaaccent omacron -20
+KPX rcommaaccent oslash -20
+KPX rcommaaccent otilde -20
+KPX rcommaaccent period -60
+KPX rcommaaccent q -20
+KPX rcommaaccent s -15
+KPX rcommaaccent sacute -15
+KPX rcommaaccent scaron -15
+KPX rcommaaccent scedilla -15
+KPX rcommaaccent scommaaccent -15
+KPX rcommaaccent t 20
+KPX rcommaaccent tcommaaccent 20
+KPX rcommaaccent v 10
+KPX rcommaaccent y 10
+KPX rcommaaccent yacute 10
+KPX rcommaaccent ydieresis 10
+KPX s w -15
+KPX sacute w -15
+KPX scaron w -15
+KPX scedilla w -15
+KPX scommaaccent w -15
+KPX semicolon space -40
+KPX space T -100
+KPX space Tcaron -100
+KPX space Tcommaaccent -100
+KPX space V -80
+KPX space W -80
+KPX space Y -120
+KPX space Yacute -120
+KPX space Ydieresis -120
+KPX space quotedblleft -80
+KPX space quoteleft -60
+KPX v a -20
+KPX v aacute -20
+KPX v abreve -20
+KPX v acircumflex -20
+KPX v adieresis -20
+KPX v agrave -20
+KPX v amacron -20
+KPX v aogonek -20
+KPX v aring -20
+KPX v atilde -20
+KPX v comma -80
+KPX v o -30
+KPX v oacute -30
+KPX v ocircumflex -30
+KPX v odieresis -30
+KPX v ograve -30
+KPX v ohungarumlaut -30
+KPX v omacron -30
+KPX v oslash -30
+KPX v otilde -30
+KPX v period -80
+KPX w comma -40
+KPX w o -20
+KPX w oacute -20
+KPX w ocircumflex -20
+KPX w odieresis -20
+KPX w ograve -20
+KPX w ohungarumlaut -20
+KPX w omacron -20
+KPX w oslash -20
+KPX w otilde -20
+KPX w period -40
+KPX x e -10
+KPX x eacute -10
+KPX x ecaron -10
+KPX x ecircumflex -10
+KPX x edieresis -10
+KPX x edotaccent -10
+KPX x egrave -10
+KPX x emacron -10
+KPX x eogonek -10
+KPX y a -30
+KPX y aacute -30
+KPX y abreve -30
+KPX y acircumflex -30
+KPX y adieresis -30
+KPX y agrave -30
+KPX y amacron -30
+KPX y aogonek -30
+KPX y aring -30
+KPX y atilde -30
+KPX y comma -80
+KPX y e -10
+KPX y eacute -10
+KPX y ecaron -10
+KPX y ecircumflex -10
+KPX y edieresis -10
+KPX y edotaccent -10
+KPX y egrave -10
+KPX y emacron -10
+KPX y eogonek -10
+KPX y o -25
+KPX y oacute -25
+KPX y ocircumflex -25
+KPX y odieresis -25
+KPX y ograve -25
+KPX y ohungarumlaut -25
+KPX y omacron -25
+KPX y oslash -25
+KPX y otilde -25
+KPX y period -80
+KPX yacute a -30
+KPX yacute aacute -30
+KPX yacute abreve -30
+KPX yacute acircumflex -30
+KPX yacute adieresis -30
+KPX yacute agrave -30
+KPX yacute amacron -30
+KPX yacute aogonek -30
+KPX yacute aring -30
+KPX yacute atilde -30
+KPX yacute comma -80
+KPX yacute e -10
+KPX yacute eacute -10
+KPX yacute ecaron -10
+KPX yacute ecircumflex -10
+KPX yacute edieresis -10
+KPX yacute edotaccent -10
+KPX yacute egrave -10
+KPX yacute emacron -10
+KPX yacute eogonek -10
+KPX yacute o -25
+KPX yacute oacute -25
+KPX yacute ocircumflex -25
+KPX yacute odieresis -25
+KPX yacute ograve -25
+KPX yacute ohungarumlaut -25
+KPX yacute omacron -25
+KPX yacute oslash -25
+KPX yacute otilde -25
+KPX yacute period -80
+KPX ydieresis a -30
+KPX ydieresis aacute -30
+KPX ydieresis abreve -30
+KPX ydieresis acircumflex -30
+KPX ydieresis adieresis -30
+KPX ydieresis agrave -30
+KPX ydieresis amacron -30
+KPX ydieresis aogonek -30
+KPX ydieresis aring -30
+KPX ydieresis atilde -30
+KPX ydieresis comma -80
+KPX ydieresis e -10
+KPX ydieresis eacute -10
+KPX ydieresis ecaron -10
+KPX ydieresis ecircumflex -10
+KPX ydieresis edieresis -10
+KPX ydieresis edotaccent -10
+KPX ydieresis egrave -10
+KPX ydieresis emacron -10
+KPX ydieresis eogonek -10
+KPX ydieresis o -25
+KPX ydieresis oacute -25
+KPX ydieresis ocircumflex -25
+KPX ydieresis odieresis -25
+KPX ydieresis ograve -25
+KPX ydieresis ohungarumlaut -25
+KPX ydieresis omacron -25
+KPX ydieresis oslash -25
+KPX ydieresis otilde -25
+KPX ydieresis period -80
+KPX z e 10
+KPX z eacute 10
+KPX z ecaron 10
+KPX z ecircumflex 10
+KPX z edieresis 10
+KPX z edotaccent 10
+KPX z egrave 10
+KPX z emacron 10
+KPX z eogonek 10
+KPX zacute e 10
+KPX zacute eacute 10
+KPX zacute ecaron 10
+KPX zacute ecircumflex 10
+KPX zacute edieresis 10
+KPX zacute edotaccent 10
+KPX zacute egrave 10
+KPX zacute emacron 10
+KPX zacute eogonek 10
+KPX zcaron e 10
+KPX zcaron eacute 10
+KPX zcaron ecaron 10
+KPX zcaron ecircumflex 10
+KPX zcaron edieresis 10
+KPX zcaron edotaccent 10
+KPX zcaron egrave 10
+KPX zcaron emacron 10
+KPX zcaron eogonek 10
+KPX zdotaccent e 10
+KPX zdotaccent eacute 10
+KPX zdotaccent ecaron 10
+KPX zdotaccent ecircumflex 10
+KPX zdotaccent edieresis 10
+KPX zdotaccent edotaccent 10
+KPX zdotaccent egrave 10
+KPX zdotaccent emacron 10
+KPX zdotaccent eogonek 10
+EndKernPairs
+EndKernData
+EndFontMetrics
diff --git a/program/libraries/afm/Helvetica-BoldOblique.afm b/program/libraries/afm/Helvetica-BoldOblique.afm
--- /dev/null
@@ -0,0 +1,2827 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.
+Comment Creation Date: Thu May 1 12:45:12 1997
+Comment UniqueID 43053
+Comment VMusage 14482 68586
+FontName Helvetica-BoldOblique
+FullName Helvetica Bold Oblique
+FamilyName Helvetica
+Weight Bold
+ItalicAngle -12
+IsFixedPitch false
+CharacterSet ExtendedRoman
+FontBBox -174 -228 1114 962
+UnderlinePosition -100
+UnderlineThickness 50
+Version 002.000
+Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries.
+EncodingScheme AdobeStandardEncoding
+CapHeight 718
+XHeight 532
+Ascender 718
+Descender -207
+StdHW 118
+StdVW 140
+StartCharMetrics 315
+C 32 ; WX 278 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 333 ; N exclam ; B 94 0 397 718 ;
+C 34 ; WX 474 ; N quotedbl ; B 193 447 529 718 ;
+C 35 ; WX 556 ; N numbersign ; B 60 0 644 698 ;
+C 36 ; WX 556 ; N dollar ; B 67 -115 622 775 ;
+C 37 ; WX 889 ; N percent ; B 136 -19 901 710 ;
+C 38 ; WX 722 ; N ampersand ; B 89 -19 732 718 ;
+C 39 ; WX 278 ; N quoteright ; B 167 445 362 718 ;
+C 40 ; WX 333 ; N parenleft ; B 76 -208 470 734 ;
+C 41 ; WX 333 ; N parenright ; B -25 -208 369 734 ;
+C 42 ; WX 389 ; N asterisk ; B 146 387 481 718 ;
+C 43 ; WX 584 ; N plus ; B 82 0 610 506 ;
+C 44 ; WX 278 ; N comma ; B 28 -168 245 146 ;
+C 45 ; WX 333 ; N hyphen ; B 73 215 379 345 ;
+C 46 ; WX 278 ; N period ; B 64 0 245 146 ;
+C 47 ; WX 278 ; N slash ; B -37 -19 468 737 ;
+C 48 ; WX 556 ; N zero ; B 86 -19 617 710 ;
+C 49 ; WX 556 ; N one ; B 173 0 529 710 ;
+C 50 ; WX 556 ; N two ; B 26 0 619 710 ;
+C 51 ; WX 556 ; N three ; B 65 -19 608 710 ;
+C 52 ; WX 556 ; N four ; B 60 0 598 710 ;
+C 53 ; WX 556 ; N five ; B 64 -19 636 698 ;
+C 54 ; WX 556 ; N six ; B 85 -19 619 710 ;
+C 55 ; WX 556 ; N seven ; B 125 0 676 698 ;
+C 56 ; WX 556 ; N eight ; B 69 -19 616 710 ;
+C 57 ; WX 556 ; N nine ; B 78 -19 615 710 ;
+C 58 ; WX 333 ; N colon ; B 92 0 351 512 ;
+C 59 ; WX 333 ; N semicolon ; B 56 -168 351 512 ;
+C 60 ; WX 584 ; N less ; B 82 -8 655 514 ;
+C 61 ; WX 584 ; N equal ; B 58 87 633 419 ;
+C 62 ; WX 584 ; N greater ; B 36 -8 609 514 ;
+C 63 ; WX 611 ; N question ; B 165 0 671 727 ;
+C 64 ; WX 975 ; N at ; B 186 -19 954 737 ;
+C 65 ; WX 722 ; N A ; B 20 0 702 718 ;
+C 66 ; WX 722 ; N B ; B 76 0 764 718 ;
+C 67 ; WX 722 ; N C ; B 107 -19 789 737 ;
+C 68 ; WX 722 ; N D ; B 76 0 777 718 ;
+C 69 ; WX 667 ; N E ; B 76 0 757 718 ;
+C 70 ; WX 611 ; N F ; B 76 0 740 718 ;
+C 71 ; WX 778 ; N G ; B 108 -19 817 737 ;
+C 72 ; WX 722 ; N H ; B 71 0 804 718 ;
+C 73 ; WX 278 ; N I ; B 64 0 367 718 ;
+C 74 ; WX 556 ; N J ; B 60 -18 637 718 ;
+C 75 ; WX 722 ; N K ; B 87 0 858 718 ;
+C 76 ; WX 611 ; N L ; B 76 0 611 718 ;
+C 77 ; WX 833 ; N M ; B 69 0 918 718 ;
+C 78 ; WX 722 ; N N ; B 69 0 807 718 ;
+C 79 ; WX 778 ; N O ; B 107 -19 823 737 ;
+C 80 ; WX 667 ; N P ; B 76 0 738 718 ;
+C 81 ; WX 778 ; N Q ; B 107 -52 823 737 ;
+C 82 ; WX 722 ; N R ; B 76 0 778 718 ;
+C 83 ; WX 667 ; N S ; B 81 -19 718 737 ;
+C 84 ; WX 611 ; N T ; B 140 0 751 718 ;
+C 85 ; WX 722 ; N U ; B 116 -19 804 718 ;
+C 86 ; WX 667 ; N V ; B 172 0 801 718 ;
+C 87 ; WX 944 ; N W ; B 169 0 1082 718 ;
+C 88 ; WX 667 ; N X ; B 14 0 791 718 ;
+C 89 ; WX 667 ; N Y ; B 168 0 806 718 ;
+C 90 ; WX 611 ; N Z ; B 25 0 737 718 ;
+C 91 ; WX 333 ; N bracketleft ; B 21 -196 462 722 ;
+C 92 ; WX 278 ; N backslash ; B 124 -19 307 737 ;
+C 93 ; WX 333 ; N bracketright ; B -18 -196 423 722 ;
+C 94 ; WX 584 ; N asciicircum ; B 131 323 591 698 ;
+C 95 ; WX 556 ; N underscore ; B -27 -125 540 -75 ;
+C 96 ; WX 278 ; N quoteleft ; B 165 454 361 727 ;
+C 97 ; WX 556 ; N a ; B 55 -14 583 546 ;
+C 98 ; WX 611 ; N b ; B 61 -14 645 718 ;
+C 99 ; WX 556 ; N c ; B 79 -14 599 546 ;
+C 100 ; WX 611 ; N d ; B 82 -14 704 718 ;
+C 101 ; WX 556 ; N e ; B 70 -14 593 546 ;
+C 102 ; WX 333 ; N f ; B 87 0 469 727 ; L i fi ; L l fl ;
+C 103 ; WX 611 ; N g ; B 38 -217 666 546 ;
+C 104 ; WX 611 ; N h ; B 65 0 629 718 ;
+C 105 ; WX 278 ; N i ; B 69 0 363 725 ;
+C 106 ; WX 278 ; N j ; B -42 -214 363 725 ;
+C 107 ; WX 556 ; N k ; B 69 0 670 718 ;
+C 108 ; WX 278 ; N l ; B 69 0 362 718 ;
+C 109 ; WX 889 ; N m ; B 64 0 909 546 ;
+C 110 ; WX 611 ; N n ; B 65 0 629 546 ;
+C 111 ; WX 611 ; N o ; B 82 -14 643 546 ;
+C 112 ; WX 611 ; N p ; B 18 -207 645 546 ;
+C 113 ; WX 611 ; N q ; B 80 -207 665 546 ;
+C 114 ; WX 389 ; N r ; B 64 0 489 546 ;
+C 115 ; WX 556 ; N s ; B 63 -14 584 546 ;
+C 116 ; WX 333 ; N t ; B 100 -6 422 676 ;
+C 117 ; WX 611 ; N u ; B 98 -14 658 532 ;
+C 118 ; WX 556 ; N v ; B 126 0 656 532 ;
+C 119 ; WX 778 ; N w ; B 123 0 882 532 ;
+C 120 ; WX 556 ; N x ; B 15 0 648 532 ;
+C 121 ; WX 556 ; N y ; B 42 -214 652 532 ;
+C 122 ; WX 500 ; N z ; B 20 0 583 532 ;
+C 123 ; WX 389 ; N braceleft ; B 94 -196 518 722 ;
+C 124 ; WX 280 ; N bar ; B 36 -225 361 775 ;
+C 125 ; WX 389 ; N braceright ; B -18 -196 407 722 ;
+C 126 ; WX 584 ; N asciitilde ; B 115 163 577 343 ;
+C 161 ; WX 333 ; N exclamdown ; B 50 -186 353 532 ;
+C 162 ; WX 556 ; N cent ; B 79 -118 599 628 ;
+C 163 ; WX 556 ; N sterling ; B 50 -16 635 718 ;
+C 164 ; WX 167 ; N fraction ; B -174 -19 487 710 ;
+C 165 ; WX 556 ; N yen ; B 60 0 713 698 ;
+C 166 ; WX 556 ; N florin ; B -50 -210 669 737 ;
+C 167 ; WX 556 ; N section ; B 61 -184 598 727 ;
+C 168 ; WX 556 ; N currency ; B 27 76 680 636 ;
+C 169 ; WX 238 ; N quotesingle ; B 165 447 321 718 ;
+C 170 ; WX 500 ; N quotedblleft ; B 160 454 588 727 ;
+C 171 ; WX 556 ; N guillemotleft ; B 135 76 571 484 ;
+C 172 ; WX 333 ; N guilsinglleft ; B 130 76 353 484 ;
+C 173 ; WX 333 ; N guilsinglright ; B 99 76 322 484 ;
+C 174 ; WX 611 ; N fi ; B 87 0 696 727 ;
+C 175 ; WX 611 ; N fl ; B 87 0 695 727 ;
+C 177 ; WX 556 ; N endash ; B 48 227 627 333 ;
+C 178 ; WX 556 ; N dagger ; B 118 -171 626 718 ;
+C 179 ; WX 556 ; N daggerdbl ; B 46 -171 628 718 ;
+C 180 ; WX 278 ; N periodcentered ; B 110 172 276 334 ;
+C 182 ; WX 556 ; N paragraph ; B 98 -191 688 700 ;
+C 183 ; WX 350 ; N bullet ; B 83 194 420 524 ;
+C 184 ; WX 278 ; N quotesinglbase ; B 41 -146 236 127 ;
+C 185 ; WX 500 ; N quotedblbase ; B 36 -146 463 127 ;
+C 186 ; WX 500 ; N quotedblright ; B 162 445 589 718 ;
+C 187 ; WX 556 ; N guillemotright ; B 104 76 540 484 ;
+C 188 ; WX 1000 ; N ellipsis ; B 92 0 939 146 ;
+C 189 ; WX 1000 ; N perthousand ; B 76 -19 1038 710 ;
+C 191 ; WX 611 ; N questiondown ; B 53 -195 559 532 ;
+C 193 ; WX 333 ; N grave ; B 136 604 353 750 ;
+C 194 ; WX 333 ; N acute ; B 236 604 515 750 ;
+C 195 ; WX 333 ; N circumflex ; B 118 604 471 750 ;
+C 196 ; WX 333 ; N tilde ; B 113 610 507 737 ;
+C 197 ; WX 333 ; N macron ; B 122 604 483 678 ;
+C 198 ; WX 333 ; N breve ; B 156 604 494 750 ;
+C 199 ; WX 333 ; N dotaccent ; B 235 614 385 729 ;
+C 200 ; WX 333 ; N dieresis ; B 137 614 482 729 ;
+C 202 ; WX 333 ; N ring ; B 200 568 420 776 ;
+C 203 ; WX 333 ; N cedilla ; B -37 -228 220 0 ;
+C 205 ; WX 333 ; N hungarumlaut ; B 137 604 645 750 ;
+C 206 ; WX 333 ; N ogonek ; B 41 -228 264 0 ;
+C 207 ; WX 333 ; N caron ; B 149 604 502 750 ;
+C 208 ; WX 1000 ; N emdash ; B 48 227 1071 333 ;
+C 225 ; WX 1000 ; N AE ; B 5 0 1100 718 ;
+C 227 ; WX 370 ; N ordfeminine ; B 125 401 465 737 ;
+C 232 ; WX 611 ; N Lslash ; B 34 0 611 718 ;
+C 233 ; WX 778 ; N Oslash ; B 35 -27 894 745 ;
+C 234 ; WX 1000 ; N OE ; B 99 -19 1114 737 ;
+C 235 ; WX 365 ; N ordmasculine ; B 123 401 485 737 ;
+C 241 ; WX 889 ; N ae ; B 56 -14 923 546 ;
+C 245 ; WX 278 ; N dotlessi ; B 69 0 322 532 ;
+C 248 ; WX 278 ; N lslash ; B 40 0 407 718 ;
+C 249 ; WX 611 ; N oslash ; B 22 -29 701 560 ;
+C 250 ; WX 944 ; N oe ; B 82 -14 977 546 ;
+C 251 ; WX 611 ; N germandbls ; B 69 -14 657 731 ;
+C -1 ; WX 278 ; N Idieresis ; B 64 0 494 915 ;
+C -1 ; WX 556 ; N eacute ; B 70 -14 627 750 ;
+C -1 ; WX 556 ; N abreve ; B 55 -14 606 750 ;
+C -1 ; WX 611 ; N uhungarumlaut ; B 98 -14 784 750 ;
+C -1 ; WX 556 ; N ecaron ; B 70 -14 614 750 ;
+C -1 ; WX 667 ; N Ydieresis ; B 168 0 806 915 ;
+C -1 ; WX 584 ; N divide ; B 82 -42 610 548 ;
+C -1 ; WX 667 ; N Yacute ; B 168 0 806 936 ;
+C -1 ; WX 722 ; N Acircumflex ; B 20 0 706 936 ;
+C -1 ; WX 556 ; N aacute ; B 55 -14 627 750 ;
+C -1 ; WX 722 ; N Ucircumflex ; B 116 -19 804 936 ;
+C -1 ; WX 556 ; N yacute ; B 42 -214 652 750 ;
+C -1 ; WX 556 ; N scommaaccent ; B 63 -228 584 546 ;
+C -1 ; WX 556 ; N ecircumflex ; B 70 -14 593 750 ;
+C -1 ; WX 722 ; N Uring ; B 116 -19 804 962 ;
+C -1 ; WX 722 ; N Udieresis ; B 116 -19 804 915 ;
+C -1 ; WX 556 ; N aogonek ; B 55 -224 583 546 ;
+C -1 ; WX 722 ; N Uacute ; B 116 -19 804 936 ;
+C -1 ; WX 611 ; N uogonek ; B 98 -228 658 532 ;
+C -1 ; WX 667 ; N Edieresis ; B 76 0 757 915 ;
+C -1 ; WX 722 ; N Dcroat ; B 62 0 777 718 ;
+C -1 ; WX 250 ; N commaaccent ; B 16 -228 188 -50 ;
+C -1 ; WX 737 ; N copyright ; B 56 -19 835 737 ;
+C -1 ; WX 667 ; N Emacron ; B 76 0 757 864 ;
+C -1 ; WX 556 ; N ccaron ; B 79 -14 614 750 ;
+C -1 ; WX 556 ; N aring ; B 55 -14 583 776 ;
+C -1 ; WX 722 ; N Ncommaaccent ; B 69 -228 807 718 ;
+C -1 ; WX 278 ; N lacute ; B 69 0 528 936 ;
+C -1 ; WX 556 ; N agrave ; B 55 -14 583 750 ;
+C -1 ; WX 611 ; N Tcommaaccent ; B 140 -228 751 718 ;
+C -1 ; WX 722 ; N Cacute ; B 107 -19 789 936 ;
+C -1 ; WX 556 ; N atilde ; B 55 -14 619 737 ;
+C -1 ; WX 667 ; N Edotaccent ; B 76 0 757 915 ;
+C -1 ; WX 556 ; N scaron ; B 63 -14 614 750 ;
+C -1 ; WX 556 ; N scedilla ; B 63 -228 584 546 ;
+C -1 ; WX 278 ; N iacute ; B 69 0 488 750 ;
+C -1 ; WX 494 ; N lozenge ; B 90 0 564 745 ;
+C -1 ; WX 722 ; N Rcaron ; B 76 0 778 936 ;
+C -1 ; WX 778 ; N Gcommaaccent ; B 108 -228 817 737 ;
+C -1 ; WX 611 ; N ucircumflex ; B 98 -14 658 750 ;
+C -1 ; WX 556 ; N acircumflex ; B 55 -14 583 750 ;
+C -1 ; WX 722 ; N Amacron ; B 20 0 718 864 ;
+C -1 ; WX 389 ; N rcaron ; B 64 0 530 750 ;
+C -1 ; WX 556 ; N ccedilla ; B 79 -228 599 546 ;
+C -1 ; WX 611 ; N Zdotaccent ; B 25 0 737 915 ;
+C -1 ; WX 667 ; N Thorn ; B 76 0 716 718 ;
+C -1 ; WX 778 ; N Omacron ; B 107 -19 823 864 ;
+C -1 ; WX 722 ; N Racute ; B 76 0 778 936 ;
+C -1 ; WX 667 ; N Sacute ; B 81 -19 722 936 ;
+C -1 ; WX 743 ; N dcaron ; B 82 -14 903 718 ;
+C -1 ; WX 722 ; N Umacron ; B 116 -19 804 864 ;
+C -1 ; WX 611 ; N uring ; B 98 -14 658 776 ;
+C -1 ; WX 333 ; N threesuperior ; B 91 271 441 710 ;
+C -1 ; WX 778 ; N Ograve ; B 107 -19 823 936 ;
+C -1 ; WX 722 ; N Agrave ; B 20 0 702 936 ;
+C -1 ; WX 722 ; N Abreve ; B 20 0 729 936 ;
+C -1 ; WX 584 ; N multiply ; B 57 1 635 505 ;
+C -1 ; WX 611 ; N uacute ; B 98 -14 658 750 ;
+C -1 ; WX 611 ; N Tcaron ; B 140 0 751 936 ;
+C -1 ; WX 494 ; N partialdiff ; B 43 -21 585 750 ;
+C -1 ; WX 556 ; N ydieresis ; B 42 -214 652 729 ;
+C -1 ; WX 722 ; N Nacute ; B 69 0 807 936 ;
+C -1 ; WX 278 ; N icircumflex ; B 69 0 444 750 ;
+C -1 ; WX 667 ; N Ecircumflex ; B 76 0 757 936 ;
+C -1 ; WX 556 ; N adieresis ; B 55 -14 594 729 ;
+C -1 ; WX 556 ; N edieresis ; B 70 -14 594 729 ;
+C -1 ; WX 556 ; N cacute ; B 79 -14 627 750 ;
+C -1 ; WX 611 ; N nacute ; B 65 0 654 750 ;
+C -1 ; WX 611 ; N umacron ; B 98 -14 658 678 ;
+C -1 ; WX 722 ; N Ncaron ; B 69 0 807 936 ;
+C -1 ; WX 278 ; N Iacute ; B 64 0 528 936 ;
+C -1 ; WX 584 ; N plusminus ; B 40 0 625 506 ;
+C -1 ; WX 280 ; N brokenbar ; B 52 -150 345 700 ;
+C -1 ; WX 737 ; N registered ; B 55 -19 834 737 ;
+C -1 ; WX 778 ; N Gbreve ; B 108 -19 817 936 ;
+C -1 ; WX 278 ; N Idotaccent ; B 64 0 397 915 ;
+C -1 ; WX 600 ; N summation ; B 14 -10 670 706 ;
+C -1 ; WX 667 ; N Egrave ; B 76 0 757 936 ;
+C -1 ; WX 389 ; N racute ; B 64 0 543 750 ;
+C -1 ; WX 611 ; N omacron ; B 82 -14 643 678 ;
+C -1 ; WX 611 ; N Zacute ; B 25 0 737 936 ;
+C -1 ; WX 611 ; N Zcaron ; B 25 0 737 936 ;
+C -1 ; WX 549 ; N greaterequal ; B 26 0 629 704 ;
+C -1 ; WX 722 ; N Eth ; B 62 0 777 718 ;
+C -1 ; WX 722 ; N Ccedilla ; B 107 -228 789 737 ;
+C -1 ; WX 278 ; N lcommaaccent ; B 30 -228 362 718 ;
+C -1 ; WX 389 ; N tcaron ; B 100 -6 608 878 ;
+C -1 ; WX 556 ; N eogonek ; B 70 -228 593 546 ;
+C -1 ; WX 722 ; N Uogonek ; B 116 -228 804 718 ;
+C -1 ; WX 722 ; N Aacute ; B 20 0 750 936 ;
+C -1 ; WX 722 ; N Adieresis ; B 20 0 716 915 ;
+C -1 ; WX 556 ; N egrave ; B 70 -14 593 750 ;
+C -1 ; WX 500 ; N zacute ; B 20 0 599 750 ;
+C -1 ; WX 278 ; N iogonek ; B -14 -224 363 725 ;
+C -1 ; WX 778 ; N Oacute ; B 107 -19 823 936 ;
+C -1 ; WX 611 ; N oacute ; B 82 -14 654 750 ;
+C -1 ; WX 556 ; N amacron ; B 55 -14 595 678 ;
+C -1 ; WX 556 ; N sacute ; B 63 -14 627 750 ;
+C -1 ; WX 278 ; N idieresis ; B 69 0 455 729 ;
+C -1 ; WX 778 ; N Ocircumflex ; B 107 -19 823 936 ;
+C -1 ; WX 722 ; N Ugrave ; B 116 -19 804 936 ;
+C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ;
+C -1 ; WX 611 ; N thorn ; B 18 -208 645 718 ;
+C -1 ; WX 333 ; N twosuperior ; B 69 283 449 710 ;
+C -1 ; WX 778 ; N Odieresis ; B 107 -19 823 915 ;
+C -1 ; WX 611 ; N mu ; B 22 -207 658 532 ;
+C -1 ; WX 278 ; N igrave ; B 69 0 326 750 ;
+C -1 ; WX 611 ; N ohungarumlaut ; B 82 -14 784 750 ;
+C -1 ; WX 667 ; N Eogonek ; B 76 -224 757 718 ;
+C -1 ; WX 611 ; N dcroat ; B 82 -14 789 718 ;
+C -1 ; WX 834 ; N threequarters ; B 99 -19 839 710 ;
+C -1 ; WX 667 ; N Scedilla ; B 81 -228 718 737 ;
+C -1 ; WX 400 ; N lcaron ; B 69 0 561 718 ;
+C -1 ; WX 722 ; N Kcommaaccent ; B 87 -228 858 718 ;
+C -1 ; WX 611 ; N Lacute ; B 76 0 611 936 ;
+C -1 ; WX 1000 ; N trademark ; B 179 306 1109 718 ;
+C -1 ; WX 556 ; N edotaccent ; B 70 -14 593 729 ;
+C -1 ; WX 278 ; N Igrave ; B 64 0 367 936 ;
+C -1 ; WX 278 ; N Imacron ; B 64 0 496 864 ;
+C -1 ; WX 611 ; N Lcaron ; B 76 0 643 718 ;
+C -1 ; WX 834 ; N onehalf ; B 132 -19 858 710 ;
+C -1 ; WX 549 ; N lessequal ; B 29 0 676 704 ;
+C -1 ; WX 611 ; N ocircumflex ; B 82 -14 643 750 ;
+C -1 ; WX 611 ; N ntilde ; B 65 0 646 737 ;
+C -1 ; WX 722 ; N Uhungarumlaut ; B 116 -19 880 936 ;
+C -1 ; WX 667 ; N Eacute ; B 76 0 757 936 ;
+C -1 ; WX 556 ; N emacron ; B 70 -14 595 678 ;
+C -1 ; WX 611 ; N gbreve ; B 38 -217 666 750 ;
+C -1 ; WX 834 ; N onequarter ; B 132 -19 806 710 ;
+C -1 ; WX 667 ; N Scaron ; B 81 -19 718 936 ;
+C -1 ; WX 667 ; N Scommaaccent ; B 81 -228 718 737 ;
+C -1 ; WX 778 ; N Ohungarumlaut ; B 107 -19 908 936 ;
+C -1 ; WX 400 ; N degree ; B 175 426 467 712 ;
+C -1 ; WX 611 ; N ograve ; B 82 -14 643 750 ;
+C -1 ; WX 722 ; N Ccaron ; B 107 -19 789 936 ;
+C -1 ; WX 611 ; N ugrave ; B 98 -14 658 750 ;
+C -1 ; WX 549 ; N radical ; B 112 -46 689 850 ;
+C -1 ; WX 722 ; N Dcaron ; B 76 0 777 936 ;
+C -1 ; WX 389 ; N rcommaaccent ; B 26 -228 489 546 ;
+C -1 ; WX 722 ; N Ntilde ; B 69 0 807 923 ;
+C -1 ; WX 611 ; N otilde ; B 82 -14 646 737 ;
+C -1 ; WX 722 ; N Rcommaaccent ; B 76 -228 778 718 ;
+C -1 ; WX 611 ; N Lcommaaccent ; B 76 -228 611 718 ;
+C -1 ; WX 722 ; N Atilde ; B 20 0 741 923 ;
+C -1 ; WX 722 ; N Aogonek ; B 20 -224 702 718 ;
+C -1 ; WX 722 ; N Aring ; B 20 0 702 962 ;
+C -1 ; WX 778 ; N Otilde ; B 107 -19 823 923 ;
+C -1 ; WX 500 ; N zdotaccent ; B 20 0 583 729 ;
+C -1 ; WX 667 ; N Ecaron ; B 76 0 757 936 ;
+C -1 ; WX 278 ; N Iogonek ; B -41 -228 367 718 ;
+C -1 ; WX 556 ; N kcommaaccent ; B 69 -228 670 718 ;
+C -1 ; WX 584 ; N minus ; B 82 197 610 309 ;
+C -1 ; WX 278 ; N Icircumflex ; B 64 0 484 936 ;
+C -1 ; WX 611 ; N ncaron ; B 65 0 641 750 ;
+C -1 ; WX 333 ; N tcommaaccent ; B 58 -228 422 676 ;
+C -1 ; WX 584 ; N logicalnot ; B 105 108 633 419 ;
+C -1 ; WX 611 ; N odieresis ; B 82 -14 643 729 ;
+C -1 ; WX 611 ; N udieresis ; B 98 -14 658 729 ;
+C -1 ; WX 549 ; N notequal ; B 32 -49 630 570 ;
+C -1 ; WX 611 ; N gcommaaccent ; B 38 -217 666 850 ;
+C -1 ; WX 611 ; N eth ; B 82 -14 670 737 ;
+C -1 ; WX 500 ; N zcaron ; B 20 0 586 750 ;
+C -1 ; WX 611 ; N ncommaaccent ; B 65 -228 629 546 ;
+C -1 ; WX 333 ; N onesuperior ; B 148 283 388 710 ;
+C -1 ; WX 278 ; N imacron ; B 69 0 429 678 ;
+C -1 ; WX 556 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+StartKernData
+StartKernPairs 2481
+KPX A C -40
+KPX A Cacute -40
+KPX A Ccaron -40
+KPX A Ccedilla -40
+KPX A G -50
+KPX A Gbreve -50
+KPX A Gcommaaccent -50
+KPX A O -40
+KPX A Oacute -40
+KPX A Ocircumflex -40
+KPX A Odieresis -40
+KPX A Ograve -40
+KPX A Ohungarumlaut -40
+KPX A Omacron -40
+KPX A Oslash -40
+KPX A Otilde -40
+KPX A Q -40
+KPX A T -90
+KPX A Tcaron -90
+KPX A Tcommaaccent -90
+KPX A U -50
+KPX A Uacute -50
+KPX A Ucircumflex -50
+KPX A Udieresis -50
+KPX A Ugrave -50
+KPX A Uhungarumlaut -50
+KPX A Umacron -50
+KPX A Uogonek -50
+KPX A Uring -50
+KPX A V -80
+KPX A W -60
+KPX A Y -110
+KPX A Yacute -110
+KPX A Ydieresis -110
+KPX A u -30
+KPX A uacute -30
+KPX A ucircumflex -30
+KPX A udieresis -30
+KPX A ugrave -30
+KPX A uhungarumlaut -30
+KPX A umacron -30
+KPX A uogonek -30
+KPX A uring -30
+KPX A v -40
+KPX A w -30
+KPX A y -30
+KPX A yacute -30
+KPX A ydieresis -30
+KPX Aacute C -40
+KPX Aacute Cacute -40
+KPX Aacute Ccaron -40
+KPX Aacute Ccedilla -40
+KPX Aacute G -50
+KPX Aacute Gbreve -50
+KPX Aacute Gcommaaccent -50
+KPX Aacute O -40
+KPX Aacute Oacute -40
+KPX Aacute Ocircumflex -40
+KPX Aacute Odieresis -40
+KPX Aacute Ograve -40
+KPX Aacute Ohungarumlaut -40
+KPX Aacute Omacron -40
+KPX Aacute Oslash -40
+KPX Aacute Otilde -40
+KPX Aacute Q -40
+KPX Aacute T -90
+KPX Aacute Tcaron -90
+KPX Aacute Tcommaaccent -90
+KPX Aacute U -50
+KPX Aacute Uacute -50
+KPX Aacute Ucircumflex -50
+KPX Aacute Udieresis -50
+KPX Aacute Ugrave -50
+KPX Aacute Uhungarumlaut -50
+KPX Aacute Umacron -50
+KPX Aacute Uogonek -50
+KPX Aacute Uring -50
+KPX Aacute V -80
+KPX Aacute W -60
+KPX Aacute Y -110
+KPX Aacute Yacute -110
+KPX Aacute Ydieresis -110
+KPX Aacute u -30
+KPX Aacute uacute -30
+KPX Aacute ucircumflex -30
+KPX Aacute udieresis -30
+KPX Aacute ugrave -30
+KPX Aacute uhungarumlaut -30
+KPX Aacute umacron -30
+KPX Aacute uogonek -30
+KPX Aacute uring -30
+KPX Aacute v -40
+KPX Aacute w -30
+KPX Aacute y -30
+KPX Aacute yacute -30
+KPX Aacute ydieresis -30
+KPX Abreve C -40
+KPX Abreve Cacute -40
+KPX Abreve Ccaron -40
+KPX Abreve Ccedilla -40
+KPX Abreve G -50
+KPX Abreve Gbreve -50
+KPX Abreve Gcommaaccent -50
+KPX Abreve O -40
+KPX Abreve Oacute -40
+KPX Abreve Ocircumflex -40
+KPX Abreve Odieresis -40
+KPX Abreve Ograve -40
+KPX Abreve Ohungarumlaut -40
+KPX Abreve Omacron -40
+KPX Abreve Oslash -40
+KPX Abreve Otilde -40
+KPX Abreve Q -40
+KPX Abreve T -90
+KPX Abreve Tcaron -90
+KPX Abreve Tcommaaccent -90
+KPX Abreve U -50
+KPX Abreve Uacute -50
+KPX Abreve Ucircumflex -50
+KPX Abreve Udieresis -50
+KPX Abreve Ugrave -50
+KPX Abreve Uhungarumlaut -50
+KPX Abreve Umacron -50
+KPX Abreve Uogonek -50
+KPX Abreve Uring -50
+KPX Abreve V -80
+KPX Abreve W -60
+KPX Abreve Y -110
+KPX Abreve Yacute -110
+KPX Abreve Ydieresis -110
+KPX Abreve u -30
+KPX Abreve uacute -30
+KPX Abreve ucircumflex -30
+KPX Abreve udieresis -30
+KPX Abreve ugrave -30
+KPX Abreve uhungarumlaut -30
+KPX Abreve umacron -30
+KPX Abreve uogonek -30
+KPX Abreve uring -30
+KPX Abreve v -40
+KPX Abreve w -30
+KPX Abreve y -30
+KPX Abreve yacute -30
+KPX Abreve ydieresis -30
+KPX Acircumflex C -40
+KPX Acircumflex Cacute -40
+KPX Acircumflex Ccaron -40
+KPX Acircumflex Ccedilla -40
+KPX Acircumflex G -50
+KPX Acircumflex Gbreve -50
+KPX Acircumflex Gcommaaccent -50
+KPX Acircumflex O -40
+KPX Acircumflex Oacute -40
+KPX Acircumflex Ocircumflex -40
+KPX Acircumflex Odieresis -40
+KPX Acircumflex Ograve -40
+KPX Acircumflex Ohungarumlaut -40
+KPX Acircumflex Omacron -40
+KPX Acircumflex Oslash -40
+KPX Acircumflex Otilde -40
+KPX Acircumflex Q -40
+KPX Acircumflex T -90
+KPX Acircumflex Tcaron -90
+KPX Acircumflex Tcommaaccent -90
+KPX Acircumflex U -50
+KPX Acircumflex Uacute -50
+KPX Acircumflex Ucircumflex -50
+KPX Acircumflex Udieresis -50
+KPX Acircumflex Ugrave -50
+KPX Acircumflex Uhungarumlaut -50
+KPX Acircumflex Umacron -50
+KPX Acircumflex Uogonek -50
+KPX Acircumflex Uring -50
+KPX Acircumflex V -80
+KPX Acircumflex W -60
+KPX Acircumflex Y -110
+KPX Acircumflex Yacute -110
+KPX Acircumflex Ydieresis -110
+KPX Acircumflex u -30
+KPX Acircumflex uacute -30
+KPX Acircumflex ucircumflex -30
+KPX Acircumflex udieresis -30
+KPX Acircumflex ugrave -30
+KPX Acircumflex uhungarumlaut -30
+KPX Acircumflex umacron -30
+KPX Acircumflex uogonek -30
+KPX Acircumflex uring -30
+KPX Acircumflex v -40
+KPX Acircumflex w -30
+KPX Acircumflex y -30
+KPX Acircumflex yacute -30
+KPX Acircumflex ydieresis -30
+KPX Adieresis C -40
+KPX Adieresis Cacute -40
+KPX Adieresis Ccaron -40
+KPX Adieresis Ccedilla -40
+KPX Adieresis G -50
+KPX Adieresis Gbreve -50
+KPX Adieresis Gcommaaccent -50
+KPX Adieresis O -40
+KPX Adieresis Oacute -40
+KPX Adieresis Ocircumflex -40
+KPX Adieresis Odieresis -40
+KPX Adieresis Ograve -40
+KPX Adieresis Ohungarumlaut -40
+KPX Adieresis Omacron -40
+KPX Adieresis Oslash -40
+KPX Adieresis Otilde -40
+KPX Adieresis Q -40
+KPX Adieresis T -90
+KPX Adieresis Tcaron -90
+KPX Adieresis Tcommaaccent -90
+KPX Adieresis U -50
+KPX Adieresis Uacute -50
+KPX Adieresis Ucircumflex -50
+KPX Adieresis Udieresis -50
+KPX Adieresis Ugrave -50
+KPX Adieresis Uhungarumlaut -50
+KPX Adieresis Umacron -50
+KPX Adieresis Uogonek -50
+KPX Adieresis Uring -50
+KPX Adieresis V -80
+KPX Adieresis W -60
+KPX Adieresis Y -110
+KPX Adieresis Yacute -110
+KPX Adieresis Ydieresis -110
+KPX Adieresis u -30
+KPX Adieresis uacute -30
+KPX Adieresis ucircumflex -30
+KPX Adieresis udieresis -30
+KPX Adieresis ugrave -30
+KPX Adieresis uhungarumlaut -30
+KPX Adieresis umacron -30
+KPX Adieresis uogonek -30
+KPX Adieresis uring -30
+KPX Adieresis v -40
+KPX Adieresis w -30
+KPX Adieresis y -30
+KPX Adieresis yacute -30
+KPX Adieresis ydieresis -30
+KPX Agrave C -40
+KPX Agrave Cacute -40
+KPX Agrave Ccaron -40
+KPX Agrave Ccedilla -40
+KPX Agrave G -50
+KPX Agrave Gbreve -50
+KPX Agrave Gcommaaccent -50
+KPX Agrave O -40
+KPX Agrave Oacute -40
+KPX Agrave Ocircumflex -40
+KPX Agrave Odieresis -40
+KPX Agrave Ograve -40
+KPX Agrave Ohungarumlaut -40
+KPX Agrave Omacron -40
+KPX Agrave Oslash -40
+KPX Agrave Otilde -40
+KPX Agrave Q -40
+KPX Agrave T -90
+KPX Agrave Tcaron -90
+KPX Agrave Tcommaaccent -90
+KPX Agrave U -50
+KPX Agrave Uacute -50
+KPX Agrave Ucircumflex -50
+KPX Agrave Udieresis -50
+KPX Agrave Ugrave -50
+KPX Agrave Uhungarumlaut -50
+KPX Agrave Umacron -50
+KPX Agrave Uogonek -50
+KPX Agrave Uring -50
+KPX Agrave V -80
+KPX Agrave W -60
+KPX Agrave Y -110
+KPX Agrave Yacute -110
+KPX Agrave Ydieresis -110
+KPX Agrave u -30
+KPX Agrave uacute -30
+KPX Agrave ucircumflex -30
+KPX Agrave udieresis -30
+KPX Agrave ugrave -30
+KPX Agrave uhungarumlaut -30
+KPX Agrave umacron -30
+KPX Agrave uogonek -30
+KPX Agrave uring -30
+KPX Agrave v -40
+KPX Agrave w -30
+KPX Agrave y -30
+KPX Agrave yacute -30
+KPX Agrave ydieresis -30
+KPX Amacron C -40
+KPX Amacron Cacute -40
+KPX Amacron Ccaron -40
+KPX Amacron Ccedilla -40
+KPX Amacron G -50
+KPX Amacron Gbreve -50
+KPX Amacron Gcommaaccent -50
+KPX Amacron O -40
+KPX Amacron Oacute -40
+KPX Amacron Ocircumflex -40
+KPX Amacron Odieresis -40
+KPX Amacron Ograve -40
+KPX Amacron Ohungarumlaut -40
+KPX Amacron Omacron -40
+KPX Amacron Oslash -40
+KPX Amacron Otilde -40
+KPX Amacron Q -40
+KPX Amacron T -90
+KPX Amacron Tcaron -90
+KPX Amacron Tcommaaccent -90
+KPX Amacron U -50
+KPX Amacron Uacute -50
+KPX Amacron Ucircumflex -50
+KPX Amacron Udieresis -50
+KPX Amacron Ugrave -50
+KPX Amacron Uhungarumlaut -50
+KPX Amacron Umacron -50
+KPX Amacron Uogonek -50
+KPX Amacron Uring -50
+KPX Amacron V -80
+KPX Amacron W -60
+KPX Amacron Y -110
+KPX Amacron Yacute -110
+KPX Amacron Ydieresis -110
+KPX Amacron u -30
+KPX Amacron uacute -30
+KPX Amacron ucircumflex -30
+KPX Amacron udieresis -30
+KPX Amacron ugrave -30
+KPX Amacron uhungarumlaut -30
+KPX Amacron umacron -30
+KPX Amacron uogonek -30
+KPX Amacron uring -30
+KPX Amacron v -40
+KPX Amacron w -30
+KPX Amacron y -30
+KPX Amacron yacute -30
+KPX Amacron ydieresis -30
+KPX Aogonek C -40
+KPX Aogonek Cacute -40
+KPX Aogonek Ccaron -40
+KPX Aogonek Ccedilla -40
+KPX Aogonek G -50
+KPX Aogonek Gbreve -50
+KPX Aogonek Gcommaaccent -50
+KPX Aogonek O -40
+KPX Aogonek Oacute -40
+KPX Aogonek Ocircumflex -40
+KPX Aogonek Odieresis -40
+KPX Aogonek Ograve -40
+KPX Aogonek Ohungarumlaut -40
+KPX Aogonek Omacron -40
+KPX Aogonek Oslash -40
+KPX Aogonek Otilde -40
+KPX Aogonek Q -40
+KPX Aogonek T -90
+KPX Aogonek Tcaron -90
+KPX Aogonek Tcommaaccent -90
+KPX Aogonek U -50
+KPX Aogonek Uacute -50
+KPX Aogonek Ucircumflex -50
+KPX Aogonek Udieresis -50
+KPX Aogonek Ugrave -50
+KPX Aogonek Uhungarumlaut -50
+KPX Aogonek Umacron -50
+KPX Aogonek Uogonek -50
+KPX Aogonek Uring -50
+KPX Aogonek V -80
+KPX Aogonek W -60
+KPX Aogonek Y -110
+KPX Aogonek Yacute -110
+KPX Aogonek Ydieresis -110
+KPX Aogonek u -30
+KPX Aogonek uacute -30
+KPX Aogonek ucircumflex -30
+KPX Aogonek udieresis -30
+KPX Aogonek ugrave -30
+KPX Aogonek uhungarumlaut -30
+KPX Aogonek umacron -30
+KPX Aogonek uogonek -30
+KPX Aogonek uring -30
+KPX Aogonek v -40
+KPX Aogonek w -30
+KPX Aogonek y -30
+KPX Aogonek yacute -30
+KPX Aogonek ydieresis -30
+KPX Aring C -40
+KPX Aring Cacute -40
+KPX Aring Ccaron -40
+KPX Aring Ccedilla -40
+KPX Aring G -50
+KPX Aring Gbreve -50
+KPX Aring Gcommaaccent -50
+KPX Aring O -40
+KPX Aring Oacute -40
+KPX Aring Ocircumflex -40
+KPX Aring Odieresis -40
+KPX Aring Ograve -40
+KPX Aring Ohungarumlaut -40
+KPX Aring Omacron -40
+KPX Aring Oslash -40
+KPX Aring Otilde -40
+KPX Aring Q -40
+KPX Aring T -90
+KPX Aring Tcaron -90
+KPX Aring Tcommaaccent -90
+KPX Aring U -50
+KPX Aring Uacute -50
+KPX Aring Ucircumflex -50
+KPX Aring Udieresis -50
+KPX Aring Ugrave -50
+KPX Aring Uhungarumlaut -50
+KPX Aring Umacron -50
+KPX Aring Uogonek -50
+KPX Aring Uring -50
+KPX Aring V -80
+KPX Aring W -60
+KPX Aring Y -110
+KPX Aring Yacute -110
+KPX Aring Ydieresis -110
+KPX Aring u -30
+KPX Aring uacute -30
+KPX Aring ucircumflex -30
+KPX Aring udieresis -30
+KPX Aring ugrave -30
+KPX Aring uhungarumlaut -30
+KPX Aring umacron -30
+KPX Aring uogonek -30
+KPX Aring uring -30
+KPX Aring v -40
+KPX Aring w -30
+KPX Aring y -30
+KPX Aring yacute -30
+KPX Aring ydieresis -30
+KPX Atilde C -40
+KPX Atilde Cacute -40
+KPX Atilde Ccaron -40
+KPX Atilde Ccedilla -40
+KPX Atilde G -50
+KPX Atilde Gbreve -50
+KPX Atilde Gcommaaccent -50
+KPX Atilde O -40
+KPX Atilde Oacute -40
+KPX Atilde Ocircumflex -40
+KPX Atilde Odieresis -40
+KPX Atilde Ograve -40
+KPX Atilde Ohungarumlaut -40
+KPX Atilde Omacron -40
+KPX Atilde Oslash -40
+KPX Atilde Otilde -40
+KPX Atilde Q -40
+KPX Atilde T -90
+KPX Atilde Tcaron -90
+KPX Atilde Tcommaaccent -90
+KPX Atilde U -50
+KPX Atilde Uacute -50
+KPX Atilde Ucircumflex -50
+KPX Atilde Udieresis -50
+KPX Atilde Ugrave -50
+KPX Atilde Uhungarumlaut -50
+KPX Atilde Umacron -50
+KPX Atilde Uogonek -50
+KPX Atilde Uring -50
+KPX Atilde V -80
+KPX Atilde W -60
+KPX Atilde Y -110
+KPX Atilde Yacute -110
+KPX Atilde Ydieresis -110
+KPX Atilde u -30
+KPX Atilde uacute -30
+KPX Atilde ucircumflex -30
+KPX Atilde udieresis -30
+KPX Atilde ugrave -30
+KPX Atilde uhungarumlaut -30
+KPX Atilde umacron -30
+KPX Atilde uogonek -30
+KPX Atilde uring -30
+KPX Atilde v -40
+KPX Atilde w -30
+KPX Atilde y -30
+KPX Atilde yacute -30
+KPX Atilde ydieresis -30
+KPX B A -30
+KPX B Aacute -30
+KPX B Abreve -30
+KPX B Acircumflex -30
+KPX B Adieresis -30
+KPX B Agrave -30
+KPX B Amacron -30
+KPX B Aogonek -30
+KPX B Aring -30
+KPX B Atilde -30
+KPX B U -10
+KPX B Uacute -10
+KPX B Ucircumflex -10
+KPX B Udieresis -10
+KPX B Ugrave -10
+KPX B Uhungarumlaut -10
+KPX B Umacron -10
+KPX B Uogonek -10
+KPX B Uring -10
+KPX D A -40
+KPX D Aacute -40
+KPX D Abreve -40
+KPX D Acircumflex -40
+KPX D Adieresis -40
+KPX D Agrave -40
+KPX D Amacron -40
+KPX D Aogonek -40
+KPX D Aring -40
+KPX D Atilde -40
+KPX D V -40
+KPX D W -40
+KPX D Y -70
+KPX D Yacute -70
+KPX D Ydieresis -70
+KPX D comma -30
+KPX D period -30
+KPX Dcaron A -40
+KPX Dcaron Aacute -40
+KPX Dcaron Abreve -40
+KPX Dcaron Acircumflex -40
+KPX Dcaron Adieresis -40
+KPX Dcaron Agrave -40
+KPX Dcaron Amacron -40
+KPX Dcaron Aogonek -40
+KPX Dcaron Aring -40
+KPX Dcaron Atilde -40
+KPX Dcaron V -40
+KPX Dcaron W -40
+KPX Dcaron Y -70
+KPX Dcaron Yacute -70
+KPX Dcaron Ydieresis -70
+KPX Dcaron comma -30
+KPX Dcaron period -30
+KPX Dcroat A -40
+KPX Dcroat Aacute -40
+KPX Dcroat Abreve -40
+KPX Dcroat Acircumflex -40
+KPX Dcroat Adieresis -40
+KPX Dcroat Agrave -40
+KPX Dcroat Amacron -40
+KPX Dcroat Aogonek -40
+KPX Dcroat Aring -40
+KPX Dcroat Atilde -40
+KPX Dcroat V -40
+KPX Dcroat W -40
+KPX Dcroat Y -70
+KPX Dcroat Yacute -70
+KPX Dcroat Ydieresis -70
+KPX Dcroat comma -30
+KPX Dcroat period -30
+KPX F A -80
+KPX F Aacute -80
+KPX F Abreve -80
+KPX F Acircumflex -80
+KPX F Adieresis -80
+KPX F Agrave -80
+KPX F Amacron -80
+KPX F Aogonek -80
+KPX F Aring -80
+KPX F Atilde -80
+KPX F a -20
+KPX F aacute -20
+KPX F abreve -20
+KPX F acircumflex -20
+KPX F adieresis -20
+KPX F agrave -20
+KPX F amacron -20
+KPX F aogonek -20
+KPX F aring -20
+KPX F atilde -20
+KPX F comma -100
+KPX F period -100
+KPX J A -20
+KPX J Aacute -20
+KPX J Abreve -20
+KPX J Acircumflex -20
+KPX J Adieresis -20
+KPX J Agrave -20
+KPX J Amacron -20
+KPX J Aogonek -20
+KPX J Aring -20
+KPX J Atilde -20
+KPX J comma -20
+KPX J period -20
+KPX J u -20
+KPX J uacute -20
+KPX J ucircumflex -20
+KPX J udieresis -20
+KPX J ugrave -20
+KPX J uhungarumlaut -20
+KPX J umacron -20
+KPX J uogonek -20
+KPX J uring -20
+KPX K O -30
+KPX K Oacute -30
+KPX K Ocircumflex -30
+KPX K Odieresis -30
+KPX K Ograve -30
+KPX K Ohungarumlaut -30
+KPX K Omacron -30
+KPX K Oslash -30
+KPX K Otilde -30
+KPX K e -15
+KPX K eacute -15
+KPX K ecaron -15
+KPX K ecircumflex -15
+KPX K edieresis -15
+KPX K edotaccent -15
+KPX K egrave -15
+KPX K emacron -15
+KPX K eogonek -15
+KPX K o -35
+KPX K oacute -35
+KPX K ocircumflex -35
+KPX K odieresis -35
+KPX K ograve -35
+KPX K ohungarumlaut -35
+KPX K omacron -35
+KPX K oslash -35
+KPX K otilde -35
+KPX K u -30
+KPX K uacute -30
+KPX K ucircumflex -30
+KPX K udieresis -30
+KPX K ugrave -30
+KPX K uhungarumlaut -30
+KPX K umacron -30
+KPX K uogonek -30
+KPX K uring -30
+KPX K y -40
+KPX K yacute -40
+KPX K ydieresis -40
+KPX Kcommaaccent O -30
+KPX Kcommaaccent Oacute -30
+KPX Kcommaaccent Ocircumflex -30
+KPX Kcommaaccent Odieresis -30
+KPX Kcommaaccent Ograve -30
+KPX Kcommaaccent Ohungarumlaut -30
+KPX Kcommaaccent Omacron -30
+KPX Kcommaaccent Oslash -30
+KPX Kcommaaccent Otilde -30
+KPX Kcommaaccent e -15
+KPX Kcommaaccent eacute -15
+KPX Kcommaaccent ecaron -15
+KPX Kcommaaccent ecircumflex -15
+KPX Kcommaaccent edieresis -15
+KPX Kcommaaccent edotaccent -15
+KPX Kcommaaccent egrave -15
+KPX Kcommaaccent emacron -15
+KPX Kcommaaccent eogonek -15
+KPX Kcommaaccent o -35
+KPX Kcommaaccent oacute -35
+KPX Kcommaaccent ocircumflex -35
+KPX Kcommaaccent odieresis -35
+KPX Kcommaaccent ograve -35
+KPX Kcommaaccent ohungarumlaut -35
+KPX Kcommaaccent omacron -35
+KPX Kcommaaccent oslash -35
+KPX Kcommaaccent otilde -35
+KPX Kcommaaccent u -30
+KPX Kcommaaccent uacute -30
+KPX Kcommaaccent ucircumflex -30
+KPX Kcommaaccent udieresis -30
+KPX Kcommaaccent ugrave -30
+KPX Kcommaaccent uhungarumlaut -30
+KPX Kcommaaccent umacron -30
+KPX Kcommaaccent uogonek -30
+KPX Kcommaaccent uring -30
+KPX Kcommaaccent y -40
+KPX Kcommaaccent yacute -40
+KPX Kcommaaccent ydieresis -40
+KPX L T -90
+KPX L Tcaron -90
+KPX L Tcommaaccent -90
+KPX L V -110
+KPX L W -80
+KPX L Y -120
+KPX L Yacute -120
+KPX L Ydieresis -120
+KPX L quotedblright -140
+KPX L quoteright -140
+KPX L y -30
+KPX L yacute -30
+KPX L ydieresis -30
+KPX Lacute T -90
+KPX Lacute Tcaron -90
+KPX Lacute Tcommaaccent -90
+KPX Lacute V -110
+KPX Lacute W -80
+KPX Lacute Y -120
+KPX Lacute Yacute -120
+KPX Lacute Ydieresis -120
+KPX Lacute quotedblright -140
+KPX Lacute quoteright -140
+KPX Lacute y -30
+KPX Lacute yacute -30
+KPX Lacute ydieresis -30
+KPX Lcommaaccent T -90
+KPX Lcommaaccent Tcaron -90
+KPX Lcommaaccent Tcommaaccent -90
+KPX Lcommaaccent V -110
+KPX Lcommaaccent W -80
+KPX Lcommaaccent Y -120
+KPX Lcommaaccent Yacute -120
+KPX Lcommaaccent Ydieresis -120
+KPX Lcommaaccent quotedblright -140
+KPX Lcommaaccent quoteright -140
+KPX Lcommaaccent y -30
+KPX Lcommaaccent yacute -30
+KPX Lcommaaccent ydieresis -30
+KPX Lslash T -90
+KPX Lslash Tcaron -90
+KPX Lslash Tcommaaccent -90
+KPX Lslash V -110
+KPX Lslash W -80
+KPX Lslash Y -120
+KPX Lslash Yacute -120
+KPX Lslash Ydieresis -120
+KPX Lslash quotedblright -140
+KPX Lslash quoteright -140
+KPX Lslash y -30
+KPX Lslash yacute -30
+KPX Lslash ydieresis -30
+KPX O A -50
+KPX O Aacute -50
+KPX O Abreve -50
+KPX O Acircumflex -50
+KPX O Adieresis -50
+KPX O Agrave -50
+KPX O Amacron -50
+KPX O Aogonek -50
+KPX O Aring -50
+KPX O Atilde -50
+KPX O T -40
+KPX O Tcaron -40
+KPX O Tcommaaccent -40
+KPX O V -50
+KPX O W -50
+KPX O X -50
+KPX O Y -70
+KPX O Yacute -70
+KPX O Ydieresis -70
+KPX O comma -40
+KPX O period -40
+KPX Oacute A -50
+KPX Oacute Aacute -50
+KPX Oacute Abreve -50
+KPX Oacute Acircumflex -50
+KPX Oacute Adieresis -50
+KPX Oacute Agrave -50
+KPX Oacute Amacron -50
+KPX Oacute Aogonek -50
+KPX Oacute Aring -50
+KPX Oacute Atilde -50
+KPX Oacute T -40
+KPX Oacute Tcaron -40
+KPX Oacute Tcommaaccent -40
+KPX Oacute V -50
+KPX Oacute W -50
+KPX Oacute X -50
+KPX Oacute Y -70
+KPX Oacute Yacute -70
+KPX Oacute Ydieresis -70
+KPX Oacute comma -40
+KPX Oacute period -40
+KPX Ocircumflex A -50
+KPX Ocircumflex Aacute -50
+KPX Ocircumflex Abreve -50
+KPX Ocircumflex Acircumflex -50
+KPX Ocircumflex Adieresis -50
+KPX Ocircumflex Agrave -50
+KPX Ocircumflex Amacron -50
+KPX Ocircumflex Aogonek -50
+KPX Ocircumflex Aring -50
+KPX Ocircumflex Atilde -50
+KPX Ocircumflex T -40
+KPX Ocircumflex Tcaron -40
+KPX Ocircumflex Tcommaaccent -40
+KPX Ocircumflex V -50
+KPX Ocircumflex W -50
+KPX Ocircumflex X -50
+KPX Ocircumflex Y -70
+KPX Ocircumflex Yacute -70
+KPX Ocircumflex Ydieresis -70
+KPX Ocircumflex comma -40
+KPX Ocircumflex period -40
+KPX Odieresis A -50
+KPX Odieresis Aacute -50
+KPX Odieresis Abreve -50
+KPX Odieresis Acircumflex -50
+KPX Odieresis Adieresis -50
+KPX Odieresis Agrave -50
+KPX Odieresis Amacron -50
+KPX Odieresis Aogonek -50
+KPX Odieresis Aring -50
+KPX Odieresis Atilde -50
+KPX Odieresis T -40
+KPX Odieresis Tcaron -40
+KPX Odieresis Tcommaaccent -40
+KPX Odieresis V -50
+KPX Odieresis W -50
+KPX Odieresis X -50
+KPX Odieresis Y -70
+KPX Odieresis Yacute -70
+KPX Odieresis Ydieresis -70
+KPX Odieresis comma -40
+KPX Odieresis period -40
+KPX Ograve A -50
+KPX Ograve Aacute -50
+KPX Ograve Abreve -50
+KPX Ograve Acircumflex -50
+KPX Ograve Adieresis -50
+KPX Ograve Agrave -50
+KPX Ograve Amacron -50
+KPX Ograve Aogonek -50
+KPX Ograve Aring -50
+KPX Ograve Atilde -50
+KPX Ograve T -40
+KPX Ograve Tcaron -40
+KPX Ograve Tcommaaccent -40
+KPX Ograve V -50
+KPX Ograve W -50
+KPX Ograve X -50
+KPX Ograve Y -70
+KPX Ograve Yacute -70
+KPX Ograve Ydieresis -70
+KPX Ograve comma -40
+KPX Ograve period -40
+KPX Ohungarumlaut A -50
+KPX Ohungarumlaut Aacute -50
+KPX Ohungarumlaut Abreve -50
+KPX Ohungarumlaut Acircumflex -50
+KPX Ohungarumlaut Adieresis -50
+KPX Ohungarumlaut Agrave -50
+KPX Ohungarumlaut Amacron -50
+KPX Ohungarumlaut Aogonek -50
+KPX Ohungarumlaut Aring -50
+KPX Ohungarumlaut Atilde -50
+KPX Ohungarumlaut T -40
+KPX Ohungarumlaut Tcaron -40
+KPX Ohungarumlaut Tcommaaccent -40
+KPX Ohungarumlaut V -50
+KPX Ohungarumlaut W -50
+KPX Ohungarumlaut X -50
+KPX Ohungarumlaut Y -70
+KPX Ohungarumlaut Yacute -70
+KPX Ohungarumlaut Ydieresis -70
+KPX Ohungarumlaut comma -40
+KPX Ohungarumlaut period -40
+KPX Omacron A -50
+KPX Omacron Aacute -50
+KPX Omacron Abreve -50
+KPX Omacron Acircumflex -50
+KPX Omacron Adieresis -50
+KPX Omacron Agrave -50
+KPX Omacron Amacron -50
+KPX Omacron Aogonek -50
+KPX Omacron Aring -50
+KPX Omacron Atilde -50
+KPX Omacron T -40
+KPX Omacron Tcaron -40
+KPX Omacron Tcommaaccent -40
+KPX Omacron V -50
+KPX Omacron W -50
+KPX Omacron X -50
+KPX Omacron Y -70
+KPX Omacron Yacute -70
+KPX Omacron Ydieresis -70
+KPX Omacron comma -40
+KPX Omacron period -40
+KPX Oslash A -50
+KPX Oslash Aacute -50
+KPX Oslash Abreve -50
+KPX Oslash Acircumflex -50
+KPX Oslash Adieresis -50
+KPX Oslash Agrave -50
+KPX Oslash Amacron -50
+KPX Oslash Aogonek -50
+KPX Oslash Aring -50
+KPX Oslash Atilde -50
+KPX Oslash T -40
+KPX Oslash Tcaron -40
+KPX Oslash Tcommaaccent -40
+KPX Oslash V -50
+KPX Oslash W -50
+KPX Oslash X -50
+KPX Oslash Y -70
+KPX Oslash Yacute -70
+KPX Oslash Ydieresis -70
+KPX Oslash comma -40
+KPX Oslash period -40
+KPX Otilde A -50
+KPX Otilde Aacute -50
+KPX Otilde Abreve -50
+KPX Otilde Acircumflex -50
+KPX Otilde Adieresis -50
+KPX Otilde Agrave -50
+KPX Otilde Amacron -50
+KPX Otilde Aogonek -50
+KPX Otilde Aring -50
+KPX Otilde Atilde -50
+KPX Otilde T -40
+KPX Otilde Tcaron -40
+KPX Otilde Tcommaaccent -40
+KPX Otilde V -50
+KPX Otilde W -50
+KPX Otilde X -50
+KPX Otilde Y -70
+KPX Otilde Yacute -70
+KPX Otilde Ydieresis -70
+KPX Otilde comma -40
+KPX Otilde period -40
+KPX P A -100
+KPX P Aacute -100
+KPX P Abreve -100
+KPX P Acircumflex -100
+KPX P Adieresis -100
+KPX P Agrave -100
+KPX P Amacron -100
+KPX P Aogonek -100
+KPX P Aring -100
+KPX P Atilde -100
+KPX P a -30
+KPX P aacute -30
+KPX P abreve -30
+KPX P acircumflex -30
+KPX P adieresis -30
+KPX P agrave -30
+KPX P amacron -30
+KPX P aogonek -30
+KPX P aring -30
+KPX P atilde -30
+KPX P comma -120
+KPX P e -30
+KPX P eacute -30
+KPX P ecaron -30
+KPX P ecircumflex -30
+KPX P edieresis -30
+KPX P edotaccent -30
+KPX P egrave -30
+KPX P emacron -30
+KPX P eogonek -30
+KPX P o -40
+KPX P oacute -40
+KPX P ocircumflex -40
+KPX P odieresis -40
+KPX P ograve -40
+KPX P ohungarumlaut -40
+KPX P omacron -40
+KPX P oslash -40
+KPX P otilde -40
+KPX P period -120
+KPX Q U -10
+KPX Q Uacute -10
+KPX Q Ucircumflex -10
+KPX Q Udieresis -10
+KPX Q Ugrave -10
+KPX Q Uhungarumlaut -10
+KPX Q Umacron -10
+KPX Q Uogonek -10
+KPX Q Uring -10
+KPX Q comma 20
+KPX Q period 20
+KPX R O -20
+KPX R Oacute -20
+KPX R Ocircumflex -20
+KPX R Odieresis -20
+KPX R Ograve -20
+KPX R Ohungarumlaut -20
+KPX R Omacron -20
+KPX R Oslash -20
+KPX R Otilde -20
+KPX R T -20
+KPX R Tcaron -20
+KPX R Tcommaaccent -20
+KPX R U -20
+KPX R Uacute -20
+KPX R Ucircumflex -20
+KPX R Udieresis -20
+KPX R Ugrave -20
+KPX R Uhungarumlaut -20
+KPX R Umacron -20
+KPX R Uogonek -20
+KPX R Uring -20
+KPX R V -50
+KPX R W -40
+KPX R Y -50
+KPX R Yacute -50
+KPX R Ydieresis -50
+KPX Racute O -20
+KPX Racute Oacute -20
+KPX Racute Ocircumflex -20
+KPX Racute Odieresis -20
+KPX Racute Ograve -20
+KPX Racute Ohungarumlaut -20
+KPX Racute Omacron -20
+KPX Racute Oslash -20
+KPX Racute Otilde -20
+KPX Racute T -20
+KPX Racute Tcaron -20
+KPX Racute Tcommaaccent -20
+KPX Racute U -20
+KPX Racute Uacute -20
+KPX Racute Ucircumflex -20
+KPX Racute Udieresis -20
+KPX Racute Ugrave -20
+KPX Racute Uhungarumlaut -20
+KPX Racute Umacron -20
+KPX Racute Uogonek -20
+KPX Racute Uring -20
+KPX Racute V -50
+KPX Racute W -40
+KPX Racute Y -50
+KPX Racute Yacute -50
+KPX Racute Ydieresis -50
+KPX Rcaron O -20
+KPX Rcaron Oacute -20
+KPX Rcaron Ocircumflex -20
+KPX Rcaron Odieresis -20
+KPX Rcaron Ograve -20
+KPX Rcaron Ohungarumlaut -20
+KPX Rcaron Omacron -20
+KPX Rcaron Oslash -20
+KPX Rcaron Otilde -20
+KPX Rcaron T -20
+KPX Rcaron Tcaron -20
+KPX Rcaron Tcommaaccent -20
+KPX Rcaron U -20
+KPX Rcaron Uacute -20
+KPX Rcaron Ucircumflex -20
+KPX Rcaron Udieresis -20
+KPX Rcaron Ugrave -20
+KPX Rcaron Uhungarumlaut -20
+KPX Rcaron Umacron -20
+KPX Rcaron Uogonek -20
+KPX Rcaron Uring -20
+KPX Rcaron V -50
+KPX Rcaron W -40
+KPX Rcaron Y -50
+KPX Rcaron Yacute -50
+KPX Rcaron Ydieresis -50
+KPX Rcommaaccent O -20
+KPX Rcommaaccent Oacute -20
+KPX Rcommaaccent Ocircumflex -20
+KPX Rcommaaccent Odieresis -20
+KPX Rcommaaccent Ograve -20
+KPX Rcommaaccent Ohungarumlaut -20
+KPX Rcommaaccent Omacron -20
+KPX Rcommaaccent Oslash -20
+KPX Rcommaaccent Otilde -20
+KPX Rcommaaccent T -20
+KPX Rcommaaccent Tcaron -20
+KPX Rcommaaccent Tcommaaccent -20
+KPX Rcommaaccent U -20
+KPX Rcommaaccent Uacute -20
+KPX Rcommaaccent Ucircumflex -20
+KPX Rcommaaccent Udieresis -20
+KPX Rcommaaccent Ugrave -20
+KPX Rcommaaccent Uhungarumlaut -20
+KPX Rcommaaccent Umacron -20
+KPX Rcommaaccent Uogonek -20
+KPX Rcommaaccent Uring -20
+KPX Rcommaaccent V -50
+KPX Rcommaaccent W -40
+KPX Rcommaaccent Y -50
+KPX Rcommaaccent Yacute -50
+KPX Rcommaaccent Ydieresis -50
+KPX T A -90
+KPX T Aacute -90
+KPX T Abreve -90
+KPX T Acircumflex -90
+KPX T Adieresis -90
+KPX T Agrave -90
+KPX T Amacron -90
+KPX T Aogonek -90
+KPX T Aring -90
+KPX T Atilde -90
+KPX T O -40
+KPX T Oacute -40
+KPX T Ocircumflex -40
+KPX T Odieresis -40
+KPX T Ograve -40
+KPX T Ohungarumlaut -40
+KPX T Omacron -40
+KPX T Oslash -40
+KPX T Otilde -40
+KPX T a -80
+KPX T aacute -80
+KPX T abreve -80
+KPX T acircumflex -80
+KPX T adieresis -80
+KPX T agrave -80
+KPX T amacron -80
+KPX T aogonek -80
+KPX T aring -80
+KPX T atilde -80
+KPX T colon -40
+KPX T comma -80
+KPX T e -60
+KPX T eacute -60
+KPX T ecaron -60
+KPX T ecircumflex -60
+KPX T edieresis -60
+KPX T edotaccent -60
+KPX T egrave -60
+KPX T emacron -60
+KPX T eogonek -60
+KPX T hyphen -120
+KPX T o -80
+KPX T oacute -80
+KPX T ocircumflex -80
+KPX T odieresis -80
+KPX T ograve -80
+KPX T ohungarumlaut -80
+KPX T omacron -80
+KPX T oslash -80
+KPX T otilde -80
+KPX T period -80
+KPX T r -80
+KPX T racute -80
+KPX T rcommaaccent -80
+KPX T semicolon -40
+KPX T u -90
+KPX T uacute -90
+KPX T ucircumflex -90
+KPX T udieresis -90
+KPX T ugrave -90
+KPX T uhungarumlaut -90
+KPX T umacron -90
+KPX T uogonek -90
+KPX T uring -90
+KPX T w -60
+KPX T y -60
+KPX T yacute -60
+KPX T ydieresis -60
+KPX Tcaron A -90
+KPX Tcaron Aacute -90
+KPX Tcaron Abreve -90
+KPX Tcaron Acircumflex -90
+KPX Tcaron Adieresis -90
+KPX Tcaron Agrave -90
+KPX Tcaron Amacron -90
+KPX Tcaron Aogonek -90
+KPX Tcaron Aring -90
+KPX Tcaron Atilde -90
+KPX Tcaron O -40
+KPX Tcaron Oacute -40
+KPX Tcaron Ocircumflex -40
+KPX Tcaron Odieresis -40
+KPX Tcaron Ograve -40
+KPX Tcaron Ohungarumlaut -40
+KPX Tcaron Omacron -40
+KPX Tcaron Oslash -40
+KPX Tcaron Otilde -40
+KPX Tcaron a -80
+KPX Tcaron aacute -80
+KPX Tcaron abreve -80
+KPX Tcaron acircumflex -80
+KPX Tcaron adieresis -80
+KPX Tcaron agrave -80
+KPX Tcaron amacron -80
+KPX Tcaron aogonek -80
+KPX Tcaron aring -80
+KPX Tcaron atilde -80
+KPX Tcaron colon -40
+KPX Tcaron comma -80
+KPX Tcaron e -60
+KPX Tcaron eacute -60
+KPX Tcaron ecaron -60
+KPX Tcaron ecircumflex -60
+KPX Tcaron edieresis -60
+KPX Tcaron edotaccent -60
+KPX Tcaron egrave -60
+KPX Tcaron emacron -60
+KPX Tcaron eogonek -60
+KPX Tcaron hyphen -120
+KPX Tcaron o -80
+KPX Tcaron oacute -80
+KPX Tcaron ocircumflex -80
+KPX Tcaron odieresis -80
+KPX Tcaron ograve -80
+KPX Tcaron ohungarumlaut -80
+KPX Tcaron omacron -80
+KPX Tcaron oslash -80
+KPX Tcaron otilde -80
+KPX Tcaron period -80
+KPX Tcaron r -80
+KPX Tcaron racute -80
+KPX Tcaron rcommaaccent -80
+KPX Tcaron semicolon -40
+KPX Tcaron u -90
+KPX Tcaron uacute -90
+KPX Tcaron ucircumflex -90
+KPX Tcaron udieresis -90
+KPX Tcaron ugrave -90
+KPX Tcaron uhungarumlaut -90
+KPX Tcaron umacron -90
+KPX Tcaron uogonek -90
+KPX Tcaron uring -90
+KPX Tcaron w -60
+KPX Tcaron y -60
+KPX Tcaron yacute -60
+KPX Tcaron ydieresis -60
+KPX Tcommaaccent A -90
+KPX Tcommaaccent Aacute -90
+KPX Tcommaaccent Abreve -90
+KPX Tcommaaccent Acircumflex -90
+KPX Tcommaaccent Adieresis -90
+KPX Tcommaaccent Agrave -90
+KPX Tcommaaccent Amacron -90
+KPX Tcommaaccent Aogonek -90
+KPX Tcommaaccent Aring -90
+KPX Tcommaaccent Atilde -90
+KPX Tcommaaccent O -40
+KPX Tcommaaccent Oacute -40
+KPX Tcommaaccent Ocircumflex -40
+KPX Tcommaaccent Odieresis -40
+KPX Tcommaaccent Ograve -40
+KPX Tcommaaccent Ohungarumlaut -40
+KPX Tcommaaccent Omacron -40
+KPX Tcommaaccent Oslash -40
+KPX Tcommaaccent Otilde -40
+KPX Tcommaaccent a -80
+KPX Tcommaaccent aacute -80
+KPX Tcommaaccent abreve -80
+KPX Tcommaaccent acircumflex -80
+KPX Tcommaaccent adieresis -80
+KPX Tcommaaccent agrave -80
+KPX Tcommaaccent amacron -80
+KPX Tcommaaccent aogonek -80
+KPX Tcommaaccent aring -80
+KPX Tcommaaccent atilde -80
+KPX Tcommaaccent colon -40
+KPX Tcommaaccent comma -80
+KPX Tcommaaccent e -60
+KPX Tcommaaccent eacute -60
+KPX Tcommaaccent ecaron -60
+KPX Tcommaaccent ecircumflex -60
+KPX Tcommaaccent edieresis -60
+KPX Tcommaaccent edotaccent -60
+KPX Tcommaaccent egrave -60
+KPX Tcommaaccent emacron -60
+KPX Tcommaaccent eogonek -60
+KPX Tcommaaccent hyphen -120
+KPX Tcommaaccent o -80
+KPX Tcommaaccent oacute -80
+KPX Tcommaaccent ocircumflex -80
+KPX Tcommaaccent odieresis -80
+KPX Tcommaaccent ograve -80
+KPX Tcommaaccent ohungarumlaut -80
+KPX Tcommaaccent omacron -80
+KPX Tcommaaccent oslash -80
+KPX Tcommaaccent otilde -80
+KPX Tcommaaccent period -80
+KPX Tcommaaccent r -80
+KPX Tcommaaccent racute -80
+KPX Tcommaaccent rcommaaccent -80
+KPX Tcommaaccent semicolon -40
+KPX Tcommaaccent u -90
+KPX Tcommaaccent uacute -90
+KPX Tcommaaccent ucircumflex -90
+KPX Tcommaaccent udieresis -90
+KPX Tcommaaccent ugrave -90
+KPX Tcommaaccent uhungarumlaut -90
+KPX Tcommaaccent umacron -90
+KPX Tcommaaccent uogonek -90
+KPX Tcommaaccent uring -90
+KPX Tcommaaccent w -60
+KPX Tcommaaccent y -60
+KPX Tcommaaccent yacute -60
+KPX Tcommaaccent ydieresis -60
+KPX U A -50
+KPX U Aacute -50
+KPX U Abreve -50
+KPX U Acircumflex -50
+KPX U Adieresis -50
+KPX U Agrave -50
+KPX U Amacron -50
+KPX U Aogonek -50
+KPX U Aring -50
+KPX U Atilde -50
+KPX U comma -30
+KPX U period -30
+KPX Uacute A -50
+KPX Uacute Aacute -50
+KPX Uacute Abreve -50
+KPX Uacute Acircumflex -50
+KPX Uacute Adieresis -50
+KPX Uacute Agrave -50
+KPX Uacute Amacron -50
+KPX Uacute Aogonek -50
+KPX Uacute Aring -50
+KPX Uacute Atilde -50
+KPX Uacute comma -30
+KPX Uacute period -30
+KPX Ucircumflex A -50
+KPX Ucircumflex Aacute -50
+KPX Ucircumflex Abreve -50
+KPX Ucircumflex Acircumflex -50
+KPX Ucircumflex Adieresis -50
+KPX Ucircumflex Agrave -50
+KPX Ucircumflex Amacron -50
+KPX Ucircumflex Aogonek -50
+KPX Ucircumflex Aring -50
+KPX Ucircumflex Atilde -50
+KPX Ucircumflex comma -30
+KPX Ucircumflex period -30
+KPX Udieresis A -50
+KPX Udieresis Aacute -50
+KPX Udieresis Abreve -50
+KPX Udieresis Acircumflex -50
+KPX Udieresis Adieresis -50
+KPX Udieresis Agrave -50
+KPX Udieresis Amacron -50
+KPX Udieresis Aogonek -50
+KPX Udieresis Aring -50
+KPX Udieresis Atilde -50
+KPX Udieresis comma -30
+KPX Udieresis period -30
+KPX Ugrave A -50
+KPX Ugrave Aacute -50
+KPX Ugrave Abreve -50
+KPX Ugrave Acircumflex -50
+KPX Ugrave Adieresis -50
+KPX Ugrave Agrave -50
+KPX Ugrave Amacron -50
+KPX Ugrave Aogonek -50
+KPX Ugrave Aring -50
+KPX Ugrave Atilde -50
+KPX Ugrave comma -30
+KPX Ugrave period -30
+KPX Uhungarumlaut A -50
+KPX Uhungarumlaut Aacute -50
+KPX Uhungarumlaut Abreve -50
+KPX Uhungarumlaut Acircumflex -50
+KPX Uhungarumlaut Adieresis -50
+KPX Uhungarumlaut Agrave -50
+KPX Uhungarumlaut Amacron -50
+KPX Uhungarumlaut Aogonek -50
+KPX Uhungarumlaut Aring -50
+KPX Uhungarumlaut Atilde -50
+KPX Uhungarumlaut comma -30
+KPX Uhungarumlaut period -30
+KPX Umacron A -50
+KPX Umacron Aacute -50
+KPX Umacron Abreve -50
+KPX Umacron Acircumflex -50
+KPX Umacron Adieresis -50
+KPX Umacron Agrave -50
+KPX Umacron Amacron -50
+KPX Umacron Aogonek -50
+KPX Umacron Aring -50
+KPX Umacron Atilde -50
+KPX Umacron comma -30
+KPX Umacron period -30
+KPX Uogonek A -50
+KPX Uogonek Aacute -50
+KPX Uogonek Abreve -50
+KPX Uogonek Acircumflex -50
+KPX Uogonek Adieresis -50
+KPX Uogonek Agrave -50
+KPX Uogonek Amacron -50
+KPX Uogonek Aogonek -50
+KPX Uogonek Aring -50
+KPX Uogonek Atilde -50
+KPX Uogonek comma -30
+KPX Uogonek period -30
+KPX Uring A -50
+KPX Uring Aacute -50
+KPX Uring Abreve -50
+KPX Uring Acircumflex -50
+KPX Uring Adieresis -50
+KPX Uring Agrave -50
+KPX Uring Amacron -50
+KPX Uring Aogonek -50
+KPX Uring Aring -50
+KPX Uring Atilde -50
+KPX Uring comma -30
+KPX Uring period -30
+KPX V A -80
+KPX V Aacute -80
+KPX V Abreve -80
+KPX V Acircumflex -80
+KPX V Adieresis -80
+KPX V Agrave -80
+KPX V Amacron -80
+KPX V Aogonek -80
+KPX V Aring -80
+KPX V Atilde -80
+KPX V G -50
+KPX V Gbreve -50
+KPX V Gcommaaccent -50
+KPX V O -50
+KPX V Oacute -50
+KPX V Ocircumflex -50
+KPX V Odieresis -50
+KPX V Ograve -50
+KPX V Ohungarumlaut -50
+KPX V Omacron -50
+KPX V Oslash -50
+KPX V Otilde -50
+KPX V a -60
+KPX V aacute -60
+KPX V abreve -60
+KPX V acircumflex -60
+KPX V adieresis -60
+KPX V agrave -60
+KPX V amacron -60
+KPX V aogonek -60
+KPX V aring -60
+KPX V atilde -60
+KPX V colon -40
+KPX V comma -120
+KPX V e -50
+KPX V eacute -50
+KPX V ecaron -50
+KPX V ecircumflex -50
+KPX V edieresis -50
+KPX V edotaccent -50
+KPX V egrave -50
+KPX V emacron -50
+KPX V eogonek -50
+KPX V hyphen -80
+KPX V o -90
+KPX V oacute -90
+KPX V ocircumflex -90
+KPX V odieresis -90
+KPX V ograve -90
+KPX V ohungarumlaut -90
+KPX V omacron -90
+KPX V oslash -90
+KPX V otilde -90
+KPX V period -120
+KPX V semicolon -40
+KPX V u -60
+KPX V uacute -60
+KPX V ucircumflex -60
+KPX V udieresis -60
+KPX V ugrave -60
+KPX V uhungarumlaut -60
+KPX V umacron -60
+KPX V uogonek -60
+KPX V uring -60
+KPX W A -60
+KPX W Aacute -60
+KPX W Abreve -60
+KPX W Acircumflex -60
+KPX W Adieresis -60
+KPX W Agrave -60
+KPX W Amacron -60
+KPX W Aogonek -60
+KPX W Aring -60
+KPX W Atilde -60
+KPX W O -20
+KPX W Oacute -20
+KPX W Ocircumflex -20
+KPX W Odieresis -20
+KPX W Ograve -20
+KPX W Ohungarumlaut -20
+KPX W Omacron -20
+KPX W Oslash -20
+KPX W Otilde -20
+KPX W a -40
+KPX W aacute -40
+KPX W abreve -40
+KPX W acircumflex -40
+KPX W adieresis -40
+KPX W agrave -40
+KPX W amacron -40
+KPX W aogonek -40
+KPX W aring -40
+KPX W atilde -40
+KPX W colon -10
+KPX W comma -80
+KPX W e -35
+KPX W eacute -35
+KPX W ecaron -35
+KPX W ecircumflex -35
+KPX W edieresis -35
+KPX W edotaccent -35
+KPX W egrave -35
+KPX W emacron -35
+KPX W eogonek -35
+KPX W hyphen -40
+KPX W o -60
+KPX W oacute -60
+KPX W ocircumflex -60
+KPX W odieresis -60
+KPX W ograve -60
+KPX W ohungarumlaut -60
+KPX W omacron -60
+KPX W oslash -60
+KPX W otilde -60
+KPX W period -80
+KPX W semicolon -10
+KPX W u -45
+KPX W uacute -45
+KPX W ucircumflex -45
+KPX W udieresis -45
+KPX W ugrave -45
+KPX W uhungarumlaut -45
+KPX W umacron -45
+KPX W uogonek -45
+KPX W uring -45
+KPX W y -20
+KPX W yacute -20
+KPX W ydieresis -20
+KPX Y A -110
+KPX Y Aacute -110
+KPX Y Abreve -110
+KPX Y Acircumflex -110
+KPX Y Adieresis -110
+KPX Y Agrave -110
+KPX Y Amacron -110
+KPX Y Aogonek -110
+KPX Y Aring -110
+KPX Y Atilde -110
+KPX Y O -70
+KPX Y Oacute -70
+KPX Y Ocircumflex -70
+KPX Y Odieresis -70
+KPX Y Ograve -70
+KPX Y Ohungarumlaut -70
+KPX Y Omacron -70
+KPX Y Oslash -70
+KPX Y Otilde -70
+KPX Y a -90
+KPX Y aacute -90
+KPX Y abreve -90
+KPX Y acircumflex -90
+KPX Y adieresis -90
+KPX Y agrave -90
+KPX Y amacron -90
+KPX Y aogonek -90
+KPX Y aring -90
+KPX Y atilde -90
+KPX Y colon -50
+KPX Y comma -100
+KPX Y e -80
+KPX Y eacute -80
+KPX Y ecaron -80
+KPX Y ecircumflex -80
+KPX Y edieresis -80
+KPX Y edotaccent -80
+KPX Y egrave -80
+KPX Y emacron -80
+KPX Y eogonek -80
+KPX Y o -100
+KPX Y oacute -100
+KPX Y ocircumflex -100
+KPX Y odieresis -100
+KPX Y ograve -100
+KPX Y ohungarumlaut -100
+KPX Y omacron -100
+KPX Y oslash -100
+KPX Y otilde -100
+KPX Y period -100
+KPX Y semicolon -50
+KPX Y u -100
+KPX Y uacute -100
+KPX Y ucircumflex -100
+KPX Y udieresis -100
+KPX Y ugrave -100
+KPX Y uhungarumlaut -100
+KPX Y umacron -100
+KPX Y uogonek -100
+KPX Y uring -100
+KPX Yacute A -110
+KPX Yacute Aacute -110
+KPX Yacute Abreve -110
+KPX Yacute Acircumflex -110
+KPX Yacute Adieresis -110
+KPX Yacute Agrave -110
+KPX Yacute Amacron -110
+KPX Yacute Aogonek -110
+KPX Yacute Aring -110
+KPX Yacute Atilde -110
+KPX Yacute O -70
+KPX Yacute Oacute -70
+KPX Yacute Ocircumflex -70
+KPX Yacute Odieresis -70
+KPX Yacute Ograve -70
+KPX Yacute Ohungarumlaut -70
+KPX Yacute Omacron -70
+KPX Yacute Oslash -70
+KPX Yacute Otilde -70
+KPX Yacute a -90
+KPX Yacute aacute -90
+KPX Yacute abreve -90
+KPX Yacute acircumflex -90
+KPX Yacute adieresis -90
+KPX Yacute agrave -90
+KPX Yacute amacron -90
+KPX Yacute aogonek -90
+KPX Yacute aring -90
+KPX Yacute atilde -90
+KPX Yacute colon -50
+KPX Yacute comma -100
+KPX Yacute e -80
+KPX Yacute eacute -80
+KPX Yacute ecaron -80
+KPX Yacute ecircumflex -80
+KPX Yacute edieresis -80
+KPX Yacute edotaccent -80
+KPX Yacute egrave -80
+KPX Yacute emacron -80
+KPX Yacute eogonek -80
+KPX Yacute o -100
+KPX Yacute oacute -100
+KPX Yacute ocircumflex -100
+KPX Yacute odieresis -100
+KPX Yacute ograve -100
+KPX Yacute ohungarumlaut -100
+KPX Yacute omacron -100
+KPX Yacute oslash -100
+KPX Yacute otilde -100
+KPX Yacute period -100
+KPX Yacute semicolon -50
+KPX Yacute u -100
+KPX Yacute uacute -100
+KPX Yacute ucircumflex -100
+KPX Yacute udieresis -100
+KPX Yacute ugrave -100
+KPX Yacute uhungarumlaut -100
+KPX Yacute umacron -100
+KPX Yacute uogonek -100
+KPX Yacute uring -100
+KPX Ydieresis A -110
+KPX Ydieresis Aacute -110
+KPX Ydieresis Abreve -110
+KPX Ydieresis Acircumflex -110
+KPX Ydieresis Adieresis -110
+KPX Ydieresis Agrave -110
+KPX Ydieresis Amacron -110
+KPX Ydieresis Aogonek -110
+KPX Ydieresis Aring -110
+KPX Ydieresis Atilde -110
+KPX Ydieresis O -70
+KPX Ydieresis Oacute -70
+KPX Ydieresis Ocircumflex -70
+KPX Ydieresis Odieresis -70
+KPX Ydieresis Ograve -70
+KPX Ydieresis Ohungarumlaut -70
+KPX Ydieresis Omacron -70
+KPX Ydieresis Oslash -70
+KPX Ydieresis Otilde -70
+KPX Ydieresis a -90
+KPX Ydieresis aacute -90
+KPX Ydieresis abreve -90
+KPX Ydieresis acircumflex -90
+KPX Ydieresis adieresis -90
+KPX Ydieresis agrave -90
+KPX Ydieresis amacron -90
+KPX Ydieresis aogonek -90
+KPX Ydieresis aring -90
+KPX Ydieresis atilde -90
+KPX Ydieresis colon -50
+KPX Ydieresis comma -100
+KPX Ydieresis e -80
+KPX Ydieresis eacute -80
+KPX Ydieresis ecaron -80
+KPX Ydieresis ecircumflex -80
+KPX Ydieresis edieresis -80
+KPX Ydieresis edotaccent -80
+KPX Ydieresis egrave -80
+KPX Ydieresis emacron -80
+KPX Ydieresis eogonek -80
+KPX Ydieresis o -100
+KPX Ydieresis oacute -100
+KPX Ydieresis ocircumflex -100
+KPX Ydieresis odieresis -100
+KPX Ydieresis ograve -100
+KPX Ydieresis ohungarumlaut -100
+KPX Ydieresis omacron -100
+KPX Ydieresis oslash -100
+KPX Ydieresis otilde -100
+KPX Ydieresis period -100
+KPX Ydieresis semicolon -50
+KPX Ydieresis u -100
+KPX Ydieresis uacute -100
+KPX Ydieresis ucircumflex -100
+KPX Ydieresis udieresis -100
+KPX Ydieresis ugrave -100
+KPX Ydieresis uhungarumlaut -100
+KPX Ydieresis umacron -100
+KPX Ydieresis uogonek -100
+KPX Ydieresis uring -100
+KPX a g -10
+KPX a gbreve -10
+KPX a gcommaaccent -10
+KPX a v -15
+KPX a w -15
+KPX a y -20
+KPX a yacute -20
+KPX a ydieresis -20
+KPX aacute g -10
+KPX aacute gbreve -10
+KPX aacute gcommaaccent -10
+KPX aacute v -15
+KPX aacute w -15
+KPX aacute y -20
+KPX aacute yacute -20
+KPX aacute ydieresis -20
+KPX abreve g -10
+KPX abreve gbreve -10
+KPX abreve gcommaaccent -10
+KPX abreve v -15
+KPX abreve w -15
+KPX abreve y -20
+KPX abreve yacute -20
+KPX abreve ydieresis -20
+KPX acircumflex g -10
+KPX acircumflex gbreve -10
+KPX acircumflex gcommaaccent -10
+KPX acircumflex v -15
+KPX acircumflex w -15
+KPX acircumflex y -20
+KPX acircumflex yacute -20
+KPX acircumflex ydieresis -20
+KPX adieresis g -10
+KPX adieresis gbreve -10
+KPX adieresis gcommaaccent -10
+KPX adieresis v -15
+KPX adieresis w -15
+KPX adieresis y -20
+KPX adieresis yacute -20
+KPX adieresis ydieresis -20
+KPX agrave g -10
+KPX agrave gbreve -10
+KPX agrave gcommaaccent -10
+KPX agrave v -15
+KPX agrave w -15
+KPX agrave y -20
+KPX agrave yacute -20
+KPX agrave ydieresis -20
+KPX amacron g -10
+KPX amacron gbreve -10
+KPX amacron gcommaaccent -10
+KPX amacron v -15
+KPX amacron w -15
+KPX amacron y -20
+KPX amacron yacute -20
+KPX amacron ydieresis -20
+KPX aogonek g -10
+KPX aogonek gbreve -10
+KPX aogonek gcommaaccent -10
+KPX aogonek v -15
+KPX aogonek w -15
+KPX aogonek y -20
+KPX aogonek yacute -20
+KPX aogonek ydieresis -20
+KPX aring g -10
+KPX aring gbreve -10
+KPX aring gcommaaccent -10
+KPX aring v -15
+KPX aring w -15
+KPX aring y -20
+KPX aring yacute -20
+KPX aring ydieresis -20
+KPX atilde g -10
+KPX atilde gbreve -10
+KPX atilde gcommaaccent -10
+KPX atilde v -15
+KPX atilde w -15
+KPX atilde y -20
+KPX atilde yacute -20
+KPX atilde ydieresis -20
+KPX b l -10
+KPX b lacute -10
+KPX b lcommaaccent -10
+KPX b lslash -10
+KPX b u -20
+KPX b uacute -20
+KPX b ucircumflex -20
+KPX b udieresis -20
+KPX b ugrave -20
+KPX b uhungarumlaut -20
+KPX b umacron -20
+KPX b uogonek -20
+KPX b uring -20
+KPX b v -20
+KPX b y -20
+KPX b yacute -20
+KPX b ydieresis -20
+KPX c h -10
+KPX c k -20
+KPX c kcommaaccent -20
+KPX c l -20
+KPX c lacute -20
+KPX c lcommaaccent -20
+KPX c lslash -20
+KPX c y -10
+KPX c yacute -10
+KPX c ydieresis -10
+KPX cacute h -10
+KPX cacute k -20
+KPX cacute kcommaaccent -20
+KPX cacute l -20
+KPX cacute lacute -20
+KPX cacute lcommaaccent -20
+KPX cacute lslash -20
+KPX cacute y -10
+KPX cacute yacute -10
+KPX cacute ydieresis -10
+KPX ccaron h -10
+KPX ccaron k -20
+KPX ccaron kcommaaccent -20
+KPX ccaron l -20
+KPX ccaron lacute -20
+KPX ccaron lcommaaccent -20
+KPX ccaron lslash -20
+KPX ccaron y -10
+KPX ccaron yacute -10
+KPX ccaron ydieresis -10
+KPX ccedilla h -10
+KPX ccedilla k -20
+KPX ccedilla kcommaaccent -20
+KPX ccedilla l -20
+KPX ccedilla lacute -20
+KPX ccedilla lcommaaccent -20
+KPX ccedilla lslash -20
+KPX ccedilla y -10
+KPX ccedilla yacute -10
+KPX ccedilla ydieresis -10
+KPX colon space -40
+KPX comma quotedblright -120
+KPX comma quoteright -120
+KPX comma space -40
+KPX d d -10
+KPX d dcroat -10
+KPX d v -15
+KPX d w -15
+KPX d y -15
+KPX d yacute -15
+KPX d ydieresis -15
+KPX dcroat d -10
+KPX dcroat dcroat -10
+KPX dcroat v -15
+KPX dcroat w -15
+KPX dcroat y -15
+KPX dcroat yacute -15
+KPX dcroat ydieresis -15
+KPX e comma 10
+KPX e period 20
+KPX e v -15
+KPX e w -15
+KPX e x -15
+KPX e y -15
+KPX e yacute -15
+KPX e ydieresis -15
+KPX eacute comma 10
+KPX eacute period 20
+KPX eacute v -15
+KPX eacute w -15
+KPX eacute x -15
+KPX eacute y -15
+KPX eacute yacute -15
+KPX eacute ydieresis -15
+KPX ecaron comma 10
+KPX ecaron period 20
+KPX ecaron v -15
+KPX ecaron w -15
+KPX ecaron x -15
+KPX ecaron y -15
+KPX ecaron yacute -15
+KPX ecaron ydieresis -15
+KPX ecircumflex comma 10
+KPX ecircumflex period 20
+KPX ecircumflex v -15
+KPX ecircumflex w -15
+KPX ecircumflex x -15
+KPX ecircumflex y -15
+KPX ecircumflex yacute -15
+KPX ecircumflex ydieresis -15
+KPX edieresis comma 10
+KPX edieresis period 20
+KPX edieresis v -15
+KPX edieresis w -15
+KPX edieresis x -15
+KPX edieresis y -15
+KPX edieresis yacute -15
+KPX edieresis ydieresis -15
+KPX edotaccent comma 10
+KPX edotaccent period 20
+KPX edotaccent v -15
+KPX edotaccent w -15
+KPX edotaccent x -15
+KPX edotaccent y -15
+KPX edotaccent yacute -15
+KPX edotaccent ydieresis -15
+KPX egrave comma 10
+KPX egrave period 20
+KPX egrave v -15
+KPX egrave w -15
+KPX egrave x -15
+KPX egrave y -15
+KPX egrave yacute -15
+KPX egrave ydieresis -15
+KPX emacron comma 10
+KPX emacron period 20
+KPX emacron v -15
+KPX emacron w -15
+KPX emacron x -15
+KPX emacron y -15
+KPX emacron yacute -15
+KPX emacron ydieresis -15
+KPX eogonek comma 10
+KPX eogonek period 20
+KPX eogonek v -15
+KPX eogonek w -15
+KPX eogonek x -15
+KPX eogonek y -15
+KPX eogonek yacute -15
+KPX eogonek ydieresis -15
+KPX f comma -10
+KPX f e -10
+KPX f eacute -10
+KPX f ecaron -10
+KPX f ecircumflex -10
+KPX f edieresis -10
+KPX f edotaccent -10
+KPX f egrave -10
+KPX f emacron -10
+KPX f eogonek -10
+KPX f o -20
+KPX f oacute -20
+KPX f ocircumflex -20
+KPX f odieresis -20
+KPX f ograve -20
+KPX f ohungarumlaut -20
+KPX f omacron -20
+KPX f oslash -20
+KPX f otilde -20
+KPX f period -10
+KPX f quotedblright 30
+KPX f quoteright 30
+KPX g e 10
+KPX g eacute 10
+KPX g ecaron 10
+KPX g ecircumflex 10
+KPX g edieresis 10
+KPX g edotaccent 10
+KPX g egrave 10
+KPX g emacron 10
+KPX g eogonek 10
+KPX g g -10
+KPX g gbreve -10
+KPX g gcommaaccent -10
+KPX gbreve e 10
+KPX gbreve eacute 10
+KPX gbreve ecaron 10
+KPX gbreve ecircumflex 10
+KPX gbreve edieresis 10
+KPX gbreve edotaccent 10
+KPX gbreve egrave 10
+KPX gbreve emacron 10
+KPX gbreve eogonek 10
+KPX gbreve g -10
+KPX gbreve gbreve -10
+KPX gbreve gcommaaccent -10
+KPX gcommaaccent e 10
+KPX gcommaaccent eacute 10
+KPX gcommaaccent ecaron 10
+KPX gcommaaccent ecircumflex 10
+KPX gcommaaccent edieresis 10
+KPX gcommaaccent edotaccent 10
+KPX gcommaaccent egrave 10
+KPX gcommaaccent emacron 10
+KPX gcommaaccent eogonek 10
+KPX gcommaaccent g -10
+KPX gcommaaccent gbreve -10
+KPX gcommaaccent gcommaaccent -10
+KPX h y -20
+KPX h yacute -20
+KPX h ydieresis -20
+KPX k o -15
+KPX k oacute -15
+KPX k ocircumflex -15
+KPX k odieresis -15
+KPX k ograve -15
+KPX k ohungarumlaut -15
+KPX k omacron -15
+KPX k oslash -15
+KPX k otilde -15
+KPX kcommaaccent o -15
+KPX kcommaaccent oacute -15
+KPX kcommaaccent ocircumflex -15
+KPX kcommaaccent odieresis -15
+KPX kcommaaccent ograve -15
+KPX kcommaaccent ohungarumlaut -15
+KPX kcommaaccent omacron -15
+KPX kcommaaccent oslash -15
+KPX kcommaaccent otilde -15
+KPX l w -15
+KPX l y -15
+KPX l yacute -15
+KPX l ydieresis -15
+KPX lacute w -15
+KPX lacute y -15
+KPX lacute yacute -15
+KPX lacute ydieresis -15
+KPX lcommaaccent w -15
+KPX lcommaaccent y -15
+KPX lcommaaccent yacute -15
+KPX lcommaaccent ydieresis -15
+KPX lslash w -15
+KPX lslash y -15
+KPX lslash yacute -15
+KPX lslash ydieresis -15
+KPX m u -20
+KPX m uacute -20
+KPX m ucircumflex -20
+KPX m udieresis -20
+KPX m ugrave -20
+KPX m uhungarumlaut -20
+KPX m umacron -20
+KPX m uogonek -20
+KPX m uring -20
+KPX m y -30
+KPX m yacute -30
+KPX m ydieresis -30
+KPX n u -10
+KPX n uacute -10
+KPX n ucircumflex -10
+KPX n udieresis -10
+KPX n ugrave -10
+KPX n uhungarumlaut -10
+KPX n umacron -10
+KPX n uogonek -10
+KPX n uring -10
+KPX n v -40
+KPX n y -20
+KPX n yacute -20
+KPX n ydieresis -20
+KPX nacute u -10
+KPX nacute uacute -10
+KPX nacute ucircumflex -10
+KPX nacute udieresis -10
+KPX nacute ugrave -10
+KPX nacute uhungarumlaut -10
+KPX nacute umacron -10
+KPX nacute uogonek -10
+KPX nacute uring -10
+KPX nacute v -40
+KPX nacute y -20
+KPX nacute yacute -20
+KPX nacute ydieresis -20
+KPX ncaron u -10
+KPX ncaron uacute -10
+KPX ncaron ucircumflex -10
+KPX ncaron udieresis -10
+KPX ncaron ugrave -10
+KPX ncaron uhungarumlaut -10
+KPX ncaron umacron -10
+KPX ncaron uogonek -10
+KPX ncaron uring -10
+KPX ncaron v -40
+KPX ncaron y -20
+KPX ncaron yacute -20
+KPX ncaron ydieresis -20
+KPX ncommaaccent u -10
+KPX ncommaaccent uacute -10
+KPX ncommaaccent ucircumflex -10
+KPX ncommaaccent udieresis -10
+KPX ncommaaccent ugrave -10
+KPX ncommaaccent uhungarumlaut -10
+KPX ncommaaccent umacron -10
+KPX ncommaaccent uogonek -10
+KPX ncommaaccent uring -10
+KPX ncommaaccent v -40
+KPX ncommaaccent y -20
+KPX ncommaaccent yacute -20
+KPX ncommaaccent ydieresis -20
+KPX ntilde u -10
+KPX ntilde uacute -10
+KPX ntilde ucircumflex -10
+KPX ntilde udieresis -10
+KPX ntilde ugrave -10
+KPX ntilde uhungarumlaut -10
+KPX ntilde umacron -10
+KPX ntilde uogonek -10
+KPX ntilde uring -10
+KPX ntilde v -40
+KPX ntilde y -20
+KPX ntilde yacute -20
+KPX ntilde ydieresis -20
+KPX o v -20
+KPX o w -15
+KPX o x -30
+KPX o y -20
+KPX o yacute -20
+KPX o ydieresis -20
+KPX oacute v -20
+KPX oacute w -15
+KPX oacute x -30
+KPX oacute y -20
+KPX oacute yacute -20
+KPX oacute ydieresis -20
+KPX ocircumflex v -20
+KPX ocircumflex w -15
+KPX ocircumflex x -30
+KPX ocircumflex y -20
+KPX ocircumflex yacute -20
+KPX ocircumflex ydieresis -20
+KPX odieresis v -20
+KPX odieresis w -15
+KPX odieresis x -30
+KPX odieresis y -20
+KPX odieresis yacute -20
+KPX odieresis ydieresis -20
+KPX ograve v -20
+KPX ograve w -15
+KPX ograve x -30
+KPX ograve y -20
+KPX ograve yacute -20
+KPX ograve ydieresis -20
+KPX ohungarumlaut v -20
+KPX ohungarumlaut w -15
+KPX ohungarumlaut x -30
+KPX ohungarumlaut y -20
+KPX ohungarumlaut yacute -20
+KPX ohungarumlaut ydieresis -20
+KPX omacron v -20
+KPX omacron w -15
+KPX omacron x -30
+KPX omacron y -20
+KPX omacron yacute -20
+KPX omacron ydieresis -20
+KPX oslash v -20
+KPX oslash w -15
+KPX oslash x -30
+KPX oslash y -20
+KPX oslash yacute -20
+KPX oslash ydieresis -20
+KPX otilde v -20
+KPX otilde w -15
+KPX otilde x -30
+KPX otilde y -20
+KPX otilde yacute -20
+KPX otilde ydieresis -20
+KPX p y -15
+KPX p yacute -15
+KPX p ydieresis -15
+KPX period quotedblright -120
+KPX period quoteright -120
+KPX period space -40
+KPX quotedblright space -80
+KPX quoteleft quoteleft -46
+KPX quoteright d -80
+KPX quoteright dcroat -80
+KPX quoteright l -20
+KPX quoteright lacute -20
+KPX quoteright lcommaaccent -20
+KPX quoteright lslash -20
+KPX quoteright quoteright -46
+KPX quoteright r -40
+KPX quoteright racute -40
+KPX quoteright rcaron -40
+KPX quoteright rcommaaccent -40
+KPX quoteright s -60
+KPX quoteright sacute -60
+KPX quoteright scaron -60
+KPX quoteright scedilla -60
+KPX quoteright scommaaccent -60
+KPX quoteright space -80
+KPX quoteright v -20
+KPX r c -20
+KPX r cacute -20
+KPX r ccaron -20
+KPX r ccedilla -20
+KPX r comma -60
+KPX r d -20
+KPX r dcroat -20
+KPX r g -15
+KPX r gbreve -15
+KPX r gcommaaccent -15
+KPX r hyphen -20
+KPX r o -20
+KPX r oacute -20
+KPX r ocircumflex -20
+KPX r odieresis -20
+KPX r ograve -20
+KPX r ohungarumlaut -20
+KPX r omacron -20
+KPX r oslash -20
+KPX r otilde -20
+KPX r period -60
+KPX r q -20
+KPX r s -15
+KPX r sacute -15
+KPX r scaron -15
+KPX r scedilla -15
+KPX r scommaaccent -15
+KPX r t 20
+KPX r tcommaaccent 20
+KPX r v 10
+KPX r y 10
+KPX r yacute 10
+KPX r ydieresis 10
+KPX racute c -20
+KPX racute cacute -20
+KPX racute ccaron -20
+KPX racute ccedilla -20
+KPX racute comma -60
+KPX racute d -20
+KPX racute dcroat -20
+KPX racute g -15
+KPX racute gbreve -15
+KPX racute gcommaaccent -15
+KPX racute hyphen -20
+KPX racute o -20
+KPX racute oacute -20
+KPX racute ocircumflex -20
+KPX racute odieresis -20
+KPX racute ograve -20
+KPX racute ohungarumlaut -20
+KPX racute omacron -20
+KPX racute oslash -20
+KPX racute otilde -20
+KPX racute period -60
+KPX racute q -20
+KPX racute s -15
+KPX racute sacute -15
+KPX racute scaron -15
+KPX racute scedilla -15
+KPX racute scommaaccent -15
+KPX racute t 20
+KPX racute tcommaaccent 20
+KPX racute v 10
+KPX racute y 10
+KPX racute yacute 10
+KPX racute ydieresis 10
+KPX rcaron c -20
+KPX rcaron cacute -20
+KPX rcaron ccaron -20
+KPX rcaron ccedilla -20
+KPX rcaron comma -60
+KPX rcaron d -20
+KPX rcaron dcroat -20
+KPX rcaron g -15
+KPX rcaron gbreve -15
+KPX rcaron gcommaaccent -15
+KPX rcaron hyphen -20
+KPX rcaron o -20
+KPX rcaron oacute -20
+KPX rcaron ocircumflex -20
+KPX rcaron odieresis -20
+KPX rcaron ograve -20
+KPX rcaron ohungarumlaut -20
+KPX rcaron omacron -20
+KPX rcaron oslash -20
+KPX rcaron otilde -20
+KPX rcaron period -60
+KPX rcaron q -20
+KPX rcaron s -15
+KPX rcaron sacute -15
+KPX rcaron scaron -15
+KPX rcaron scedilla -15
+KPX rcaron scommaaccent -15
+KPX rcaron t 20
+KPX rcaron tcommaaccent 20
+KPX rcaron v 10
+KPX rcaron y 10
+KPX rcaron yacute 10
+KPX rcaron ydieresis 10
+KPX rcommaaccent c -20
+KPX rcommaaccent cacute -20
+KPX rcommaaccent ccaron -20
+KPX rcommaaccent ccedilla -20
+KPX rcommaaccent comma -60
+KPX rcommaaccent d -20
+KPX rcommaaccent dcroat -20
+KPX rcommaaccent g -15
+KPX rcommaaccent gbreve -15
+KPX rcommaaccent gcommaaccent -15
+KPX rcommaaccent hyphen -20
+KPX rcommaaccent o -20
+KPX rcommaaccent oacute -20
+KPX rcommaaccent ocircumflex -20
+KPX rcommaaccent odieresis -20
+KPX rcommaaccent ograve -20
+KPX rcommaaccent ohungarumlaut -20
+KPX rcommaaccent omacron -20
+KPX rcommaaccent oslash -20
+KPX rcommaaccent otilde -20
+KPX rcommaaccent period -60
+KPX rcommaaccent q -20
+KPX rcommaaccent s -15
+KPX rcommaaccent sacute -15
+KPX rcommaaccent scaron -15
+KPX rcommaaccent scedilla -15
+KPX rcommaaccent scommaaccent -15
+KPX rcommaaccent t 20
+KPX rcommaaccent tcommaaccent 20
+KPX rcommaaccent v 10
+KPX rcommaaccent y 10
+KPX rcommaaccent yacute 10
+KPX rcommaaccent ydieresis 10
+KPX s w -15
+KPX sacute w -15
+KPX scaron w -15
+KPX scedilla w -15
+KPX scommaaccent w -15
+KPX semicolon space -40
+KPX space T -100
+KPX space Tcaron -100
+KPX space Tcommaaccent -100
+KPX space V -80
+KPX space W -80
+KPX space Y -120
+KPX space Yacute -120
+KPX space Ydieresis -120
+KPX space quotedblleft -80
+KPX space quoteleft -60
+KPX v a -20
+KPX v aacute -20
+KPX v abreve -20
+KPX v acircumflex -20
+KPX v adieresis -20
+KPX v agrave -20
+KPX v amacron -20
+KPX v aogonek -20
+KPX v aring -20
+KPX v atilde -20
+KPX v comma -80
+KPX v o -30
+KPX v oacute -30
+KPX v ocircumflex -30
+KPX v odieresis -30
+KPX v ograve -30
+KPX v ohungarumlaut -30
+KPX v omacron -30
+KPX v oslash -30
+KPX v otilde -30
+KPX v period -80
+KPX w comma -40
+KPX w o -20
+KPX w oacute -20
+KPX w ocircumflex -20
+KPX w odieresis -20
+KPX w ograve -20
+KPX w ohungarumlaut -20
+KPX w omacron -20
+KPX w oslash -20
+KPX w otilde -20
+KPX w period -40
+KPX x e -10
+KPX x eacute -10
+KPX x ecaron -10
+KPX x ecircumflex -10
+KPX x edieresis -10
+KPX x edotaccent -10
+KPX x egrave -10
+KPX x emacron -10
+KPX x eogonek -10
+KPX y a -30
+KPX y aacute -30
+KPX y abreve -30
+KPX y acircumflex -30
+KPX y adieresis -30
+KPX y agrave -30
+KPX y amacron -30
+KPX y aogonek -30
+KPX y aring -30
+KPX y atilde -30
+KPX y comma -80
+KPX y e -10
+KPX y eacute -10
+KPX y ecaron -10
+KPX y ecircumflex -10
+KPX y edieresis -10
+KPX y edotaccent -10
+KPX y egrave -10
+KPX y emacron -10
+KPX y eogonek -10
+KPX y o -25
+KPX y oacute -25
+KPX y ocircumflex -25
+KPX y odieresis -25
+KPX y ograve -25
+KPX y ohungarumlaut -25
+KPX y omacron -25
+KPX y oslash -25
+KPX y otilde -25
+KPX y period -80
+KPX yacute a -30
+KPX yacute aacute -30
+KPX yacute abreve -30
+KPX yacute acircumflex -30
+KPX yacute adieresis -30
+KPX yacute agrave -30
+KPX yacute amacron -30
+KPX yacute aogonek -30
+KPX yacute aring -30
+KPX yacute atilde -30
+KPX yacute comma -80
+KPX yacute e -10
+KPX yacute eacute -10
+KPX yacute ecaron -10
+KPX yacute ecircumflex -10
+KPX yacute edieresis -10
+KPX yacute edotaccent -10
+KPX yacute egrave -10
+KPX yacute emacron -10
+KPX yacute eogonek -10
+KPX yacute o -25
+KPX yacute oacute -25
+KPX yacute ocircumflex -25
+KPX yacute odieresis -25
+KPX yacute ograve -25
+KPX yacute ohungarumlaut -25
+KPX yacute omacron -25
+KPX yacute oslash -25
+KPX yacute otilde -25
+KPX yacute period -80
+KPX ydieresis a -30
+KPX ydieresis aacute -30
+KPX ydieresis abreve -30
+KPX ydieresis acircumflex -30
+KPX ydieresis adieresis -30
+KPX ydieresis agrave -30
+KPX ydieresis amacron -30
+KPX ydieresis aogonek -30
+KPX ydieresis aring -30
+KPX ydieresis atilde -30
+KPX ydieresis comma -80
+KPX ydieresis e -10
+KPX ydieresis eacute -10
+KPX ydieresis ecaron -10
+KPX ydieresis ecircumflex -10
+KPX ydieresis edieresis -10
+KPX ydieresis edotaccent -10
+KPX ydieresis egrave -10
+KPX ydieresis emacron -10
+KPX ydieresis eogonek -10
+KPX ydieresis o -25
+KPX ydieresis oacute -25
+KPX ydieresis ocircumflex -25
+KPX ydieresis odieresis -25
+KPX ydieresis ograve -25
+KPX ydieresis ohungarumlaut -25
+KPX ydieresis omacron -25
+KPX ydieresis oslash -25
+KPX ydieresis otilde -25
+KPX ydieresis period -80
+KPX z e 10
+KPX z eacute 10
+KPX z ecaron 10
+KPX z ecircumflex 10
+KPX z edieresis 10
+KPX z edotaccent 10
+KPX z egrave 10
+KPX z emacron 10
+KPX z eogonek 10
+KPX zacute e 10
+KPX zacute eacute 10
+KPX zacute ecaron 10
+KPX zacute ecircumflex 10
+KPX zacute edieresis 10
+KPX zacute edotaccent 10
+KPX zacute egrave 10
+KPX zacute emacron 10
+KPX zacute eogonek 10
+KPX zcaron e 10
+KPX zcaron eacute 10
+KPX zcaron ecaron 10
+KPX zcaron ecircumflex 10
+KPX zcaron edieresis 10
+KPX zcaron edotaccent 10
+KPX zcaron egrave 10
+KPX zcaron emacron 10
+KPX zcaron eogonek 10
+KPX zdotaccent e 10
+KPX zdotaccent eacute 10
+KPX zdotaccent ecaron 10
+KPX zdotaccent ecircumflex 10
+KPX zdotaccent edieresis 10
+KPX zdotaccent edotaccent 10
+KPX zdotaccent egrave 10
+KPX zdotaccent emacron 10
+KPX zdotaccent eogonek 10
+EndKernPairs
+EndKernData
+EndFontMetrics
diff --git a/program/libraries/afm/Helvetica-Oblique.afm b/program/libraries/afm/Helvetica-Oblique.afm
--- /dev/null
@@ -0,0 +1,3051 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.
+Comment Creation Date: Thu May 1 12:44:31 1997
+Comment UniqueID 43055
+Comment VMusage 14960 69346
+FontName Helvetica-Oblique
+FullName Helvetica Oblique
+FamilyName Helvetica
+Weight Medium
+ItalicAngle -12
+IsFixedPitch false
+CharacterSet ExtendedRoman
+FontBBox -170 -225 1116 931
+UnderlinePosition -100
+UnderlineThickness 50
+Version 002.000
+Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries.
+EncodingScheme AdobeStandardEncoding
+CapHeight 718
+XHeight 523
+Ascender 718
+Descender -207
+StdHW 76
+StdVW 88
+StartCharMetrics 315
+C 32 ; WX 278 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 278 ; N exclam ; B 90 0 340 718 ;
+C 34 ; WX 355 ; N quotedbl ; B 168 463 438 718 ;
+C 35 ; WX 556 ; N numbersign ; B 73 0 631 688 ;
+C 36 ; WX 556 ; N dollar ; B 69 -115 617 775 ;
+C 37 ; WX 889 ; N percent ; B 147 -19 889 703 ;
+C 38 ; WX 667 ; N ampersand ; B 77 -15 647 718 ;
+C 39 ; WX 222 ; N quoteright ; B 151 463 310 718 ;
+C 40 ; WX 333 ; N parenleft ; B 108 -207 454 733 ;
+C 41 ; WX 333 ; N parenright ; B -9 -207 337 733 ;
+C 42 ; WX 389 ; N asterisk ; B 165 431 475 718 ;
+C 43 ; WX 584 ; N plus ; B 85 0 606 505 ;
+C 44 ; WX 278 ; N comma ; B 56 -147 214 106 ;
+C 45 ; WX 333 ; N hyphen ; B 93 232 357 322 ;
+C 46 ; WX 278 ; N period ; B 87 0 214 106 ;
+C 47 ; WX 278 ; N slash ; B -21 -19 452 737 ;
+C 48 ; WX 556 ; N zero ; B 93 -19 608 703 ;
+C 49 ; WX 556 ; N one ; B 207 0 508 703 ;
+C 50 ; WX 556 ; N two ; B 26 0 617 703 ;
+C 51 ; WX 556 ; N three ; B 75 -19 610 703 ;
+C 52 ; WX 556 ; N four ; B 61 0 576 703 ;
+C 53 ; WX 556 ; N five ; B 68 -19 621 688 ;
+C 54 ; WX 556 ; N six ; B 91 -19 615 703 ;
+C 55 ; WX 556 ; N seven ; B 137 0 669 688 ;
+C 56 ; WX 556 ; N eight ; B 74 -19 607 703 ;
+C 57 ; WX 556 ; N nine ; B 82 -19 609 703 ;
+C 58 ; WX 278 ; N colon ; B 87 0 301 516 ;
+C 59 ; WX 278 ; N semicolon ; B 56 -147 301 516 ;
+C 60 ; WX 584 ; N less ; B 94 11 641 495 ;
+C 61 ; WX 584 ; N equal ; B 63 115 628 390 ;
+C 62 ; WX 584 ; N greater ; B 50 11 597 495 ;
+C 63 ; WX 556 ; N question ; B 161 0 610 727 ;
+C 64 ; WX 1015 ; N at ; B 215 -19 965 737 ;
+C 65 ; WX 667 ; N A ; B 14 0 654 718 ;
+C 66 ; WX 667 ; N B ; B 74 0 712 718 ;
+C 67 ; WX 722 ; N C ; B 108 -19 782 737 ;
+C 68 ; WX 722 ; N D ; B 81 0 764 718 ;
+C 69 ; WX 667 ; N E ; B 86 0 762 718 ;
+C 70 ; WX 611 ; N F ; B 86 0 736 718 ;
+C 71 ; WX 778 ; N G ; B 111 -19 799 737 ;
+C 72 ; WX 722 ; N H ; B 77 0 799 718 ;
+C 73 ; WX 278 ; N I ; B 91 0 341 718 ;
+C 74 ; WX 500 ; N J ; B 47 -19 581 718 ;
+C 75 ; WX 667 ; N K ; B 76 0 808 718 ;
+C 76 ; WX 556 ; N L ; B 76 0 555 718 ;
+C 77 ; WX 833 ; N M ; B 73 0 914 718 ;
+C 78 ; WX 722 ; N N ; B 76 0 799 718 ;
+C 79 ; WX 778 ; N O ; B 105 -19 826 737 ;
+C 80 ; WX 667 ; N P ; B 86 0 737 718 ;
+C 81 ; WX 778 ; N Q ; B 105 -56 826 737 ;
+C 82 ; WX 722 ; N R ; B 88 0 773 718 ;
+C 83 ; WX 667 ; N S ; B 90 -19 713 737 ;
+C 84 ; WX 611 ; N T ; B 148 0 750 718 ;
+C 85 ; WX 722 ; N U ; B 123 -19 797 718 ;
+C 86 ; WX 667 ; N V ; B 173 0 800 718 ;
+C 87 ; WX 944 ; N W ; B 169 0 1081 718 ;
+C 88 ; WX 667 ; N X ; B 19 0 790 718 ;
+C 89 ; WX 667 ; N Y ; B 167 0 806 718 ;
+C 90 ; WX 611 ; N Z ; B 23 0 741 718 ;
+C 91 ; WX 278 ; N bracketleft ; B 21 -196 403 722 ;
+C 92 ; WX 278 ; N backslash ; B 140 -19 291 737 ;
+C 93 ; WX 278 ; N bracketright ; B -14 -196 368 722 ;
+C 94 ; WX 469 ; N asciicircum ; B 42 264 539 688 ;
+C 95 ; WX 556 ; N underscore ; B -27 -125 540 -75 ;
+C 96 ; WX 222 ; N quoteleft ; B 165 470 323 725 ;
+C 97 ; WX 556 ; N a ; B 61 -15 559 538 ;
+C 98 ; WX 556 ; N b ; B 58 -15 584 718 ;
+C 99 ; WX 500 ; N c ; B 74 -15 553 538 ;
+C 100 ; WX 556 ; N d ; B 84 -15 652 718 ;
+C 101 ; WX 556 ; N e ; B 84 -15 578 538 ;
+C 102 ; WX 278 ; N f ; B 86 0 416 728 ; L i fi ; L l fl ;
+C 103 ; WX 556 ; N g ; B 42 -220 610 538 ;
+C 104 ; WX 556 ; N h ; B 65 0 573 718 ;
+C 105 ; WX 222 ; N i ; B 67 0 308 718 ;
+C 106 ; WX 222 ; N j ; B -60 -210 308 718 ;
+C 107 ; WX 500 ; N k ; B 67 0 600 718 ;
+C 108 ; WX 222 ; N l ; B 67 0 308 718 ;
+C 109 ; WX 833 ; N m ; B 65 0 852 538 ;
+C 110 ; WX 556 ; N n ; B 65 0 573 538 ;
+C 111 ; WX 556 ; N o ; B 83 -14 585 538 ;
+C 112 ; WX 556 ; N p ; B 14 -207 584 538 ;
+C 113 ; WX 556 ; N q ; B 84 -207 605 538 ;
+C 114 ; WX 333 ; N r ; B 77 0 446 538 ;
+C 115 ; WX 500 ; N s ; B 63 -15 529 538 ;
+C 116 ; WX 278 ; N t ; B 102 -7 368 669 ;
+C 117 ; WX 556 ; N u ; B 94 -15 600 523 ;
+C 118 ; WX 500 ; N v ; B 119 0 603 523 ;
+C 119 ; WX 722 ; N w ; B 125 0 820 523 ;
+C 120 ; WX 500 ; N x ; B 11 0 594 523 ;
+C 121 ; WX 500 ; N y ; B 15 -214 600 523 ;
+C 122 ; WX 500 ; N z ; B 31 0 571 523 ;
+C 123 ; WX 334 ; N braceleft ; B 92 -196 445 722 ;
+C 124 ; WX 260 ; N bar ; B 46 -225 332 775 ;
+C 125 ; WX 334 ; N braceright ; B 0 -196 354 722 ;
+C 126 ; WX 584 ; N asciitilde ; B 111 180 580 326 ;
+C 161 ; WX 333 ; N exclamdown ; B 77 -195 326 523 ;
+C 162 ; WX 556 ; N cent ; B 95 -115 584 623 ;
+C 163 ; WX 556 ; N sterling ; B 49 -16 634 718 ;
+C 164 ; WX 167 ; N fraction ; B -170 -19 482 703 ;
+C 165 ; WX 556 ; N yen ; B 81 0 699 688 ;
+C 166 ; WX 556 ; N florin ; B -52 -207 654 737 ;
+C 167 ; WX 556 ; N section ; B 76 -191 584 737 ;
+C 168 ; WX 556 ; N currency ; B 60 99 646 603 ;
+C 169 ; WX 191 ; N quotesingle ; B 157 463 285 718 ;
+C 170 ; WX 333 ; N quotedblleft ; B 138 470 461 725 ;
+C 171 ; WX 556 ; N guillemotleft ; B 146 108 554 446 ;
+C 172 ; WX 333 ; N guilsinglleft ; B 137 108 340 446 ;
+C 173 ; WX 333 ; N guilsinglright ; B 111 108 314 446 ;
+C 174 ; WX 500 ; N fi ; B 86 0 587 728 ;
+C 175 ; WX 500 ; N fl ; B 86 0 585 728 ;
+C 177 ; WX 556 ; N endash ; B 51 240 623 313 ;
+C 178 ; WX 556 ; N dagger ; B 135 -159 622 718 ;
+C 179 ; WX 556 ; N daggerdbl ; B 52 -159 623 718 ;
+C 180 ; WX 278 ; N periodcentered ; B 129 190 257 315 ;
+C 182 ; WX 537 ; N paragraph ; B 126 -173 650 718 ;
+C 183 ; WX 350 ; N bullet ; B 91 202 413 517 ;
+C 184 ; WX 222 ; N quotesinglbase ; B 21 -149 180 106 ;
+C 185 ; WX 333 ; N quotedblbase ; B -6 -149 318 106 ;
+C 186 ; WX 333 ; N quotedblright ; B 124 463 448 718 ;
+C 187 ; WX 556 ; N guillemotright ; B 120 108 528 446 ;
+C 188 ; WX 1000 ; N ellipsis ; B 115 0 908 106 ;
+C 189 ; WX 1000 ; N perthousand ; B 88 -19 1029 703 ;
+C 191 ; WX 611 ; N questiondown ; B 85 -201 534 525 ;
+C 193 ; WX 333 ; N grave ; B 170 593 337 734 ;
+C 194 ; WX 333 ; N acute ; B 248 593 475 734 ;
+C 195 ; WX 333 ; N circumflex ; B 147 593 438 734 ;
+C 196 ; WX 333 ; N tilde ; B 125 606 490 722 ;
+C 197 ; WX 333 ; N macron ; B 143 627 468 684 ;
+C 198 ; WX 333 ; N breve ; B 167 595 476 731 ;
+C 199 ; WX 333 ; N dotaccent ; B 249 604 362 706 ;
+C 200 ; WX 333 ; N dieresis ; B 168 604 443 706 ;
+C 202 ; WX 333 ; N ring ; B 214 572 402 756 ;
+C 203 ; WX 333 ; N cedilla ; B 2 -225 232 0 ;
+C 205 ; WX 333 ; N hungarumlaut ; B 157 593 565 734 ;
+C 206 ; WX 333 ; N ogonek ; B 43 -225 249 0 ;
+C 207 ; WX 333 ; N caron ; B 177 593 468 734 ;
+C 208 ; WX 1000 ; N emdash ; B 51 240 1067 313 ;
+C 225 ; WX 1000 ; N AE ; B 8 0 1097 718 ;
+C 227 ; WX 370 ; N ordfeminine ; B 127 405 449 737 ;
+C 232 ; WX 556 ; N Lslash ; B 41 0 555 718 ;
+C 233 ; WX 778 ; N Oslash ; B 43 -19 890 737 ;
+C 234 ; WX 1000 ; N OE ; B 98 -19 1116 737 ;
+C 235 ; WX 365 ; N ordmasculine ; B 141 405 468 737 ;
+C 241 ; WX 889 ; N ae ; B 61 -15 909 538 ;
+C 245 ; WX 278 ; N dotlessi ; B 95 0 294 523 ;
+C 248 ; WX 222 ; N lslash ; B 41 0 347 718 ;
+C 249 ; WX 611 ; N oslash ; B 29 -22 647 545 ;
+C 250 ; WX 944 ; N oe ; B 83 -15 964 538 ;
+C 251 ; WX 611 ; N germandbls ; B 67 -15 658 728 ;
+C -1 ; WX 278 ; N Idieresis ; B 91 0 458 901 ;
+C -1 ; WX 556 ; N eacute ; B 84 -15 587 734 ;
+C -1 ; WX 556 ; N abreve ; B 61 -15 578 731 ;
+C -1 ; WX 556 ; N uhungarumlaut ; B 94 -15 677 734 ;
+C -1 ; WX 556 ; N ecaron ; B 84 -15 580 734 ;
+C -1 ; WX 667 ; N Ydieresis ; B 167 0 806 901 ;
+C -1 ; WX 584 ; N divide ; B 85 -19 606 524 ;
+C -1 ; WX 667 ; N Yacute ; B 167 0 806 929 ;
+C -1 ; WX 667 ; N Acircumflex ; B 14 0 654 929 ;
+C -1 ; WX 556 ; N aacute ; B 61 -15 587 734 ;
+C -1 ; WX 722 ; N Ucircumflex ; B 123 -19 797 929 ;
+C -1 ; WX 500 ; N yacute ; B 15 -214 600 734 ;
+C -1 ; WX 500 ; N scommaaccent ; B 63 -225 529 538 ;
+C -1 ; WX 556 ; N ecircumflex ; B 84 -15 578 734 ;
+C -1 ; WX 722 ; N Uring ; B 123 -19 797 931 ;
+C -1 ; WX 722 ; N Udieresis ; B 123 -19 797 901 ;
+C -1 ; WX 556 ; N aogonek ; B 61 -220 559 538 ;
+C -1 ; WX 722 ; N Uacute ; B 123 -19 797 929 ;
+C -1 ; WX 556 ; N uogonek ; B 94 -225 600 523 ;
+C -1 ; WX 667 ; N Edieresis ; B 86 0 762 901 ;
+C -1 ; WX 722 ; N Dcroat ; B 69 0 764 718 ;
+C -1 ; WX 250 ; N commaaccent ; B 39 -225 172 -40 ;
+C -1 ; WX 737 ; N copyright ; B 54 -19 837 737 ;
+C -1 ; WX 667 ; N Emacron ; B 86 0 762 879 ;
+C -1 ; WX 500 ; N ccaron ; B 74 -15 553 734 ;
+C -1 ; WX 556 ; N aring ; B 61 -15 559 756 ;
+C -1 ; WX 722 ; N Ncommaaccent ; B 76 -225 799 718 ;
+C -1 ; WX 222 ; N lacute ; B 67 0 461 929 ;
+C -1 ; WX 556 ; N agrave ; B 61 -15 559 734 ;
+C -1 ; WX 611 ; N Tcommaaccent ; B 148 -225 750 718 ;
+C -1 ; WX 722 ; N Cacute ; B 108 -19 782 929 ;
+C -1 ; WX 556 ; N atilde ; B 61 -15 592 722 ;
+C -1 ; WX 667 ; N Edotaccent ; B 86 0 762 901 ;
+C -1 ; WX 500 ; N scaron ; B 63 -15 552 734 ;
+C -1 ; WX 500 ; N scedilla ; B 63 -225 529 538 ;
+C -1 ; WX 278 ; N iacute ; B 95 0 448 734 ;
+C -1 ; WX 471 ; N lozenge ; B 88 0 540 728 ;
+C -1 ; WX 722 ; N Rcaron ; B 88 0 773 929 ;
+C -1 ; WX 778 ; N Gcommaaccent ; B 111 -225 799 737 ;
+C -1 ; WX 556 ; N ucircumflex ; B 94 -15 600 734 ;
+C -1 ; WX 556 ; N acircumflex ; B 61 -15 559 734 ;
+C -1 ; WX 667 ; N Amacron ; B 14 0 677 879 ;
+C -1 ; WX 333 ; N rcaron ; B 77 0 508 734 ;
+C -1 ; WX 500 ; N ccedilla ; B 74 -225 553 538 ;
+C -1 ; WX 611 ; N Zdotaccent ; B 23 0 741 901 ;
+C -1 ; WX 667 ; N Thorn ; B 86 0 712 718 ;
+C -1 ; WX 778 ; N Omacron ; B 105 -19 826 879 ;
+C -1 ; WX 722 ; N Racute ; B 88 0 773 929 ;
+C -1 ; WX 667 ; N Sacute ; B 90 -19 713 929 ;
+C -1 ; WX 643 ; N dcaron ; B 84 -15 808 718 ;
+C -1 ; WX 722 ; N Umacron ; B 123 -19 797 879 ;
+C -1 ; WX 556 ; N uring ; B 94 -15 600 756 ;
+C -1 ; WX 333 ; N threesuperior ; B 90 270 436 703 ;
+C -1 ; WX 778 ; N Ograve ; B 105 -19 826 929 ;
+C -1 ; WX 667 ; N Agrave ; B 14 0 654 929 ;
+C -1 ; WX 667 ; N Abreve ; B 14 0 685 926 ;
+C -1 ; WX 584 ; N multiply ; B 50 0 642 506 ;
+C -1 ; WX 556 ; N uacute ; B 94 -15 600 734 ;
+C -1 ; WX 611 ; N Tcaron ; B 148 0 750 929 ;
+C -1 ; WX 476 ; N partialdiff ; B 41 -38 550 714 ;
+C -1 ; WX 500 ; N ydieresis ; B 15 -214 600 706 ;
+C -1 ; WX 722 ; N Nacute ; B 76 0 799 929 ;
+C -1 ; WX 278 ; N icircumflex ; B 95 0 411 734 ;
+C -1 ; WX 667 ; N Ecircumflex ; B 86 0 762 929 ;
+C -1 ; WX 556 ; N adieresis ; B 61 -15 559 706 ;
+C -1 ; WX 556 ; N edieresis ; B 84 -15 578 706 ;
+C -1 ; WX 500 ; N cacute ; B 74 -15 559 734 ;
+C -1 ; WX 556 ; N nacute ; B 65 0 587 734 ;
+C -1 ; WX 556 ; N umacron ; B 94 -15 600 684 ;
+C -1 ; WX 722 ; N Ncaron ; B 76 0 799 929 ;
+C -1 ; WX 278 ; N Iacute ; B 91 0 489 929 ;
+C -1 ; WX 584 ; N plusminus ; B 39 0 618 506 ;
+C -1 ; WX 260 ; N brokenbar ; B 62 -150 316 700 ;
+C -1 ; WX 737 ; N registered ; B 54 -19 837 737 ;
+C -1 ; WX 778 ; N Gbreve ; B 111 -19 799 926 ;
+C -1 ; WX 278 ; N Idotaccent ; B 91 0 377 901 ;
+C -1 ; WX 600 ; N summation ; B 15 -10 671 706 ;
+C -1 ; WX 667 ; N Egrave ; B 86 0 762 929 ;
+C -1 ; WX 333 ; N racute ; B 77 0 475 734 ;
+C -1 ; WX 556 ; N omacron ; B 83 -14 585 684 ;
+C -1 ; WX 611 ; N Zacute ; B 23 0 741 929 ;
+C -1 ; WX 611 ; N Zcaron ; B 23 0 741 929 ;
+C -1 ; WX 549 ; N greaterequal ; B 26 0 620 674 ;
+C -1 ; WX 722 ; N Eth ; B 69 0 764 718 ;
+C -1 ; WX 722 ; N Ccedilla ; B 108 -225 782 737 ;
+C -1 ; WX 222 ; N lcommaaccent ; B 25 -225 308 718 ;
+C -1 ; WX 317 ; N tcaron ; B 102 -7 501 808 ;
+C -1 ; WX 556 ; N eogonek ; B 84 -225 578 538 ;
+C -1 ; WX 722 ; N Uogonek ; B 123 -225 797 718 ;
+C -1 ; WX 667 ; N Aacute ; B 14 0 683 929 ;
+C -1 ; WX 667 ; N Adieresis ; B 14 0 654 901 ;
+C -1 ; WX 556 ; N egrave ; B 84 -15 578 734 ;
+C -1 ; WX 500 ; N zacute ; B 31 0 571 734 ;
+C -1 ; WX 222 ; N iogonek ; B -61 -225 308 718 ;
+C -1 ; WX 778 ; N Oacute ; B 105 -19 826 929 ;
+C -1 ; WX 556 ; N oacute ; B 83 -14 587 734 ;
+C -1 ; WX 556 ; N amacron ; B 61 -15 580 684 ;
+C -1 ; WX 500 ; N sacute ; B 63 -15 559 734 ;
+C -1 ; WX 278 ; N idieresis ; B 95 0 416 706 ;
+C -1 ; WX 778 ; N Ocircumflex ; B 105 -19 826 929 ;
+C -1 ; WX 722 ; N Ugrave ; B 123 -19 797 929 ;
+C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ;
+C -1 ; WX 556 ; N thorn ; B 14 -207 584 718 ;
+C -1 ; WX 333 ; N twosuperior ; B 64 281 449 703 ;
+C -1 ; WX 778 ; N Odieresis ; B 105 -19 826 901 ;
+C -1 ; WX 556 ; N mu ; B 24 -207 600 523 ;
+C -1 ; WX 278 ; N igrave ; B 95 0 310 734 ;
+C -1 ; WX 556 ; N ohungarumlaut ; B 83 -14 677 734 ;
+C -1 ; WX 667 ; N Eogonek ; B 86 -220 762 718 ;
+C -1 ; WX 556 ; N dcroat ; B 84 -15 689 718 ;
+C -1 ; WX 834 ; N threequarters ; B 130 -19 861 703 ;
+C -1 ; WX 667 ; N Scedilla ; B 90 -225 713 737 ;
+C -1 ; WX 299 ; N lcaron ; B 67 0 464 718 ;
+C -1 ; WX 667 ; N Kcommaaccent ; B 76 -225 808 718 ;
+C -1 ; WX 556 ; N Lacute ; B 76 0 555 929 ;
+C -1 ; WX 1000 ; N trademark ; B 186 306 1056 718 ;
+C -1 ; WX 556 ; N edotaccent ; B 84 -15 578 706 ;
+C -1 ; WX 278 ; N Igrave ; B 91 0 351 929 ;
+C -1 ; WX 278 ; N Imacron ; B 91 0 483 879 ;
+C -1 ; WX 556 ; N Lcaron ; B 76 0 570 718 ;
+C -1 ; WX 834 ; N onehalf ; B 114 -19 839 703 ;
+C -1 ; WX 549 ; N lessequal ; B 26 0 666 674 ;
+C -1 ; WX 556 ; N ocircumflex ; B 83 -14 585 734 ;
+C -1 ; WX 556 ; N ntilde ; B 65 0 592 722 ;
+C -1 ; WX 722 ; N Uhungarumlaut ; B 123 -19 801 929 ;
+C -1 ; WX 667 ; N Eacute ; B 86 0 762 929 ;
+C -1 ; WX 556 ; N emacron ; B 84 -15 580 684 ;
+C -1 ; WX 556 ; N gbreve ; B 42 -220 610 731 ;
+C -1 ; WX 834 ; N onequarter ; B 150 -19 802 703 ;
+C -1 ; WX 667 ; N Scaron ; B 90 -19 713 929 ;
+C -1 ; WX 667 ; N Scommaaccent ; B 90 -225 713 737 ;
+C -1 ; WX 778 ; N Ohungarumlaut ; B 105 -19 829 929 ;
+C -1 ; WX 400 ; N degree ; B 169 411 468 703 ;
+C -1 ; WX 556 ; N ograve ; B 83 -14 585 734 ;
+C -1 ; WX 722 ; N Ccaron ; B 108 -19 782 929 ;
+C -1 ; WX 556 ; N ugrave ; B 94 -15 600 734 ;
+C -1 ; WX 453 ; N radical ; B 79 -80 617 762 ;
+C -1 ; WX 722 ; N Dcaron ; B 81 0 764 929 ;
+C -1 ; WX 333 ; N rcommaaccent ; B 30 -225 446 538 ;
+C -1 ; WX 722 ; N Ntilde ; B 76 0 799 917 ;
+C -1 ; WX 556 ; N otilde ; B 83 -14 602 722 ;
+C -1 ; WX 722 ; N Rcommaaccent ; B 88 -225 773 718 ;
+C -1 ; WX 556 ; N Lcommaaccent ; B 76 -225 555 718 ;
+C -1 ; WX 667 ; N Atilde ; B 14 0 699 917 ;
+C -1 ; WX 667 ; N Aogonek ; B 14 -225 654 718 ;
+C -1 ; WX 667 ; N Aring ; B 14 0 654 931 ;
+C -1 ; WX 778 ; N Otilde ; B 105 -19 826 917 ;
+C -1 ; WX 500 ; N zdotaccent ; B 31 0 571 706 ;
+C -1 ; WX 667 ; N Ecaron ; B 86 0 762 929 ;
+C -1 ; WX 278 ; N Iogonek ; B -33 -225 341 718 ;
+C -1 ; WX 500 ; N kcommaaccent ; B 67 -225 600 718 ;
+C -1 ; WX 584 ; N minus ; B 85 216 606 289 ;
+C -1 ; WX 278 ; N Icircumflex ; B 91 0 452 929 ;
+C -1 ; WX 556 ; N ncaron ; B 65 0 580 734 ;
+C -1 ; WX 278 ; N tcommaaccent ; B 63 -225 368 669 ;
+C -1 ; WX 584 ; N logicalnot ; B 106 108 628 390 ;
+C -1 ; WX 556 ; N odieresis ; B 83 -14 585 706 ;
+C -1 ; WX 556 ; N udieresis ; B 94 -15 600 706 ;
+C -1 ; WX 549 ; N notequal ; B 34 -35 623 551 ;
+C -1 ; WX 556 ; N gcommaaccent ; B 42 -220 610 822 ;
+C -1 ; WX 556 ; N eth ; B 81 -15 617 737 ;
+C -1 ; WX 500 ; N zcaron ; B 31 0 571 734 ;
+C -1 ; WX 556 ; N ncommaaccent ; B 65 -225 573 538 ;
+C -1 ; WX 333 ; N onesuperior ; B 166 281 371 703 ;
+C -1 ; WX 278 ; N imacron ; B 95 0 417 684 ;
+C -1 ; WX 556 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+StartKernData
+StartKernPairs 2705
+KPX A C -30
+KPX A Cacute -30
+KPX A Ccaron -30
+KPX A Ccedilla -30
+KPX A G -30
+KPX A Gbreve -30
+KPX A Gcommaaccent -30
+KPX A O -30
+KPX A Oacute -30
+KPX A Ocircumflex -30
+KPX A Odieresis -30
+KPX A Ograve -30
+KPX A Ohungarumlaut -30
+KPX A Omacron -30
+KPX A Oslash -30
+KPX A Otilde -30
+KPX A Q -30
+KPX A T -120
+KPX A Tcaron -120
+KPX A Tcommaaccent -120
+KPX A U -50
+KPX A Uacute -50
+KPX A Ucircumflex -50
+KPX A Udieresis -50
+KPX A Ugrave -50
+KPX A Uhungarumlaut -50
+KPX A Umacron -50
+KPX A Uogonek -50
+KPX A Uring -50
+KPX A V -70
+KPX A W -50
+KPX A Y -100
+KPX A Yacute -100
+KPX A Ydieresis -100
+KPX A u -30
+KPX A uacute -30
+KPX A ucircumflex -30
+KPX A udieresis -30
+KPX A ugrave -30
+KPX A uhungarumlaut -30
+KPX A umacron -30
+KPX A uogonek -30
+KPX A uring -30
+KPX A v -40
+KPX A w -40
+KPX A y -40
+KPX A yacute -40
+KPX A ydieresis -40
+KPX Aacute C -30
+KPX Aacute Cacute -30
+KPX Aacute Ccaron -30
+KPX Aacute Ccedilla -30
+KPX Aacute G -30
+KPX Aacute Gbreve -30
+KPX Aacute Gcommaaccent -30
+KPX Aacute O -30
+KPX Aacute Oacute -30
+KPX Aacute Ocircumflex -30
+KPX Aacute Odieresis -30
+KPX Aacute Ograve -30
+KPX Aacute Ohungarumlaut -30
+KPX Aacute Omacron -30
+KPX Aacute Oslash -30
+KPX Aacute Otilde -30
+KPX Aacute Q -30
+KPX Aacute T -120
+KPX Aacute Tcaron -120
+KPX Aacute Tcommaaccent -120
+KPX Aacute U -50
+KPX Aacute Uacute -50
+KPX Aacute Ucircumflex -50
+KPX Aacute Udieresis -50
+KPX Aacute Ugrave -50
+KPX Aacute Uhungarumlaut -50
+KPX Aacute Umacron -50
+KPX Aacute Uogonek -50
+KPX Aacute Uring -50
+KPX Aacute V -70
+KPX Aacute W -50
+KPX Aacute Y -100
+KPX Aacute Yacute -100
+KPX Aacute Ydieresis -100
+KPX Aacute u -30
+KPX Aacute uacute -30
+KPX Aacute ucircumflex -30
+KPX Aacute udieresis -30
+KPX Aacute ugrave -30
+KPX Aacute uhungarumlaut -30
+KPX Aacute umacron -30
+KPX Aacute uogonek -30
+KPX Aacute uring -30
+KPX Aacute v -40
+KPX Aacute w -40
+KPX Aacute y -40
+KPX Aacute yacute -40
+KPX Aacute ydieresis -40
+KPX Abreve C -30
+KPX Abreve Cacute -30
+KPX Abreve Ccaron -30
+KPX Abreve Ccedilla -30
+KPX Abreve G -30
+KPX Abreve Gbreve -30
+KPX Abreve Gcommaaccent -30
+KPX Abreve O -30
+KPX Abreve Oacute -30
+KPX Abreve Ocircumflex -30
+KPX Abreve Odieresis -30
+KPX Abreve Ograve -30
+KPX Abreve Ohungarumlaut -30
+KPX Abreve Omacron -30
+KPX Abreve Oslash -30
+KPX Abreve Otilde -30
+KPX Abreve Q -30
+KPX Abreve T -120
+KPX Abreve Tcaron -120
+KPX Abreve Tcommaaccent -120
+KPX Abreve U -50
+KPX Abreve Uacute -50
+KPX Abreve Ucircumflex -50
+KPX Abreve Udieresis -50
+KPX Abreve Ugrave -50
+KPX Abreve Uhungarumlaut -50
+KPX Abreve Umacron -50
+KPX Abreve Uogonek -50
+KPX Abreve Uring -50
+KPX Abreve V -70
+KPX Abreve W -50
+KPX Abreve Y -100
+KPX Abreve Yacute -100
+KPX Abreve Ydieresis -100
+KPX Abreve u -30
+KPX Abreve uacute -30
+KPX Abreve ucircumflex -30
+KPX Abreve udieresis -30
+KPX Abreve ugrave -30
+KPX Abreve uhungarumlaut -30
+KPX Abreve umacron -30
+KPX Abreve uogonek -30
+KPX Abreve uring -30
+KPX Abreve v -40
+KPX Abreve w -40
+KPX Abreve y -40
+KPX Abreve yacute -40
+KPX Abreve ydieresis -40
+KPX Acircumflex C -30
+KPX Acircumflex Cacute -30
+KPX Acircumflex Ccaron -30
+KPX Acircumflex Ccedilla -30
+KPX Acircumflex G -30
+KPX Acircumflex Gbreve -30
+KPX Acircumflex Gcommaaccent -30
+KPX Acircumflex O -30
+KPX Acircumflex Oacute -30
+KPX Acircumflex Ocircumflex -30
+KPX Acircumflex Odieresis -30
+KPX Acircumflex Ograve -30
+KPX Acircumflex Ohungarumlaut -30
+KPX Acircumflex Omacron -30
+KPX Acircumflex Oslash -30
+KPX Acircumflex Otilde -30
+KPX Acircumflex Q -30
+KPX Acircumflex T -120
+KPX Acircumflex Tcaron -120
+KPX Acircumflex Tcommaaccent -120
+KPX Acircumflex U -50
+KPX Acircumflex Uacute -50
+KPX Acircumflex Ucircumflex -50
+KPX Acircumflex Udieresis -50
+KPX Acircumflex Ugrave -50
+KPX Acircumflex Uhungarumlaut -50
+KPX Acircumflex Umacron -50
+KPX Acircumflex Uogonek -50
+KPX Acircumflex Uring -50
+KPX Acircumflex V -70
+KPX Acircumflex W -50
+KPX Acircumflex Y -100
+KPX Acircumflex Yacute -100
+KPX Acircumflex Ydieresis -100
+KPX Acircumflex u -30
+KPX Acircumflex uacute -30
+KPX Acircumflex ucircumflex -30
+KPX Acircumflex udieresis -30
+KPX Acircumflex ugrave -30
+KPX Acircumflex uhungarumlaut -30
+KPX Acircumflex umacron -30
+KPX Acircumflex uogonek -30
+KPX Acircumflex uring -30
+KPX Acircumflex v -40
+KPX Acircumflex w -40
+KPX Acircumflex y -40
+KPX Acircumflex yacute -40
+KPX Acircumflex ydieresis -40
+KPX Adieresis C -30
+KPX Adieresis Cacute -30
+KPX Adieresis Ccaron -30
+KPX Adieresis Ccedilla -30
+KPX Adieresis G -30
+KPX Adieresis Gbreve -30
+KPX Adieresis Gcommaaccent -30
+KPX Adieresis O -30
+KPX Adieresis Oacute -30
+KPX Adieresis Ocircumflex -30
+KPX Adieresis Odieresis -30
+KPX Adieresis Ograve -30
+KPX Adieresis Ohungarumlaut -30
+KPX Adieresis Omacron -30
+KPX Adieresis Oslash -30
+KPX Adieresis Otilde -30
+KPX Adieresis Q -30
+KPX Adieresis T -120
+KPX Adieresis Tcaron -120
+KPX Adieresis Tcommaaccent -120
+KPX Adieresis U -50
+KPX Adieresis Uacute -50
+KPX Adieresis Ucircumflex -50
+KPX Adieresis Udieresis -50
+KPX Adieresis Ugrave -50
+KPX Adieresis Uhungarumlaut -50
+KPX Adieresis Umacron -50
+KPX Adieresis Uogonek -50
+KPX Adieresis Uring -50
+KPX Adieresis V -70
+KPX Adieresis W -50
+KPX Adieresis Y -100
+KPX Adieresis Yacute -100
+KPX Adieresis Ydieresis -100
+KPX Adieresis u -30
+KPX Adieresis uacute -30
+KPX Adieresis ucircumflex -30
+KPX Adieresis udieresis -30
+KPX Adieresis ugrave -30
+KPX Adieresis uhungarumlaut -30
+KPX Adieresis umacron -30
+KPX Adieresis uogonek -30
+KPX Adieresis uring -30
+KPX Adieresis v -40
+KPX Adieresis w -40
+KPX Adieresis y -40
+KPX Adieresis yacute -40
+KPX Adieresis ydieresis -40
+KPX Agrave C -30
+KPX Agrave Cacute -30
+KPX Agrave Ccaron -30
+KPX Agrave Ccedilla -30
+KPX Agrave G -30
+KPX Agrave Gbreve -30
+KPX Agrave Gcommaaccent -30
+KPX Agrave O -30
+KPX Agrave Oacute -30
+KPX Agrave Ocircumflex -30
+KPX Agrave Odieresis -30
+KPX Agrave Ograve -30
+KPX Agrave Ohungarumlaut -30
+KPX Agrave Omacron -30
+KPX Agrave Oslash -30
+KPX Agrave Otilde -30
+KPX Agrave Q -30
+KPX Agrave T -120
+KPX Agrave Tcaron -120
+KPX Agrave Tcommaaccent -120
+KPX Agrave U -50
+KPX Agrave Uacute -50
+KPX Agrave Ucircumflex -50
+KPX Agrave Udieresis -50
+KPX Agrave Ugrave -50
+KPX Agrave Uhungarumlaut -50
+KPX Agrave Umacron -50
+KPX Agrave Uogonek -50
+KPX Agrave Uring -50
+KPX Agrave V -70
+KPX Agrave W -50
+KPX Agrave Y -100
+KPX Agrave Yacute -100
+KPX Agrave Ydieresis -100
+KPX Agrave u -30
+KPX Agrave uacute -30
+KPX Agrave ucircumflex -30
+KPX Agrave udieresis -30
+KPX Agrave ugrave -30
+KPX Agrave uhungarumlaut -30
+KPX Agrave umacron -30
+KPX Agrave uogonek -30
+KPX Agrave uring -30
+KPX Agrave v -40
+KPX Agrave w -40
+KPX Agrave y -40
+KPX Agrave yacute -40
+KPX Agrave ydieresis -40
+KPX Amacron C -30
+KPX Amacron Cacute -30
+KPX Amacron Ccaron -30
+KPX Amacron Ccedilla -30
+KPX Amacron G -30
+KPX Amacron Gbreve -30
+KPX Amacron Gcommaaccent -30
+KPX Amacron O -30
+KPX Amacron Oacute -30
+KPX Amacron Ocircumflex -30
+KPX Amacron Odieresis -30
+KPX Amacron Ograve -30
+KPX Amacron Ohungarumlaut -30
+KPX Amacron Omacron -30
+KPX Amacron Oslash -30
+KPX Amacron Otilde -30
+KPX Amacron Q -30
+KPX Amacron T -120
+KPX Amacron Tcaron -120
+KPX Amacron Tcommaaccent -120
+KPX Amacron U -50
+KPX Amacron Uacute -50
+KPX Amacron Ucircumflex -50
+KPX Amacron Udieresis -50
+KPX Amacron Ugrave -50
+KPX Amacron Uhungarumlaut -50
+KPX Amacron Umacron -50
+KPX Amacron Uogonek -50
+KPX Amacron Uring -50
+KPX Amacron V -70
+KPX Amacron W -50
+KPX Amacron Y -100
+KPX Amacron Yacute -100
+KPX Amacron Ydieresis -100
+KPX Amacron u -30
+KPX Amacron uacute -30
+KPX Amacron ucircumflex -30
+KPX Amacron udieresis -30
+KPX Amacron ugrave -30
+KPX Amacron uhungarumlaut -30
+KPX Amacron umacron -30
+KPX Amacron uogonek -30
+KPX Amacron uring -30
+KPX Amacron v -40
+KPX Amacron w -40
+KPX Amacron y -40
+KPX Amacron yacute -40
+KPX Amacron ydieresis -40
+KPX Aogonek C -30
+KPX Aogonek Cacute -30
+KPX Aogonek Ccaron -30
+KPX Aogonek Ccedilla -30
+KPX Aogonek G -30
+KPX Aogonek Gbreve -30
+KPX Aogonek Gcommaaccent -30
+KPX Aogonek O -30
+KPX Aogonek Oacute -30
+KPX Aogonek Ocircumflex -30
+KPX Aogonek Odieresis -30
+KPX Aogonek Ograve -30
+KPX Aogonek Ohungarumlaut -30
+KPX Aogonek Omacron -30
+KPX Aogonek Oslash -30
+KPX Aogonek Otilde -30
+KPX Aogonek Q -30
+KPX Aogonek T -120
+KPX Aogonek Tcaron -120
+KPX Aogonek Tcommaaccent -120
+KPX Aogonek U -50
+KPX Aogonek Uacute -50
+KPX Aogonek Ucircumflex -50
+KPX Aogonek Udieresis -50
+KPX Aogonek Ugrave -50
+KPX Aogonek Uhungarumlaut -50
+KPX Aogonek Umacron -50
+KPX Aogonek Uogonek -50
+KPX Aogonek Uring -50
+KPX Aogonek V -70
+KPX Aogonek W -50
+KPX Aogonek Y -100
+KPX Aogonek Yacute -100
+KPX Aogonek Ydieresis -100
+KPX Aogonek u -30
+KPX Aogonek uacute -30
+KPX Aogonek ucircumflex -30
+KPX Aogonek udieresis -30
+KPX Aogonek ugrave -30
+KPX Aogonek uhungarumlaut -30
+KPX Aogonek umacron -30
+KPX Aogonek uogonek -30
+KPX Aogonek uring -30
+KPX Aogonek v -40
+KPX Aogonek w -40
+KPX Aogonek y -40
+KPX Aogonek yacute -40
+KPX Aogonek ydieresis -40
+KPX Aring C -30
+KPX Aring Cacute -30
+KPX Aring Ccaron -30
+KPX Aring Ccedilla -30
+KPX Aring G -30
+KPX Aring Gbreve -30
+KPX Aring Gcommaaccent -30
+KPX Aring O -30
+KPX Aring Oacute -30
+KPX Aring Ocircumflex -30
+KPX Aring Odieresis -30
+KPX Aring Ograve -30
+KPX Aring Ohungarumlaut -30
+KPX Aring Omacron -30
+KPX Aring Oslash -30
+KPX Aring Otilde -30
+KPX Aring Q -30
+KPX Aring T -120
+KPX Aring Tcaron -120
+KPX Aring Tcommaaccent -120
+KPX Aring U -50
+KPX Aring Uacute -50
+KPX Aring Ucircumflex -50
+KPX Aring Udieresis -50
+KPX Aring Ugrave -50
+KPX Aring Uhungarumlaut -50
+KPX Aring Umacron -50
+KPX Aring Uogonek -50
+KPX Aring Uring -50
+KPX Aring V -70
+KPX Aring W -50
+KPX Aring Y -100
+KPX Aring Yacute -100
+KPX Aring Ydieresis -100
+KPX Aring u -30
+KPX Aring uacute -30
+KPX Aring ucircumflex -30
+KPX Aring udieresis -30
+KPX Aring ugrave -30
+KPX Aring uhungarumlaut -30
+KPX Aring umacron -30
+KPX Aring uogonek -30
+KPX Aring uring -30
+KPX Aring v -40
+KPX Aring w -40
+KPX Aring y -40
+KPX Aring yacute -40
+KPX Aring ydieresis -40
+KPX Atilde C -30
+KPX Atilde Cacute -30
+KPX Atilde Ccaron -30
+KPX Atilde Ccedilla -30
+KPX Atilde G -30
+KPX Atilde Gbreve -30
+KPX Atilde Gcommaaccent -30
+KPX Atilde O -30
+KPX Atilde Oacute -30
+KPX Atilde Ocircumflex -30
+KPX Atilde Odieresis -30
+KPX Atilde Ograve -30
+KPX Atilde Ohungarumlaut -30
+KPX Atilde Omacron -30
+KPX Atilde Oslash -30
+KPX Atilde Otilde -30
+KPX Atilde Q -30
+KPX Atilde T -120
+KPX Atilde Tcaron -120
+KPX Atilde Tcommaaccent -120
+KPX Atilde U -50
+KPX Atilde Uacute -50
+KPX Atilde Ucircumflex -50
+KPX Atilde Udieresis -50
+KPX Atilde Ugrave -50
+KPX Atilde Uhungarumlaut -50
+KPX Atilde Umacron -50
+KPX Atilde Uogonek -50
+KPX Atilde Uring -50
+KPX Atilde V -70
+KPX Atilde W -50
+KPX Atilde Y -100
+KPX Atilde Yacute -100
+KPX Atilde Ydieresis -100
+KPX Atilde u -30
+KPX Atilde uacute -30
+KPX Atilde ucircumflex -30
+KPX Atilde udieresis -30
+KPX Atilde ugrave -30
+KPX Atilde uhungarumlaut -30
+KPX Atilde umacron -30
+KPX Atilde uogonek -30
+KPX Atilde uring -30
+KPX Atilde v -40
+KPX Atilde w -40
+KPX Atilde y -40
+KPX Atilde yacute -40
+KPX Atilde ydieresis -40
+KPX B U -10
+KPX B Uacute -10
+KPX B Ucircumflex -10
+KPX B Udieresis -10
+KPX B Ugrave -10
+KPX B Uhungarumlaut -10
+KPX B Umacron -10
+KPX B Uogonek -10
+KPX B Uring -10
+KPX B comma -20
+KPX B period -20
+KPX C comma -30
+KPX C period -30
+KPX Cacute comma -30
+KPX Cacute period -30
+KPX Ccaron comma -30
+KPX Ccaron period -30
+KPX Ccedilla comma -30
+KPX Ccedilla period -30
+KPX D A -40
+KPX D Aacute -40
+KPX D Abreve -40
+KPX D Acircumflex -40
+KPX D Adieresis -40
+KPX D Agrave -40
+KPX D Amacron -40
+KPX D Aogonek -40
+KPX D Aring -40
+KPX D Atilde -40
+KPX D V -70
+KPX D W -40
+KPX D Y -90
+KPX D Yacute -90
+KPX D Ydieresis -90
+KPX D comma -70
+KPX D period -70
+KPX Dcaron A -40
+KPX Dcaron Aacute -40
+KPX Dcaron Abreve -40
+KPX Dcaron Acircumflex -40
+KPX Dcaron Adieresis -40
+KPX Dcaron Agrave -40
+KPX Dcaron Amacron -40
+KPX Dcaron Aogonek -40
+KPX Dcaron Aring -40
+KPX Dcaron Atilde -40
+KPX Dcaron V -70
+KPX Dcaron W -40
+KPX Dcaron Y -90
+KPX Dcaron Yacute -90
+KPX Dcaron Ydieresis -90
+KPX Dcaron comma -70
+KPX Dcaron period -70
+KPX Dcroat A -40
+KPX Dcroat Aacute -40
+KPX Dcroat Abreve -40
+KPX Dcroat Acircumflex -40
+KPX Dcroat Adieresis -40
+KPX Dcroat Agrave -40
+KPX Dcroat Amacron -40
+KPX Dcroat Aogonek -40
+KPX Dcroat Aring -40
+KPX Dcroat Atilde -40
+KPX Dcroat V -70
+KPX Dcroat W -40
+KPX Dcroat Y -90
+KPX Dcroat Yacute -90
+KPX Dcroat Ydieresis -90
+KPX Dcroat comma -70
+KPX Dcroat period -70
+KPX F A -80
+KPX F Aacute -80
+KPX F Abreve -80
+KPX F Acircumflex -80
+KPX F Adieresis -80
+KPX F Agrave -80
+KPX F Amacron -80
+KPX F Aogonek -80
+KPX F Aring -80
+KPX F Atilde -80
+KPX F a -50
+KPX F aacute -50
+KPX F abreve -50
+KPX F acircumflex -50
+KPX F adieresis -50
+KPX F agrave -50
+KPX F amacron -50
+KPX F aogonek -50
+KPX F aring -50
+KPX F atilde -50
+KPX F comma -150
+KPX F e -30
+KPX F eacute -30
+KPX F ecaron -30
+KPX F ecircumflex -30
+KPX F edieresis -30
+KPX F edotaccent -30
+KPX F egrave -30
+KPX F emacron -30
+KPX F eogonek -30
+KPX F o -30
+KPX F oacute -30
+KPX F ocircumflex -30
+KPX F odieresis -30
+KPX F ograve -30
+KPX F ohungarumlaut -30
+KPX F omacron -30
+KPX F oslash -30
+KPX F otilde -30
+KPX F period -150
+KPX F r -45
+KPX F racute -45
+KPX F rcaron -45
+KPX F rcommaaccent -45
+KPX J A -20
+KPX J Aacute -20
+KPX J Abreve -20
+KPX J Acircumflex -20
+KPX J Adieresis -20
+KPX J Agrave -20
+KPX J Amacron -20
+KPX J Aogonek -20
+KPX J Aring -20
+KPX J Atilde -20
+KPX J a -20
+KPX J aacute -20
+KPX J abreve -20
+KPX J acircumflex -20
+KPX J adieresis -20
+KPX J agrave -20
+KPX J amacron -20
+KPX J aogonek -20
+KPX J aring -20
+KPX J atilde -20
+KPX J comma -30
+KPX J period -30
+KPX J u -20
+KPX J uacute -20
+KPX J ucircumflex -20
+KPX J udieresis -20
+KPX J ugrave -20
+KPX J uhungarumlaut -20
+KPX J umacron -20
+KPX J uogonek -20
+KPX J uring -20
+KPX K O -50
+KPX K Oacute -50
+KPX K Ocircumflex -50
+KPX K Odieresis -50
+KPX K Ograve -50
+KPX K Ohungarumlaut -50
+KPX K Omacron -50
+KPX K Oslash -50
+KPX K Otilde -50
+KPX K e -40
+KPX K eacute -40
+KPX K ecaron -40
+KPX K ecircumflex -40
+KPX K edieresis -40
+KPX K edotaccent -40
+KPX K egrave -40
+KPX K emacron -40
+KPX K eogonek -40
+KPX K o -40
+KPX K oacute -40
+KPX K ocircumflex -40
+KPX K odieresis -40
+KPX K ograve -40
+KPX K ohungarumlaut -40
+KPX K omacron -40
+KPX K oslash -40
+KPX K otilde -40
+KPX K u -30
+KPX K uacute -30
+KPX K ucircumflex -30
+KPX K udieresis -30
+KPX K ugrave -30
+KPX K uhungarumlaut -30
+KPX K umacron -30
+KPX K uogonek -30
+KPX K uring -30
+KPX K y -50
+KPX K yacute -50
+KPX K ydieresis -50
+KPX Kcommaaccent O -50
+KPX Kcommaaccent Oacute -50
+KPX Kcommaaccent Ocircumflex -50
+KPX Kcommaaccent Odieresis -50
+KPX Kcommaaccent Ograve -50
+KPX Kcommaaccent Ohungarumlaut -50
+KPX Kcommaaccent Omacron -50
+KPX Kcommaaccent Oslash -50
+KPX Kcommaaccent Otilde -50
+KPX Kcommaaccent e -40
+KPX Kcommaaccent eacute -40
+KPX Kcommaaccent ecaron -40
+KPX Kcommaaccent ecircumflex -40
+KPX Kcommaaccent edieresis -40
+KPX Kcommaaccent edotaccent -40
+KPX Kcommaaccent egrave -40
+KPX Kcommaaccent emacron -40
+KPX Kcommaaccent eogonek -40
+KPX Kcommaaccent o -40
+KPX Kcommaaccent oacute -40
+KPX Kcommaaccent ocircumflex -40
+KPX Kcommaaccent odieresis -40
+KPX Kcommaaccent ograve -40
+KPX Kcommaaccent ohungarumlaut -40
+KPX Kcommaaccent omacron -40
+KPX Kcommaaccent oslash -40
+KPX Kcommaaccent otilde -40
+KPX Kcommaaccent u -30
+KPX Kcommaaccent uacute -30
+KPX Kcommaaccent ucircumflex -30
+KPX Kcommaaccent udieresis -30
+KPX Kcommaaccent ugrave -30
+KPX Kcommaaccent uhungarumlaut -30
+KPX Kcommaaccent umacron -30
+KPX Kcommaaccent uogonek -30
+KPX Kcommaaccent uring -30
+KPX Kcommaaccent y -50
+KPX Kcommaaccent yacute -50
+KPX Kcommaaccent ydieresis -50
+KPX L T -110
+KPX L Tcaron -110
+KPX L Tcommaaccent -110
+KPX L V -110
+KPX L W -70
+KPX L Y -140
+KPX L Yacute -140
+KPX L Ydieresis -140
+KPX L quotedblright -140
+KPX L quoteright -160
+KPX L y -30
+KPX L yacute -30
+KPX L ydieresis -30
+KPX Lacute T -110
+KPX Lacute Tcaron -110
+KPX Lacute Tcommaaccent -110
+KPX Lacute V -110
+KPX Lacute W -70
+KPX Lacute Y -140
+KPX Lacute Yacute -140
+KPX Lacute Ydieresis -140
+KPX Lacute quotedblright -140
+KPX Lacute quoteright -160
+KPX Lacute y -30
+KPX Lacute yacute -30
+KPX Lacute ydieresis -30
+KPX Lcaron T -110
+KPX Lcaron Tcaron -110
+KPX Lcaron Tcommaaccent -110
+KPX Lcaron V -110
+KPX Lcaron W -70
+KPX Lcaron Y -140
+KPX Lcaron Yacute -140
+KPX Lcaron Ydieresis -140
+KPX Lcaron quotedblright -140
+KPX Lcaron quoteright -160
+KPX Lcaron y -30
+KPX Lcaron yacute -30
+KPX Lcaron ydieresis -30
+KPX Lcommaaccent T -110
+KPX Lcommaaccent Tcaron -110
+KPX Lcommaaccent Tcommaaccent -110
+KPX Lcommaaccent V -110
+KPX Lcommaaccent W -70
+KPX Lcommaaccent Y -140
+KPX Lcommaaccent Yacute -140
+KPX Lcommaaccent Ydieresis -140
+KPX Lcommaaccent quotedblright -140
+KPX Lcommaaccent quoteright -160
+KPX Lcommaaccent y -30
+KPX Lcommaaccent yacute -30
+KPX Lcommaaccent ydieresis -30
+KPX Lslash T -110
+KPX Lslash Tcaron -110
+KPX Lslash Tcommaaccent -110
+KPX Lslash V -110
+KPX Lslash W -70
+KPX Lslash Y -140
+KPX Lslash Yacute -140
+KPX Lslash Ydieresis -140
+KPX Lslash quotedblright -140
+KPX Lslash quoteright -160
+KPX Lslash y -30
+KPX Lslash yacute -30
+KPX Lslash ydieresis -30
+KPX O A -20
+KPX O Aacute -20
+KPX O Abreve -20
+KPX O Acircumflex -20
+KPX O Adieresis -20
+KPX O Agrave -20
+KPX O Amacron -20
+KPX O Aogonek -20
+KPX O Aring -20
+KPX O Atilde -20
+KPX O T -40
+KPX O Tcaron -40
+KPX O Tcommaaccent -40
+KPX O V -50
+KPX O W -30
+KPX O X -60
+KPX O Y -70
+KPX O Yacute -70
+KPX O Ydieresis -70
+KPX O comma -40
+KPX O period -40
+KPX Oacute A -20
+KPX Oacute Aacute -20
+KPX Oacute Abreve -20
+KPX Oacute Acircumflex -20
+KPX Oacute Adieresis -20
+KPX Oacute Agrave -20
+KPX Oacute Amacron -20
+KPX Oacute Aogonek -20
+KPX Oacute Aring -20
+KPX Oacute Atilde -20
+KPX Oacute T -40
+KPX Oacute Tcaron -40
+KPX Oacute Tcommaaccent -40
+KPX Oacute V -50
+KPX Oacute W -30
+KPX Oacute X -60
+KPX Oacute Y -70
+KPX Oacute Yacute -70
+KPX Oacute Ydieresis -70
+KPX Oacute comma -40
+KPX Oacute period -40
+KPX Ocircumflex A -20
+KPX Ocircumflex Aacute -20
+KPX Ocircumflex Abreve -20
+KPX Ocircumflex Acircumflex -20
+KPX Ocircumflex Adieresis -20
+KPX Ocircumflex Agrave -20
+KPX Ocircumflex Amacron -20
+KPX Ocircumflex Aogonek -20
+KPX Ocircumflex Aring -20
+KPX Ocircumflex Atilde -20
+KPX Ocircumflex T -40
+KPX Ocircumflex Tcaron -40
+KPX Ocircumflex Tcommaaccent -40
+KPX Ocircumflex V -50
+KPX Ocircumflex W -30
+KPX Ocircumflex X -60
+KPX Ocircumflex Y -70
+KPX Ocircumflex Yacute -70
+KPX Ocircumflex Ydieresis -70
+KPX Ocircumflex comma -40
+KPX Ocircumflex period -40
+KPX Odieresis A -20
+KPX Odieresis Aacute -20
+KPX Odieresis Abreve -20
+KPX Odieresis Acircumflex -20
+KPX Odieresis Adieresis -20
+KPX Odieresis Agrave -20
+KPX Odieresis Amacron -20
+KPX Odieresis Aogonek -20
+KPX Odieresis Aring -20
+KPX Odieresis Atilde -20
+KPX Odieresis T -40
+KPX Odieresis Tcaron -40
+KPX Odieresis Tcommaaccent -40
+KPX Odieresis V -50
+KPX Odieresis W -30
+KPX Odieresis X -60
+KPX Odieresis Y -70
+KPX Odieresis Yacute -70
+KPX Odieresis Ydieresis -70
+KPX Odieresis comma -40
+KPX Odieresis period -40
+KPX Ograve A -20
+KPX Ograve Aacute -20
+KPX Ograve Abreve -20
+KPX Ograve Acircumflex -20
+KPX Ograve Adieresis -20
+KPX Ograve Agrave -20
+KPX Ograve Amacron -20
+KPX Ograve Aogonek -20
+KPX Ograve Aring -20
+KPX Ograve Atilde -20
+KPX Ograve T -40
+KPX Ograve Tcaron -40
+KPX Ograve Tcommaaccent -40
+KPX Ograve V -50
+KPX Ograve W -30
+KPX Ograve X -60
+KPX Ograve Y -70
+KPX Ograve Yacute -70
+KPX Ograve Ydieresis -70
+KPX Ograve comma -40
+KPX Ograve period -40
+KPX Ohungarumlaut A -20
+KPX Ohungarumlaut Aacute -20
+KPX Ohungarumlaut Abreve -20
+KPX Ohungarumlaut Acircumflex -20
+KPX Ohungarumlaut Adieresis -20
+KPX Ohungarumlaut Agrave -20
+KPX Ohungarumlaut Amacron -20
+KPX Ohungarumlaut Aogonek -20
+KPX Ohungarumlaut Aring -20
+KPX Ohungarumlaut Atilde -20
+KPX Ohungarumlaut T -40
+KPX Ohungarumlaut Tcaron -40
+KPX Ohungarumlaut Tcommaaccent -40
+KPX Ohungarumlaut V -50
+KPX Ohungarumlaut W -30
+KPX Ohungarumlaut X -60
+KPX Ohungarumlaut Y -70
+KPX Ohungarumlaut Yacute -70
+KPX Ohungarumlaut Ydieresis -70
+KPX Ohungarumlaut comma -40
+KPX Ohungarumlaut period -40
+KPX Omacron A -20
+KPX Omacron Aacute -20
+KPX Omacron Abreve -20
+KPX Omacron Acircumflex -20
+KPX Omacron Adieresis -20
+KPX Omacron Agrave -20
+KPX Omacron Amacron -20
+KPX Omacron Aogonek -20
+KPX Omacron Aring -20
+KPX Omacron Atilde -20
+KPX Omacron T -40
+KPX Omacron Tcaron -40
+KPX Omacron Tcommaaccent -40
+KPX Omacron V -50
+KPX Omacron W -30
+KPX Omacron X -60
+KPX Omacron Y -70
+KPX Omacron Yacute -70
+KPX Omacron Ydieresis -70
+KPX Omacron comma -40
+KPX Omacron period -40
+KPX Oslash A -20
+KPX Oslash Aacute -20
+KPX Oslash Abreve -20
+KPX Oslash Acircumflex -20
+KPX Oslash Adieresis -20
+KPX Oslash Agrave -20
+KPX Oslash Amacron -20
+KPX Oslash Aogonek -20
+KPX Oslash Aring -20
+KPX Oslash Atilde -20
+KPX Oslash T -40
+KPX Oslash Tcaron -40
+KPX Oslash Tcommaaccent -40
+KPX Oslash V -50
+KPX Oslash W -30
+KPX Oslash X -60
+KPX Oslash Y -70
+KPX Oslash Yacute -70
+KPX Oslash Ydieresis -70
+KPX Oslash comma -40
+KPX Oslash period -40
+KPX Otilde A -20
+KPX Otilde Aacute -20
+KPX Otilde Abreve -20
+KPX Otilde Acircumflex -20
+KPX Otilde Adieresis -20
+KPX Otilde Agrave -20
+KPX Otilde Amacron -20
+KPX Otilde Aogonek -20
+KPX Otilde Aring -20
+KPX Otilde Atilde -20
+KPX Otilde T -40
+KPX Otilde Tcaron -40
+KPX Otilde Tcommaaccent -40
+KPX Otilde V -50
+KPX Otilde W -30
+KPX Otilde X -60
+KPX Otilde Y -70
+KPX Otilde Yacute -70
+KPX Otilde Ydieresis -70
+KPX Otilde comma -40
+KPX Otilde period -40
+KPX P A -120
+KPX P Aacute -120
+KPX P Abreve -120
+KPX P Acircumflex -120
+KPX P Adieresis -120
+KPX P Agrave -120
+KPX P Amacron -120
+KPX P Aogonek -120
+KPX P Aring -120
+KPX P Atilde -120
+KPX P a -40
+KPX P aacute -40
+KPX P abreve -40
+KPX P acircumflex -40
+KPX P adieresis -40
+KPX P agrave -40
+KPX P amacron -40
+KPX P aogonek -40
+KPX P aring -40
+KPX P atilde -40
+KPX P comma -180
+KPX P e -50
+KPX P eacute -50
+KPX P ecaron -50
+KPX P ecircumflex -50
+KPX P edieresis -50
+KPX P edotaccent -50
+KPX P egrave -50
+KPX P emacron -50
+KPX P eogonek -50
+KPX P o -50
+KPX P oacute -50
+KPX P ocircumflex -50
+KPX P odieresis -50
+KPX P ograve -50
+KPX P ohungarumlaut -50
+KPX P omacron -50
+KPX P oslash -50
+KPX P otilde -50
+KPX P period -180
+KPX Q U -10
+KPX Q Uacute -10
+KPX Q Ucircumflex -10
+KPX Q Udieresis -10
+KPX Q Ugrave -10
+KPX Q Uhungarumlaut -10
+KPX Q Umacron -10
+KPX Q Uogonek -10
+KPX Q Uring -10
+KPX R O -20
+KPX R Oacute -20
+KPX R Ocircumflex -20
+KPX R Odieresis -20
+KPX R Ograve -20
+KPX R Ohungarumlaut -20
+KPX R Omacron -20
+KPX R Oslash -20
+KPX R Otilde -20
+KPX R T -30
+KPX R Tcaron -30
+KPX R Tcommaaccent -30
+KPX R U -40
+KPX R Uacute -40
+KPX R Ucircumflex -40
+KPX R Udieresis -40
+KPX R Ugrave -40
+KPX R Uhungarumlaut -40
+KPX R Umacron -40
+KPX R Uogonek -40
+KPX R Uring -40
+KPX R V -50
+KPX R W -30
+KPX R Y -50
+KPX R Yacute -50
+KPX R Ydieresis -50
+KPX Racute O -20
+KPX Racute Oacute -20
+KPX Racute Ocircumflex -20
+KPX Racute Odieresis -20
+KPX Racute Ograve -20
+KPX Racute Ohungarumlaut -20
+KPX Racute Omacron -20
+KPX Racute Oslash -20
+KPX Racute Otilde -20
+KPX Racute T -30
+KPX Racute Tcaron -30
+KPX Racute Tcommaaccent -30
+KPX Racute U -40
+KPX Racute Uacute -40
+KPX Racute Ucircumflex -40
+KPX Racute Udieresis -40
+KPX Racute Ugrave -40
+KPX Racute Uhungarumlaut -40
+KPX Racute Umacron -40
+KPX Racute Uogonek -40
+KPX Racute Uring -40
+KPX Racute V -50
+KPX Racute W -30
+KPX Racute Y -50
+KPX Racute Yacute -50
+KPX Racute Ydieresis -50
+KPX Rcaron O -20
+KPX Rcaron Oacute -20
+KPX Rcaron Ocircumflex -20
+KPX Rcaron Odieresis -20
+KPX Rcaron Ograve -20
+KPX Rcaron Ohungarumlaut -20
+KPX Rcaron Omacron -20
+KPX Rcaron Oslash -20
+KPX Rcaron Otilde -20
+KPX Rcaron T -30
+KPX Rcaron Tcaron -30
+KPX Rcaron Tcommaaccent -30
+KPX Rcaron U -40
+KPX Rcaron Uacute -40
+KPX Rcaron Ucircumflex -40
+KPX Rcaron Udieresis -40
+KPX Rcaron Ugrave -40
+KPX Rcaron Uhungarumlaut -40
+KPX Rcaron Umacron -40
+KPX Rcaron Uogonek -40
+KPX Rcaron Uring -40
+KPX Rcaron V -50
+KPX Rcaron W -30
+KPX Rcaron Y -50
+KPX Rcaron Yacute -50
+KPX Rcaron Ydieresis -50
+KPX Rcommaaccent O -20
+KPX Rcommaaccent Oacute -20
+KPX Rcommaaccent Ocircumflex -20
+KPX Rcommaaccent Odieresis -20
+KPX Rcommaaccent Ograve -20
+KPX Rcommaaccent Ohungarumlaut -20
+KPX Rcommaaccent Omacron -20
+KPX Rcommaaccent Oslash -20
+KPX Rcommaaccent Otilde -20
+KPX Rcommaaccent T -30
+KPX Rcommaaccent Tcaron -30
+KPX Rcommaaccent Tcommaaccent -30
+KPX Rcommaaccent U -40
+KPX Rcommaaccent Uacute -40
+KPX Rcommaaccent Ucircumflex -40
+KPX Rcommaaccent Udieresis -40
+KPX Rcommaaccent Ugrave -40
+KPX Rcommaaccent Uhungarumlaut -40
+KPX Rcommaaccent Umacron -40
+KPX Rcommaaccent Uogonek -40
+KPX Rcommaaccent Uring -40
+KPX Rcommaaccent V -50
+KPX Rcommaaccent W -30
+KPX Rcommaaccent Y -50
+KPX Rcommaaccent Yacute -50
+KPX Rcommaaccent Ydieresis -50
+KPX S comma -20
+KPX S period -20
+KPX Sacute comma -20
+KPX Sacute period -20
+KPX Scaron comma -20
+KPX Scaron period -20
+KPX Scedilla comma -20
+KPX Scedilla period -20
+KPX Scommaaccent comma -20
+KPX Scommaaccent period -20
+KPX T A -120
+KPX T Aacute -120
+KPX T Abreve -120
+KPX T Acircumflex -120
+KPX T Adieresis -120
+KPX T Agrave -120
+KPX T Amacron -120
+KPX T Aogonek -120
+KPX T Aring -120
+KPX T Atilde -120
+KPX T O -40
+KPX T Oacute -40
+KPX T Ocircumflex -40
+KPX T Odieresis -40
+KPX T Ograve -40
+KPX T Ohungarumlaut -40
+KPX T Omacron -40
+KPX T Oslash -40
+KPX T Otilde -40
+KPX T a -120
+KPX T aacute -120
+KPX T abreve -60
+KPX T acircumflex -120
+KPX T adieresis -120
+KPX T agrave -120
+KPX T amacron -60
+KPX T aogonek -120
+KPX T aring -120
+KPX T atilde -60
+KPX T colon -20
+KPX T comma -120
+KPX T e -120
+KPX T eacute -120
+KPX T ecaron -120
+KPX T ecircumflex -120
+KPX T edieresis -120
+KPX T edotaccent -120
+KPX T egrave -60
+KPX T emacron -60
+KPX T eogonek -120
+KPX T hyphen -140
+KPX T o -120
+KPX T oacute -120
+KPX T ocircumflex -120
+KPX T odieresis -120
+KPX T ograve -120
+KPX T ohungarumlaut -120
+KPX T omacron -60
+KPX T oslash -120
+KPX T otilde -60
+KPX T period -120
+KPX T r -120
+KPX T racute -120
+KPX T rcaron -120
+KPX T rcommaaccent -120
+KPX T semicolon -20
+KPX T u -120
+KPX T uacute -120
+KPX T ucircumflex -120
+KPX T udieresis -120
+KPX T ugrave -120
+KPX T uhungarumlaut -120
+KPX T umacron -60
+KPX T uogonek -120
+KPX T uring -120
+KPX T w -120
+KPX T y -120
+KPX T yacute -120
+KPX T ydieresis -60
+KPX Tcaron A -120
+KPX Tcaron Aacute -120
+KPX Tcaron Abreve -120
+KPX Tcaron Acircumflex -120
+KPX Tcaron Adieresis -120
+KPX Tcaron Agrave -120
+KPX Tcaron Amacron -120
+KPX Tcaron Aogonek -120
+KPX Tcaron Aring -120
+KPX Tcaron Atilde -120
+KPX Tcaron O -40
+KPX Tcaron Oacute -40
+KPX Tcaron Ocircumflex -40
+KPX Tcaron Odieresis -40
+KPX Tcaron Ograve -40
+KPX Tcaron Ohungarumlaut -40
+KPX Tcaron Omacron -40
+KPX Tcaron Oslash -40
+KPX Tcaron Otilde -40
+KPX Tcaron a -120
+KPX Tcaron aacute -120
+KPX Tcaron abreve -60
+KPX Tcaron acircumflex -120
+KPX Tcaron adieresis -120
+KPX Tcaron agrave -120
+KPX Tcaron amacron -60
+KPX Tcaron aogonek -120
+KPX Tcaron aring -120
+KPX Tcaron atilde -60
+KPX Tcaron colon -20
+KPX Tcaron comma -120
+KPX Tcaron e -120
+KPX Tcaron eacute -120
+KPX Tcaron ecaron -120
+KPX Tcaron ecircumflex -120
+KPX Tcaron edieresis -120
+KPX Tcaron edotaccent -120
+KPX Tcaron egrave -60
+KPX Tcaron emacron -60
+KPX Tcaron eogonek -120
+KPX Tcaron hyphen -140
+KPX Tcaron o -120
+KPX Tcaron oacute -120
+KPX Tcaron ocircumflex -120
+KPX Tcaron odieresis -120
+KPX Tcaron ograve -120
+KPX Tcaron ohungarumlaut -120
+KPX Tcaron omacron -60
+KPX Tcaron oslash -120
+KPX Tcaron otilde -60
+KPX Tcaron period -120
+KPX Tcaron r -120
+KPX Tcaron racute -120
+KPX Tcaron rcaron -120
+KPX Tcaron rcommaaccent -120
+KPX Tcaron semicolon -20
+KPX Tcaron u -120
+KPX Tcaron uacute -120
+KPX Tcaron ucircumflex -120
+KPX Tcaron udieresis -120
+KPX Tcaron ugrave -120
+KPX Tcaron uhungarumlaut -120
+KPX Tcaron umacron -60
+KPX Tcaron uogonek -120
+KPX Tcaron uring -120
+KPX Tcaron w -120
+KPX Tcaron y -120
+KPX Tcaron yacute -120
+KPX Tcaron ydieresis -60
+KPX Tcommaaccent A -120
+KPX Tcommaaccent Aacute -120
+KPX Tcommaaccent Abreve -120
+KPX Tcommaaccent Acircumflex -120
+KPX Tcommaaccent Adieresis -120
+KPX Tcommaaccent Agrave -120
+KPX Tcommaaccent Amacron -120
+KPX Tcommaaccent Aogonek -120
+KPX Tcommaaccent Aring -120
+KPX Tcommaaccent Atilde -120
+KPX Tcommaaccent O -40
+KPX Tcommaaccent Oacute -40
+KPX Tcommaaccent Ocircumflex -40
+KPX Tcommaaccent Odieresis -40
+KPX Tcommaaccent Ograve -40
+KPX Tcommaaccent Ohungarumlaut -40
+KPX Tcommaaccent Omacron -40
+KPX Tcommaaccent Oslash -40
+KPX Tcommaaccent Otilde -40
+KPX Tcommaaccent a -120
+KPX Tcommaaccent aacute -120
+KPX Tcommaaccent abreve -60
+KPX Tcommaaccent acircumflex -120
+KPX Tcommaaccent adieresis -120
+KPX Tcommaaccent agrave -120
+KPX Tcommaaccent amacron -60
+KPX Tcommaaccent aogonek -120
+KPX Tcommaaccent aring -120
+KPX Tcommaaccent atilde -60
+KPX Tcommaaccent colon -20
+KPX Tcommaaccent comma -120
+KPX Tcommaaccent e -120
+KPX Tcommaaccent eacute -120
+KPX Tcommaaccent ecaron -120
+KPX Tcommaaccent ecircumflex -120
+KPX Tcommaaccent edieresis -120
+KPX Tcommaaccent edotaccent -120
+KPX Tcommaaccent egrave -60
+KPX Tcommaaccent emacron -60
+KPX Tcommaaccent eogonek -120
+KPX Tcommaaccent hyphen -140
+KPX Tcommaaccent o -120
+KPX Tcommaaccent oacute -120
+KPX Tcommaaccent ocircumflex -120
+KPX Tcommaaccent odieresis -120
+KPX Tcommaaccent ograve -120
+KPX Tcommaaccent ohungarumlaut -120
+KPX Tcommaaccent omacron -60
+KPX Tcommaaccent oslash -120
+KPX Tcommaaccent otilde -60
+KPX Tcommaaccent period -120
+KPX Tcommaaccent r -120
+KPX Tcommaaccent racute -120
+KPX Tcommaaccent rcaron -120
+KPX Tcommaaccent rcommaaccent -120
+KPX Tcommaaccent semicolon -20
+KPX Tcommaaccent u -120
+KPX Tcommaaccent uacute -120
+KPX Tcommaaccent ucircumflex -120
+KPX Tcommaaccent udieresis -120
+KPX Tcommaaccent ugrave -120
+KPX Tcommaaccent uhungarumlaut -120
+KPX Tcommaaccent umacron -60
+KPX Tcommaaccent uogonek -120
+KPX Tcommaaccent uring -120
+KPX Tcommaaccent w -120
+KPX Tcommaaccent y -120
+KPX Tcommaaccent yacute -120
+KPX Tcommaaccent ydieresis -60
+KPX U A -40
+KPX U Aacute -40
+KPX U Abreve -40
+KPX U Acircumflex -40
+KPX U Adieresis -40
+KPX U Agrave -40
+KPX U Amacron -40
+KPX U Aogonek -40
+KPX U Aring -40
+KPX U Atilde -40
+KPX U comma -40
+KPX U period -40
+KPX Uacute A -40
+KPX Uacute Aacute -40
+KPX Uacute Abreve -40
+KPX Uacute Acircumflex -40
+KPX Uacute Adieresis -40
+KPX Uacute Agrave -40
+KPX Uacute Amacron -40
+KPX Uacute Aogonek -40
+KPX Uacute Aring -40
+KPX Uacute Atilde -40
+KPX Uacute comma -40
+KPX Uacute period -40
+KPX Ucircumflex A -40
+KPX Ucircumflex Aacute -40
+KPX Ucircumflex Abreve -40
+KPX Ucircumflex Acircumflex -40
+KPX Ucircumflex Adieresis -40
+KPX Ucircumflex Agrave -40
+KPX Ucircumflex Amacron -40
+KPX Ucircumflex Aogonek -40
+KPX Ucircumflex Aring -40
+KPX Ucircumflex Atilde -40
+KPX Ucircumflex comma -40
+KPX Ucircumflex period -40
+KPX Udieresis A -40
+KPX Udieresis Aacute -40
+KPX Udieresis Abreve -40
+KPX Udieresis Acircumflex -40
+KPX Udieresis Adieresis -40
+KPX Udieresis Agrave -40
+KPX Udieresis Amacron -40
+KPX Udieresis Aogonek -40
+KPX Udieresis Aring -40
+KPX Udieresis Atilde -40
+KPX Udieresis comma -40
+KPX Udieresis period -40
+KPX Ugrave A -40
+KPX Ugrave Aacute -40
+KPX Ugrave Abreve -40
+KPX Ugrave Acircumflex -40
+KPX Ugrave Adieresis -40
+KPX Ugrave Agrave -40
+KPX Ugrave Amacron -40
+KPX Ugrave Aogonek -40
+KPX Ugrave Aring -40
+KPX Ugrave Atilde -40
+KPX Ugrave comma -40
+KPX Ugrave period -40
+KPX Uhungarumlaut A -40
+KPX Uhungarumlaut Aacute -40
+KPX Uhungarumlaut Abreve -40
+KPX Uhungarumlaut Acircumflex -40
+KPX Uhungarumlaut Adieresis -40
+KPX Uhungarumlaut Agrave -40
+KPX Uhungarumlaut Amacron -40
+KPX Uhungarumlaut Aogonek -40
+KPX Uhungarumlaut Aring -40
+KPX Uhungarumlaut Atilde -40
+KPX Uhungarumlaut comma -40
+KPX Uhungarumlaut period -40
+KPX Umacron A -40
+KPX Umacron Aacute -40
+KPX Umacron Abreve -40
+KPX Umacron Acircumflex -40
+KPX Umacron Adieresis -40
+KPX Umacron Agrave -40
+KPX Umacron Amacron -40
+KPX Umacron Aogonek -40
+KPX Umacron Aring -40
+KPX Umacron Atilde -40
+KPX Umacron comma -40
+KPX Umacron period -40
+KPX Uogonek A -40
+KPX Uogonek Aacute -40
+KPX Uogonek Abreve -40
+KPX Uogonek Acircumflex -40
+KPX Uogonek Adieresis -40
+KPX Uogonek Agrave -40
+KPX Uogonek Amacron -40
+KPX Uogonek Aogonek -40
+KPX Uogonek Aring -40
+KPX Uogonek Atilde -40
+KPX Uogonek comma -40
+KPX Uogonek period -40
+KPX Uring A -40
+KPX Uring Aacute -40
+KPX Uring Abreve -40
+KPX Uring Acircumflex -40
+KPX Uring Adieresis -40
+KPX Uring Agrave -40
+KPX Uring Amacron -40
+KPX Uring Aogonek -40
+KPX Uring Aring -40
+KPX Uring Atilde -40
+KPX Uring comma -40
+KPX Uring period -40
+KPX V A -80
+KPX V Aacute -80
+KPX V Abreve -80
+KPX V Acircumflex -80
+KPX V Adieresis -80
+KPX V Agrave -80
+KPX V Amacron -80
+KPX V Aogonek -80
+KPX V Aring -80
+KPX V Atilde -80
+KPX V G -40
+KPX V Gbreve -40
+KPX V Gcommaaccent -40
+KPX V O -40
+KPX V Oacute -40
+KPX V Ocircumflex -40
+KPX V Odieresis -40
+KPX V Ograve -40
+KPX V Ohungarumlaut -40
+KPX V Omacron -40
+KPX V Oslash -40
+KPX V Otilde -40
+KPX V a -70
+KPX V aacute -70
+KPX V abreve -70
+KPX V acircumflex -70
+KPX V adieresis -70
+KPX V agrave -70
+KPX V amacron -70
+KPX V aogonek -70
+KPX V aring -70
+KPX V atilde -70
+KPX V colon -40
+KPX V comma -125
+KPX V e -80
+KPX V eacute -80
+KPX V ecaron -80
+KPX V ecircumflex -80
+KPX V edieresis -80
+KPX V edotaccent -80
+KPX V egrave -80
+KPX V emacron -80
+KPX V eogonek -80
+KPX V hyphen -80
+KPX V o -80
+KPX V oacute -80
+KPX V ocircumflex -80
+KPX V odieresis -80
+KPX V ograve -80
+KPX V ohungarumlaut -80
+KPX V omacron -80
+KPX V oslash -80
+KPX V otilde -80
+KPX V period -125
+KPX V semicolon -40
+KPX V u -70
+KPX V uacute -70
+KPX V ucircumflex -70
+KPX V udieresis -70
+KPX V ugrave -70
+KPX V uhungarumlaut -70
+KPX V umacron -70
+KPX V uogonek -70
+KPX V uring -70
+KPX W A -50
+KPX W Aacute -50
+KPX W Abreve -50
+KPX W Acircumflex -50
+KPX W Adieresis -50
+KPX W Agrave -50
+KPX W Amacron -50
+KPX W Aogonek -50
+KPX W Aring -50
+KPX W Atilde -50
+KPX W O -20
+KPX W Oacute -20
+KPX W Ocircumflex -20
+KPX W Odieresis -20
+KPX W Ograve -20
+KPX W Ohungarumlaut -20
+KPX W Omacron -20
+KPX W Oslash -20
+KPX W Otilde -20
+KPX W a -40
+KPX W aacute -40
+KPX W abreve -40
+KPX W acircumflex -40
+KPX W adieresis -40
+KPX W agrave -40
+KPX W amacron -40
+KPX W aogonek -40
+KPX W aring -40
+KPX W atilde -40
+KPX W comma -80
+KPX W e -30
+KPX W eacute -30
+KPX W ecaron -30
+KPX W ecircumflex -30
+KPX W edieresis -30
+KPX W edotaccent -30
+KPX W egrave -30
+KPX W emacron -30
+KPX W eogonek -30
+KPX W hyphen -40
+KPX W o -30
+KPX W oacute -30
+KPX W ocircumflex -30
+KPX W odieresis -30
+KPX W ograve -30
+KPX W ohungarumlaut -30
+KPX W omacron -30
+KPX W oslash -30
+KPX W otilde -30
+KPX W period -80
+KPX W u -30
+KPX W uacute -30
+KPX W ucircumflex -30
+KPX W udieresis -30
+KPX W ugrave -30
+KPX W uhungarumlaut -30
+KPX W umacron -30
+KPX W uogonek -30
+KPX W uring -30
+KPX W y -20
+KPX W yacute -20
+KPX W ydieresis -20
+KPX Y A -110
+KPX Y Aacute -110
+KPX Y Abreve -110
+KPX Y Acircumflex -110
+KPX Y Adieresis -110
+KPX Y Agrave -110
+KPX Y Amacron -110
+KPX Y Aogonek -110
+KPX Y Aring -110
+KPX Y Atilde -110
+KPX Y O -85
+KPX Y Oacute -85
+KPX Y Ocircumflex -85
+KPX Y Odieresis -85
+KPX Y Ograve -85
+KPX Y Ohungarumlaut -85
+KPX Y Omacron -85
+KPX Y Oslash -85
+KPX Y Otilde -85
+KPX Y a -140
+KPX Y aacute -140
+KPX Y abreve -70
+KPX Y acircumflex -140
+KPX Y adieresis -140
+KPX Y agrave -140
+KPX Y amacron -70
+KPX Y aogonek -140
+KPX Y aring -140
+KPX Y atilde -140
+KPX Y colon -60
+KPX Y comma -140
+KPX Y e -140
+KPX Y eacute -140
+KPX Y ecaron -140
+KPX Y ecircumflex -140
+KPX Y edieresis -140
+KPX Y edotaccent -140
+KPX Y egrave -140
+KPX Y emacron -70
+KPX Y eogonek -140
+KPX Y hyphen -140
+KPX Y i -20
+KPX Y iacute -20
+KPX Y iogonek -20
+KPX Y o -140
+KPX Y oacute -140
+KPX Y ocircumflex -140
+KPX Y odieresis -140
+KPX Y ograve -140
+KPX Y ohungarumlaut -140
+KPX Y omacron -140
+KPX Y oslash -140
+KPX Y otilde -140
+KPX Y period -140
+KPX Y semicolon -60
+KPX Y u -110
+KPX Y uacute -110
+KPX Y ucircumflex -110
+KPX Y udieresis -110
+KPX Y ugrave -110
+KPX Y uhungarumlaut -110
+KPX Y umacron -110
+KPX Y uogonek -110
+KPX Y uring -110
+KPX Yacute A -110
+KPX Yacute Aacute -110
+KPX Yacute Abreve -110
+KPX Yacute Acircumflex -110
+KPX Yacute Adieresis -110
+KPX Yacute Agrave -110
+KPX Yacute Amacron -110
+KPX Yacute Aogonek -110
+KPX Yacute Aring -110
+KPX Yacute Atilde -110
+KPX Yacute O -85
+KPX Yacute Oacute -85
+KPX Yacute Ocircumflex -85
+KPX Yacute Odieresis -85
+KPX Yacute Ograve -85
+KPX Yacute Ohungarumlaut -85
+KPX Yacute Omacron -85
+KPX Yacute Oslash -85
+KPX Yacute Otilde -85
+KPX Yacute a -140
+KPX Yacute aacute -140
+KPX Yacute abreve -70
+KPX Yacute acircumflex -140
+KPX Yacute adieresis -140
+KPX Yacute agrave -140
+KPX Yacute amacron -70
+KPX Yacute aogonek -140
+KPX Yacute aring -140
+KPX Yacute atilde -70
+KPX Yacute colon -60
+KPX Yacute comma -140
+KPX Yacute e -140
+KPX Yacute eacute -140
+KPX Yacute ecaron -140
+KPX Yacute ecircumflex -140
+KPX Yacute edieresis -140
+KPX Yacute edotaccent -140
+KPX Yacute egrave -140
+KPX Yacute emacron -70
+KPX Yacute eogonek -140
+KPX Yacute hyphen -140
+KPX Yacute i -20
+KPX Yacute iacute -20
+KPX Yacute iogonek -20
+KPX Yacute o -140
+KPX Yacute oacute -140
+KPX Yacute ocircumflex -140
+KPX Yacute odieresis -140
+KPX Yacute ograve -140
+KPX Yacute ohungarumlaut -140
+KPX Yacute omacron -70
+KPX Yacute oslash -140
+KPX Yacute otilde -140
+KPX Yacute period -140
+KPX Yacute semicolon -60
+KPX Yacute u -110
+KPX Yacute uacute -110
+KPX Yacute ucircumflex -110
+KPX Yacute udieresis -110
+KPX Yacute ugrave -110
+KPX Yacute uhungarumlaut -110
+KPX Yacute umacron -110
+KPX Yacute uogonek -110
+KPX Yacute uring -110
+KPX Ydieresis A -110
+KPX Ydieresis Aacute -110
+KPX Ydieresis Abreve -110
+KPX Ydieresis Acircumflex -110
+KPX Ydieresis Adieresis -110
+KPX Ydieresis Agrave -110
+KPX Ydieresis Amacron -110
+KPX Ydieresis Aogonek -110
+KPX Ydieresis Aring -110
+KPX Ydieresis Atilde -110
+KPX Ydieresis O -85
+KPX Ydieresis Oacute -85
+KPX Ydieresis Ocircumflex -85
+KPX Ydieresis Odieresis -85
+KPX Ydieresis Ograve -85
+KPX Ydieresis Ohungarumlaut -85
+KPX Ydieresis Omacron -85
+KPX Ydieresis Oslash -85
+KPX Ydieresis Otilde -85
+KPX Ydieresis a -140
+KPX Ydieresis aacute -140
+KPX Ydieresis abreve -70
+KPX Ydieresis acircumflex -140
+KPX Ydieresis adieresis -140
+KPX Ydieresis agrave -140
+KPX Ydieresis amacron -70
+KPX Ydieresis aogonek -140
+KPX Ydieresis aring -140
+KPX Ydieresis atilde -70
+KPX Ydieresis colon -60
+KPX Ydieresis comma -140
+KPX Ydieresis e -140
+KPX Ydieresis eacute -140
+KPX Ydieresis ecaron -140
+KPX Ydieresis ecircumflex -140
+KPX Ydieresis edieresis -140
+KPX Ydieresis edotaccent -140
+KPX Ydieresis egrave -140
+KPX Ydieresis emacron -70
+KPX Ydieresis eogonek -140
+KPX Ydieresis hyphen -140
+KPX Ydieresis i -20
+KPX Ydieresis iacute -20
+KPX Ydieresis iogonek -20
+KPX Ydieresis o -140
+KPX Ydieresis oacute -140
+KPX Ydieresis ocircumflex -140
+KPX Ydieresis odieresis -140
+KPX Ydieresis ograve -140
+KPX Ydieresis ohungarumlaut -140
+KPX Ydieresis omacron -140
+KPX Ydieresis oslash -140
+KPX Ydieresis otilde -140
+KPX Ydieresis period -140
+KPX Ydieresis semicolon -60
+KPX Ydieresis u -110
+KPX Ydieresis uacute -110
+KPX Ydieresis ucircumflex -110
+KPX Ydieresis udieresis -110
+KPX Ydieresis ugrave -110
+KPX Ydieresis uhungarumlaut -110
+KPX Ydieresis umacron -110
+KPX Ydieresis uogonek -110
+KPX Ydieresis uring -110
+KPX a v -20
+KPX a w -20
+KPX a y -30
+KPX a yacute -30
+KPX a ydieresis -30
+KPX aacute v -20
+KPX aacute w -20
+KPX aacute y -30
+KPX aacute yacute -30
+KPX aacute ydieresis -30
+KPX abreve v -20
+KPX abreve w -20
+KPX abreve y -30
+KPX abreve yacute -30
+KPX abreve ydieresis -30
+KPX acircumflex v -20
+KPX acircumflex w -20
+KPX acircumflex y -30
+KPX acircumflex yacute -30
+KPX acircumflex ydieresis -30
+KPX adieresis v -20
+KPX adieresis w -20
+KPX adieresis y -30
+KPX adieresis yacute -30
+KPX adieresis ydieresis -30
+KPX agrave v -20
+KPX agrave w -20
+KPX agrave y -30
+KPX agrave yacute -30
+KPX agrave ydieresis -30
+KPX amacron v -20
+KPX amacron w -20
+KPX amacron y -30
+KPX amacron yacute -30
+KPX amacron ydieresis -30
+KPX aogonek v -20
+KPX aogonek w -20
+KPX aogonek y -30
+KPX aogonek yacute -30
+KPX aogonek ydieresis -30
+KPX aring v -20
+KPX aring w -20
+KPX aring y -30
+KPX aring yacute -30
+KPX aring ydieresis -30
+KPX atilde v -20
+KPX atilde w -20
+KPX atilde y -30
+KPX atilde yacute -30
+KPX atilde ydieresis -30
+KPX b b -10
+KPX b comma -40
+KPX b l -20
+KPX b lacute -20
+KPX b lcommaaccent -20
+KPX b lslash -20
+KPX b period -40
+KPX b u -20
+KPX b uacute -20
+KPX b ucircumflex -20
+KPX b udieresis -20
+KPX b ugrave -20
+KPX b uhungarumlaut -20
+KPX b umacron -20
+KPX b uogonek -20
+KPX b uring -20
+KPX b v -20
+KPX b y -20
+KPX b yacute -20
+KPX b ydieresis -20
+KPX c comma -15
+KPX c k -20
+KPX c kcommaaccent -20
+KPX cacute comma -15
+KPX cacute k -20
+KPX cacute kcommaaccent -20
+KPX ccaron comma -15
+KPX ccaron k -20
+KPX ccaron kcommaaccent -20
+KPX ccedilla comma -15
+KPX ccedilla k -20
+KPX ccedilla kcommaaccent -20
+KPX colon space -50
+KPX comma quotedblright -100
+KPX comma quoteright -100
+KPX e comma -15
+KPX e period -15
+KPX e v -30
+KPX e w -20
+KPX e x -30
+KPX e y -20
+KPX e yacute -20
+KPX e ydieresis -20
+KPX eacute comma -15
+KPX eacute period -15
+KPX eacute v -30
+KPX eacute w -20
+KPX eacute x -30
+KPX eacute y -20
+KPX eacute yacute -20
+KPX eacute ydieresis -20
+KPX ecaron comma -15
+KPX ecaron period -15
+KPX ecaron v -30
+KPX ecaron w -20
+KPX ecaron x -30
+KPX ecaron y -20
+KPX ecaron yacute -20
+KPX ecaron ydieresis -20
+KPX ecircumflex comma -15
+KPX ecircumflex period -15
+KPX ecircumflex v -30
+KPX ecircumflex w -20
+KPX ecircumflex x -30
+KPX ecircumflex y -20
+KPX ecircumflex yacute -20
+KPX ecircumflex ydieresis -20
+KPX edieresis comma -15
+KPX edieresis period -15
+KPX edieresis v -30
+KPX edieresis w -20
+KPX edieresis x -30
+KPX edieresis y -20
+KPX edieresis yacute -20
+KPX edieresis ydieresis -20
+KPX edotaccent comma -15
+KPX edotaccent period -15
+KPX edotaccent v -30
+KPX edotaccent w -20
+KPX edotaccent x -30
+KPX edotaccent y -20
+KPX edotaccent yacute -20
+KPX edotaccent ydieresis -20
+KPX egrave comma -15
+KPX egrave period -15
+KPX egrave v -30
+KPX egrave w -20
+KPX egrave x -30
+KPX egrave y -20
+KPX egrave yacute -20
+KPX egrave ydieresis -20
+KPX emacron comma -15
+KPX emacron period -15
+KPX emacron v -30
+KPX emacron w -20
+KPX emacron x -30
+KPX emacron y -20
+KPX emacron yacute -20
+KPX emacron ydieresis -20
+KPX eogonek comma -15
+KPX eogonek period -15
+KPX eogonek v -30
+KPX eogonek w -20
+KPX eogonek x -30
+KPX eogonek y -20
+KPX eogonek yacute -20
+KPX eogonek ydieresis -20
+KPX f a -30
+KPX f aacute -30
+KPX f abreve -30
+KPX f acircumflex -30
+KPX f adieresis -30
+KPX f agrave -30
+KPX f amacron -30
+KPX f aogonek -30
+KPX f aring -30
+KPX f atilde -30
+KPX f comma -30
+KPX f dotlessi -28
+KPX f e -30
+KPX f eacute -30
+KPX f ecaron -30
+KPX f ecircumflex -30
+KPX f edieresis -30
+KPX f edotaccent -30
+KPX f egrave -30
+KPX f emacron -30
+KPX f eogonek -30
+KPX f o -30
+KPX f oacute -30
+KPX f ocircumflex -30
+KPX f odieresis -30
+KPX f ograve -30
+KPX f ohungarumlaut -30
+KPX f omacron -30
+KPX f oslash -30
+KPX f otilde -30
+KPX f period -30
+KPX f quotedblright 60
+KPX f quoteright 50
+KPX g r -10
+KPX g racute -10
+KPX g rcaron -10
+KPX g rcommaaccent -10
+KPX gbreve r -10
+KPX gbreve racute -10
+KPX gbreve rcaron -10
+KPX gbreve rcommaaccent -10
+KPX gcommaaccent r -10
+KPX gcommaaccent racute -10
+KPX gcommaaccent rcaron -10
+KPX gcommaaccent rcommaaccent -10
+KPX h y -30
+KPX h yacute -30
+KPX h ydieresis -30
+KPX k e -20
+KPX k eacute -20
+KPX k ecaron -20
+KPX k ecircumflex -20
+KPX k edieresis -20
+KPX k edotaccent -20
+KPX k egrave -20
+KPX k emacron -20
+KPX k eogonek -20
+KPX k o -20
+KPX k oacute -20
+KPX k ocircumflex -20
+KPX k odieresis -20
+KPX k ograve -20
+KPX k ohungarumlaut -20
+KPX k omacron -20
+KPX k oslash -20
+KPX k otilde -20
+KPX kcommaaccent e -20
+KPX kcommaaccent eacute -20
+KPX kcommaaccent ecaron -20
+KPX kcommaaccent ecircumflex -20
+KPX kcommaaccent edieresis -20
+KPX kcommaaccent edotaccent -20
+KPX kcommaaccent egrave -20
+KPX kcommaaccent emacron -20
+KPX kcommaaccent eogonek -20
+KPX kcommaaccent o -20
+KPX kcommaaccent oacute -20
+KPX kcommaaccent ocircumflex -20
+KPX kcommaaccent odieresis -20
+KPX kcommaaccent ograve -20
+KPX kcommaaccent ohungarumlaut -20
+KPX kcommaaccent omacron -20
+KPX kcommaaccent oslash -20
+KPX kcommaaccent otilde -20
+KPX m u -10
+KPX m uacute -10
+KPX m ucircumflex -10
+KPX m udieresis -10
+KPX m ugrave -10
+KPX m uhungarumlaut -10
+KPX m umacron -10
+KPX m uogonek -10
+KPX m uring -10
+KPX m y -15
+KPX m yacute -15
+KPX m ydieresis -15
+KPX n u -10
+KPX n uacute -10
+KPX n ucircumflex -10
+KPX n udieresis -10
+KPX n ugrave -10
+KPX n uhungarumlaut -10
+KPX n umacron -10
+KPX n uogonek -10
+KPX n uring -10
+KPX n v -20
+KPX n y -15
+KPX n yacute -15
+KPX n ydieresis -15
+KPX nacute u -10
+KPX nacute uacute -10
+KPX nacute ucircumflex -10
+KPX nacute udieresis -10
+KPX nacute ugrave -10
+KPX nacute uhungarumlaut -10
+KPX nacute umacron -10
+KPX nacute uogonek -10
+KPX nacute uring -10
+KPX nacute v -20
+KPX nacute y -15
+KPX nacute yacute -15
+KPX nacute ydieresis -15
+KPX ncaron u -10
+KPX ncaron uacute -10
+KPX ncaron ucircumflex -10
+KPX ncaron udieresis -10
+KPX ncaron ugrave -10
+KPX ncaron uhungarumlaut -10
+KPX ncaron umacron -10
+KPX ncaron uogonek -10
+KPX ncaron uring -10
+KPX ncaron v -20
+KPX ncaron y -15
+KPX ncaron yacute -15
+KPX ncaron ydieresis -15
+KPX ncommaaccent u -10
+KPX ncommaaccent uacute -10
+KPX ncommaaccent ucircumflex -10
+KPX ncommaaccent udieresis -10
+KPX ncommaaccent ugrave -10
+KPX ncommaaccent uhungarumlaut -10
+KPX ncommaaccent umacron -10
+KPX ncommaaccent uogonek -10
+KPX ncommaaccent uring -10
+KPX ncommaaccent v -20
+KPX ncommaaccent y -15
+KPX ncommaaccent yacute -15
+KPX ncommaaccent ydieresis -15
+KPX ntilde u -10
+KPX ntilde uacute -10
+KPX ntilde ucircumflex -10
+KPX ntilde udieresis -10
+KPX ntilde ugrave -10
+KPX ntilde uhungarumlaut -10
+KPX ntilde umacron -10
+KPX ntilde uogonek -10
+KPX ntilde uring -10
+KPX ntilde v -20
+KPX ntilde y -15
+KPX ntilde yacute -15
+KPX ntilde ydieresis -15
+KPX o comma -40
+KPX o period -40
+KPX o v -15
+KPX o w -15
+KPX o x -30
+KPX o y -30
+KPX o yacute -30
+KPX o ydieresis -30
+KPX oacute comma -40
+KPX oacute period -40
+KPX oacute v -15
+KPX oacute w -15
+KPX oacute x -30
+KPX oacute y -30
+KPX oacute yacute -30
+KPX oacute ydieresis -30
+KPX ocircumflex comma -40
+KPX ocircumflex period -40
+KPX ocircumflex v -15
+KPX ocircumflex w -15
+KPX ocircumflex x -30
+KPX ocircumflex y -30
+KPX ocircumflex yacute -30
+KPX ocircumflex ydieresis -30
+KPX odieresis comma -40
+KPX odieresis period -40
+KPX odieresis v -15
+KPX odieresis w -15
+KPX odieresis x -30
+KPX odieresis y -30
+KPX odieresis yacute -30
+KPX odieresis ydieresis -30
+KPX ograve comma -40
+KPX ograve period -40
+KPX ograve v -15
+KPX ograve w -15
+KPX ograve x -30
+KPX ograve y -30
+KPX ograve yacute -30
+KPX ograve ydieresis -30
+KPX ohungarumlaut comma -40
+KPX ohungarumlaut period -40
+KPX ohungarumlaut v -15
+KPX ohungarumlaut w -15
+KPX ohungarumlaut x -30
+KPX ohungarumlaut y -30
+KPX ohungarumlaut yacute -30
+KPX ohungarumlaut ydieresis -30
+KPX omacron comma -40
+KPX omacron period -40
+KPX omacron v -15
+KPX omacron w -15
+KPX omacron x -30
+KPX omacron y -30
+KPX omacron yacute -30
+KPX omacron ydieresis -30
+KPX oslash a -55
+KPX oslash aacute -55
+KPX oslash abreve -55
+KPX oslash acircumflex -55
+KPX oslash adieresis -55
+KPX oslash agrave -55
+KPX oslash amacron -55
+KPX oslash aogonek -55
+KPX oslash aring -55
+KPX oslash atilde -55
+KPX oslash b -55
+KPX oslash c -55
+KPX oslash cacute -55
+KPX oslash ccaron -55
+KPX oslash ccedilla -55
+KPX oslash comma -95
+KPX oslash d -55
+KPX oslash dcroat -55
+KPX oslash e -55
+KPX oslash eacute -55
+KPX oslash ecaron -55
+KPX oslash ecircumflex -55
+KPX oslash edieresis -55
+KPX oslash edotaccent -55
+KPX oslash egrave -55
+KPX oslash emacron -55
+KPX oslash eogonek -55
+KPX oslash f -55
+KPX oslash g -55
+KPX oslash gbreve -55
+KPX oslash gcommaaccent -55
+KPX oslash h -55
+KPX oslash i -55
+KPX oslash iacute -55
+KPX oslash icircumflex -55
+KPX oslash idieresis -55
+KPX oslash igrave -55
+KPX oslash imacron -55
+KPX oslash iogonek -55
+KPX oslash j -55
+KPX oslash k -55
+KPX oslash kcommaaccent -55
+KPX oslash l -55
+KPX oslash lacute -55
+KPX oslash lcommaaccent -55
+KPX oslash lslash -55
+KPX oslash m -55
+KPX oslash n -55
+KPX oslash nacute -55
+KPX oslash ncaron -55
+KPX oslash ncommaaccent -55
+KPX oslash ntilde -55
+KPX oslash o -55
+KPX oslash oacute -55
+KPX oslash ocircumflex -55
+KPX oslash odieresis -55
+KPX oslash ograve -55
+KPX oslash ohungarumlaut -55
+KPX oslash omacron -55
+KPX oslash oslash -55
+KPX oslash otilde -55
+KPX oslash p -55
+KPX oslash period -95
+KPX oslash q -55
+KPX oslash r -55
+KPX oslash racute -55
+KPX oslash rcaron -55
+KPX oslash rcommaaccent -55
+KPX oslash s -55
+KPX oslash sacute -55
+KPX oslash scaron -55
+KPX oslash scedilla -55
+KPX oslash scommaaccent -55
+KPX oslash t -55
+KPX oslash tcommaaccent -55
+KPX oslash u -55
+KPX oslash uacute -55
+KPX oslash ucircumflex -55
+KPX oslash udieresis -55
+KPX oslash ugrave -55
+KPX oslash uhungarumlaut -55
+KPX oslash umacron -55
+KPX oslash uogonek -55
+KPX oslash uring -55
+KPX oslash v -70
+KPX oslash w -70
+KPX oslash x -85
+KPX oslash y -70
+KPX oslash yacute -70
+KPX oslash ydieresis -70
+KPX oslash z -55
+KPX oslash zacute -55
+KPX oslash zcaron -55
+KPX oslash zdotaccent -55
+KPX otilde comma -40
+KPX otilde period -40
+KPX otilde v -15
+KPX otilde w -15
+KPX otilde x -30
+KPX otilde y -30
+KPX otilde yacute -30
+KPX otilde ydieresis -30
+KPX p comma -35
+KPX p period -35
+KPX p y -30
+KPX p yacute -30
+KPX p ydieresis -30
+KPX period quotedblright -100
+KPX period quoteright -100
+KPX period space -60
+KPX quotedblright space -40
+KPX quoteleft quoteleft -57
+KPX quoteright d -50
+KPX quoteright dcroat -50
+KPX quoteright quoteright -57
+KPX quoteright r -50
+KPX quoteright racute -50
+KPX quoteright rcaron -50
+KPX quoteright rcommaaccent -50
+KPX quoteright s -50
+KPX quoteright sacute -50
+KPX quoteright scaron -50
+KPX quoteright scedilla -50
+KPX quoteright scommaaccent -50
+KPX quoteright space -70
+KPX r a -10
+KPX r aacute -10
+KPX r abreve -10
+KPX r acircumflex -10
+KPX r adieresis -10
+KPX r agrave -10
+KPX r amacron -10
+KPX r aogonek -10
+KPX r aring -10
+KPX r atilde -10
+KPX r colon 30
+KPX r comma -50
+KPX r i 15
+KPX r iacute 15
+KPX r icircumflex 15
+KPX r idieresis 15
+KPX r igrave 15
+KPX r imacron 15
+KPX r iogonek 15
+KPX r k 15
+KPX r kcommaaccent 15
+KPX r l 15
+KPX r lacute 15
+KPX r lcommaaccent 15
+KPX r lslash 15
+KPX r m 25
+KPX r n 25
+KPX r nacute 25
+KPX r ncaron 25
+KPX r ncommaaccent 25
+KPX r ntilde 25
+KPX r p 30
+KPX r period -50
+KPX r semicolon 30
+KPX r t 40
+KPX r tcommaaccent 40
+KPX r u 15
+KPX r uacute 15
+KPX r ucircumflex 15
+KPX r udieresis 15
+KPX r ugrave 15
+KPX r uhungarumlaut 15
+KPX r umacron 15
+KPX r uogonek 15
+KPX r uring 15
+KPX r v 30
+KPX r y 30
+KPX r yacute 30
+KPX r ydieresis 30
+KPX racute a -10
+KPX racute aacute -10
+KPX racute abreve -10
+KPX racute acircumflex -10
+KPX racute adieresis -10
+KPX racute agrave -10
+KPX racute amacron -10
+KPX racute aogonek -10
+KPX racute aring -10
+KPX racute atilde -10
+KPX racute colon 30
+KPX racute comma -50
+KPX racute i 15
+KPX racute iacute 15
+KPX racute icircumflex 15
+KPX racute idieresis 15
+KPX racute igrave 15
+KPX racute imacron 15
+KPX racute iogonek 15
+KPX racute k 15
+KPX racute kcommaaccent 15
+KPX racute l 15
+KPX racute lacute 15
+KPX racute lcommaaccent 15
+KPX racute lslash 15
+KPX racute m 25
+KPX racute n 25
+KPX racute nacute 25
+KPX racute ncaron 25
+KPX racute ncommaaccent 25
+KPX racute ntilde 25
+KPX racute p 30
+KPX racute period -50
+KPX racute semicolon 30
+KPX racute t 40
+KPX racute tcommaaccent 40
+KPX racute u 15
+KPX racute uacute 15
+KPX racute ucircumflex 15
+KPX racute udieresis 15
+KPX racute ugrave 15
+KPX racute uhungarumlaut 15
+KPX racute umacron 15
+KPX racute uogonek 15
+KPX racute uring 15
+KPX racute v 30
+KPX racute y 30
+KPX racute yacute 30
+KPX racute ydieresis 30
+KPX rcaron a -10
+KPX rcaron aacute -10
+KPX rcaron abreve -10
+KPX rcaron acircumflex -10
+KPX rcaron adieresis -10
+KPX rcaron agrave -10
+KPX rcaron amacron -10
+KPX rcaron aogonek -10
+KPX rcaron aring -10
+KPX rcaron atilde -10
+KPX rcaron colon 30
+KPX rcaron comma -50
+KPX rcaron i 15
+KPX rcaron iacute 15
+KPX rcaron icircumflex 15
+KPX rcaron idieresis 15
+KPX rcaron igrave 15
+KPX rcaron imacron 15
+KPX rcaron iogonek 15
+KPX rcaron k 15
+KPX rcaron kcommaaccent 15
+KPX rcaron l 15
+KPX rcaron lacute 15
+KPX rcaron lcommaaccent 15
+KPX rcaron lslash 15
+KPX rcaron m 25
+KPX rcaron n 25
+KPX rcaron nacute 25
+KPX rcaron ncaron 25
+KPX rcaron ncommaaccent 25
+KPX rcaron ntilde 25
+KPX rcaron p 30
+KPX rcaron period -50
+KPX rcaron semicolon 30
+KPX rcaron t 40
+KPX rcaron tcommaaccent 40
+KPX rcaron u 15
+KPX rcaron uacute 15
+KPX rcaron ucircumflex 15
+KPX rcaron udieresis 15
+KPX rcaron ugrave 15
+KPX rcaron uhungarumlaut 15
+KPX rcaron umacron 15
+KPX rcaron uogonek 15
+KPX rcaron uring 15
+KPX rcaron v 30
+KPX rcaron y 30
+KPX rcaron yacute 30
+KPX rcaron ydieresis 30
+KPX rcommaaccent a -10
+KPX rcommaaccent aacute -10
+KPX rcommaaccent abreve -10
+KPX rcommaaccent acircumflex -10
+KPX rcommaaccent adieresis -10
+KPX rcommaaccent agrave -10
+KPX rcommaaccent amacron -10
+KPX rcommaaccent aogonek -10
+KPX rcommaaccent aring -10
+KPX rcommaaccent atilde -10
+KPX rcommaaccent colon 30
+KPX rcommaaccent comma -50
+KPX rcommaaccent i 15
+KPX rcommaaccent iacute 15
+KPX rcommaaccent icircumflex 15
+KPX rcommaaccent idieresis 15
+KPX rcommaaccent igrave 15
+KPX rcommaaccent imacron 15
+KPX rcommaaccent iogonek 15
+KPX rcommaaccent k 15
+KPX rcommaaccent kcommaaccent 15
+KPX rcommaaccent l 15
+KPX rcommaaccent lacute 15
+KPX rcommaaccent lcommaaccent 15
+KPX rcommaaccent lslash 15
+KPX rcommaaccent m 25
+KPX rcommaaccent n 25
+KPX rcommaaccent nacute 25
+KPX rcommaaccent ncaron 25
+KPX rcommaaccent ncommaaccent 25
+KPX rcommaaccent ntilde 25
+KPX rcommaaccent p 30
+KPX rcommaaccent period -50
+KPX rcommaaccent semicolon 30
+KPX rcommaaccent t 40
+KPX rcommaaccent tcommaaccent 40
+KPX rcommaaccent u 15
+KPX rcommaaccent uacute 15
+KPX rcommaaccent ucircumflex 15
+KPX rcommaaccent udieresis 15
+KPX rcommaaccent ugrave 15
+KPX rcommaaccent uhungarumlaut 15
+KPX rcommaaccent umacron 15
+KPX rcommaaccent uogonek 15
+KPX rcommaaccent uring 15
+KPX rcommaaccent v 30
+KPX rcommaaccent y 30
+KPX rcommaaccent yacute 30
+KPX rcommaaccent ydieresis 30
+KPX s comma -15
+KPX s period -15
+KPX s w -30
+KPX sacute comma -15
+KPX sacute period -15
+KPX sacute w -30
+KPX scaron comma -15
+KPX scaron period -15
+KPX scaron w -30
+KPX scedilla comma -15
+KPX scedilla period -15
+KPX scedilla w -30
+KPX scommaaccent comma -15
+KPX scommaaccent period -15
+KPX scommaaccent w -30
+KPX semicolon space -50
+KPX space T -50
+KPX space Tcaron -50
+KPX space Tcommaaccent -50
+KPX space V -50
+KPX space W -40
+KPX space Y -90
+KPX space Yacute -90
+KPX space Ydieresis -90
+KPX space quotedblleft -30
+KPX space quoteleft -60
+KPX v a -25
+KPX v aacute -25
+KPX v abreve -25
+KPX v acircumflex -25
+KPX v adieresis -25
+KPX v agrave -25
+KPX v amacron -25
+KPX v aogonek -25
+KPX v aring -25
+KPX v atilde -25
+KPX v comma -80
+KPX v e -25
+KPX v eacute -25
+KPX v ecaron -25
+KPX v ecircumflex -25
+KPX v edieresis -25
+KPX v edotaccent -25
+KPX v egrave -25
+KPX v emacron -25
+KPX v eogonek -25
+KPX v o -25
+KPX v oacute -25
+KPX v ocircumflex -25
+KPX v odieresis -25
+KPX v ograve -25
+KPX v ohungarumlaut -25
+KPX v omacron -25
+KPX v oslash -25
+KPX v otilde -25
+KPX v period -80
+KPX w a -15
+KPX w aacute -15
+KPX w abreve -15
+KPX w acircumflex -15
+KPX w adieresis -15
+KPX w agrave -15
+KPX w amacron -15
+KPX w aogonek -15
+KPX w aring -15
+KPX w atilde -15
+KPX w comma -60
+KPX w e -10
+KPX w eacute -10
+KPX w ecaron -10
+KPX w ecircumflex -10
+KPX w edieresis -10
+KPX w edotaccent -10
+KPX w egrave -10
+KPX w emacron -10
+KPX w eogonek -10
+KPX w o -10
+KPX w oacute -10
+KPX w ocircumflex -10
+KPX w odieresis -10
+KPX w ograve -10
+KPX w ohungarumlaut -10
+KPX w omacron -10
+KPX w oslash -10
+KPX w otilde -10
+KPX w period -60
+KPX x e -30
+KPX x eacute -30
+KPX x ecaron -30
+KPX x ecircumflex -30
+KPX x edieresis -30
+KPX x edotaccent -30
+KPX x egrave -30
+KPX x emacron -30
+KPX x eogonek -30
+KPX y a -20
+KPX y aacute -20
+KPX y abreve -20
+KPX y acircumflex -20
+KPX y adieresis -20
+KPX y agrave -20
+KPX y amacron -20
+KPX y aogonek -20
+KPX y aring -20
+KPX y atilde -20
+KPX y comma -100
+KPX y e -20
+KPX y eacute -20
+KPX y ecaron -20
+KPX y ecircumflex -20
+KPX y edieresis -20
+KPX y edotaccent -20
+KPX y egrave -20
+KPX y emacron -20
+KPX y eogonek -20
+KPX y o -20
+KPX y oacute -20
+KPX y ocircumflex -20
+KPX y odieresis -20
+KPX y ograve -20
+KPX y ohungarumlaut -20
+KPX y omacron -20
+KPX y oslash -20
+KPX y otilde -20
+KPX y period -100
+KPX yacute a -20
+KPX yacute aacute -20
+KPX yacute abreve -20
+KPX yacute acircumflex -20
+KPX yacute adieresis -20
+KPX yacute agrave -20
+KPX yacute amacron -20
+KPX yacute aogonek -20
+KPX yacute aring -20
+KPX yacute atilde -20
+KPX yacute comma -100
+KPX yacute e -20
+KPX yacute eacute -20
+KPX yacute ecaron -20
+KPX yacute ecircumflex -20
+KPX yacute edieresis -20
+KPX yacute edotaccent -20
+KPX yacute egrave -20
+KPX yacute emacron -20
+KPX yacute eogonek -20
+KPX yacute o -20
+KPX yacute oacute -20
+KPX yacute ocircumflex -20
+KPX yacute odieresis -20
+KPX yacute ograve -20
+KPX yacute ohungarumlaut -20
+KPX yacute omacron -20
+KPX yacute oslash -20
+KPX yacute otilde -20
+KPX yacute period -100
+KPX ydieresis a -20
+KPX ydieresis aacute -20
+KPX ydieresis abreve -20
+KPX ydieresis acircumflex -20
+KPX ydieresis adieresis -20
+KPX ydieresis agrave -20
+KPX ydieresis amacron -20
+KPX ydieresis aogonek -20
+KPX ydieresis aring -20
+KPX ydieresis atilde -20
+KPX ydieresis comma -100
+KPX ydieresis e -20
+KPX ydieresis eacute -20
+KPX ydieresis ecaron -20
+KPX ydieresis ecircumflex -20
+KPX ydieresis edieresis -20
+KPX ydieresis edotaccent -20
+KPX ydieresis egrave -20
+KPX ydieresis emacron -20
+KPX ydieresis eogonek -20
+KPX ydieresis o -20
+KPX ydieresis oacute -20
+KPX ydieresis ocircumflex -20
+KPX ydieresis odieresis -20
+KPX ydieresis ograve -20
+KPX ydieresis ohungarumlaut -20
+KPX ydieresis omacron -20
+KPX ydieresis oslash -20
+KPX ydieresis otilde -20
+KPX ydieresis period -100
+KPX z e -15
+KPX z eacute -15
+KPX z ecaron -15
+KPX z ecircumflex -15
+KPX z edieresis -15
+KPX z edotaccent -15
+KPX z egrave -15
+KPX z emacron -15
+KPX z eogonek -15
+KPX z o -15
+KPX z oacute -15
+KPX z ocircumflex -15
+KPX z odieresis -15
+KPX z ograve -15
+KPX z ohungarumlaut -15
+KPX z omacron -15
+KPX z oslash -15
+KPX z otilde -15
+KPX zacute e -15
+KPX zacute eacute -15
+KPX zacute ecaron -15
+KPX zacute ecircumflex -15
+KPX zacute edieresis -15
+KPX zacute edotaccent -15
+KPX zacute egrave -15
+KPX zacute emacron -15
+KPX zacute eogonek -15
+KPX zacute o -15
+KPX zacute oacute -15
+KPX zacute ocircumflex -15
+KPX zacute odieresis -15
+KPX zacute ograve -15
+KPX zacute ohungarumlaut -15
+KPX zacute omacron -15
+KPX zacute oslash -15
+KPX zacute otilde -15
+KPX zcaron e -15
+KPX zcaron eacute -15
+KPX zcaron ecaron -15
+KPX zcaron ecircumflex -15
+KPX zcaron edieresis -15
+KPX zcaron edotaccent -15
+KPX zcaron egrave -15
+KPX zcaron emacron -15
+KPX zcaron eogonek -15
+KPX zcaron o -15
+KPX zcaron oacute -15
+KPX zcaron ocircumflex -15
+KPX zcaron odieresis -15
+KPX zcaron ograve -15
+KPX zcaron ohungarumlaut -15
+KPX zcaron omacron -15
+KPX zcaron oslash -15
+KPX zcaron otilde -15
+KPX zdotaccent e -15
+KPX zdotaccent eacute -15
+KPX zdotaccent ecaron -15
+KPX zdotaccent ecircumflex -15
+KPX zdotaccent edieresis -15
+KPX zdotaccent edotaccent -15
+KPX zdotaccent egrave -15
+KPX zdotaccent emacron -15
+KPX zdotaccent eogonek -15
+KPX zdotaccent o -15
+KPX zdotaccent oacute -15
+KPX zdotaccent ocircumflex -15
+KPX zdotaccent odieresis -15
+KPX zdotaccent ograve -15
+KPX zdotaccent ohungarumlaut -15
+KPX zdotaccent omacron -15
+KPX zdotaccent oslash -15
+KPX zdotaccent otilde -15
+EndKernPairs
+EndKernData
+EndFontMetrics
diff --git a/program/libraries/afm/Helvetica.afm b/program/libraries/afm/Helvetica.afm
--- /dev/null
@@ -0,0 +1,3051 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.
+Comment Creation Date: Thu May 1 12:38:23 1997
+Comment UniqueID 43054
+Comment VMusage 37069 48094
+FontName Helvetica
+FullName Helvetica
+FamilyName Helvetica
+Weight Medium
+ItalicAngle 0
+IsFixedPitch false
+CharacterSet ExtendedRoman
+FontBBox -166 -225 1000 931
+UnderlinePosition -100
+UnderlineThickness 50
+Version 002.000
+Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All Rights Reserved.Helvetica is a trademark of Linotype-Hell AG and/or its subsidiaries.
+EncodingScheme AdobeStandardEncoding
+CapHeight 718
+XHeight 523
+Ascender 718
+Descender -207
+StdHW 76
+StdVW 88
+StartCharMetrics 315
+C 32 ; WX 278 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 278 ; N exclam ; B 90 0 187 718 ;
+C 34 ; WX 355 ; N quotedbl ; B 70 463 285 718 ;
+C 35 ; WX 556 ; N numbersign ; B 28 0 529 688 ;
+C 36 ; WX 556 ; N dollar ; B 32 -115 520 775 ;
+C 37 ; WX 889 ; N percent ; B 39 -19 850 703 ;
+C 38 ; WX 667 ; N ampersand ; B 44 -15 645 718 ;
+C 39 ; WX 222 ; N quoteright ; B 53 463 157 718 ;
+C 40 ; WX 333 ; N parenleft ; B 68 -207 299 733 ;
+C 41 ; WX 333 ; N parenright ; B 34 -207 265 733 ;
+C 42 ; WX 389 ; N asterisk ; B 39 431 349 718 ;
+C 43 ; WX 584 ; N plus ; B 39 0 545 505 ;
+C 44 ; WX 278 ; N comma ; B 87 -147 191 106 ;
+C 45 ; WX 333 ; N hyphen ; B 44 232 289 322 ;
+C 46 ; WX 278 ; N period ; B 87 0 191 106 ;
+C 47 ; WX 278 ; N slash ; B -17 -19 295 737 ;
+C 48 ; WX 556 ; N zero ; B 37 -19 519 703 ;
+C 49 ; WX 556 ; N one ; B 101 0 359 703 ;
+C 50 ; WX 556 ; N two ; B 26 0 507 703 ;
+C 51 ; WX 556 ; N three ; B 34 -19 522 703 ;
+C 52 ; WX 556 ; N four ; B 25 0 523 703 ;
+C 53 ; WX 556 ; N five ; B 32 -19 514 688 ;
+C 54 ; WX 556 ; N six ; B 38 -19 518 703 ;
+C 55 ; WX 556 ; N seven ; B 37 0 523 688 ;
+C 56 ; WX 556 ; N eight ; B 38 -19 517 703 ;
+C 57 ; WX 556 ; N nine ; B 42 -19 514 703 ;
+C 58 ; WX 278 ; N colon ; B 87 0 191 516 ;
+C 59 ; WX 278 ; N semicolon ; B 87 -147 191 516 ;
+C 60 ; WX 584 ; N less ; B 48 11 536 495 ;
+C 61 ; WX 584 ; N equal ; B 39 115 545 390 ;
+C 62 ; WX 584 ; N greater ; B 48 11 536 495 ;
+C 63 ; WX 556 ; N question ; B 56 0 492 727 ;
+C 64 ; WX 1015 ; N at ; B 147 -19 868 737 ;
+C 65 ; WX 667 ; N A ; B 14 0 654 718 ;
+C 66 ; WX 667 ; N B ; B 74 0 627 718 ;
+C 67 ; WX 722 ; N C ; B 44 -19 681 737 ;
+C 68 ; WX 722 ; N D ; B 81 0 674 718 ;
+C 69 ; WX 667 ; N E ; B 86 0 616 718 ;
+C 70 ; WX 611 ; N F ; B 86 0 583 718 ;
+C 71 ; WX 778 ; N G ; B 48 -19 704 737 ;
+C 72 ; WX 722 ; N H ; B 77 0 646 718 ;
+C 73 ; WX 278 ; N I ; B 91 0 188 718 ;
+C 74 ; WX 500 ; N J ; B 17 -19 428 718 ;
+C 75 ; WX 667 ; N K ; B 76 0 663 718 ;
+C 76 ; WX 556 ; N L ; B 76 0 537 718 ;
+C 77 ; WX 833 ; N M ; B 73 0 761 718 ;
+C 78 ; WX 722 ; N N ; B 76 0 646 718 ;
+C 79 ; WX 778 ; N O ; B 39 -19 739 737 ;
+C 80 ; WX 667 ; N P ; B 86 0 622 718 ;
+C 81 ; WX 778 ; N Q ; B 39 -56 739 737 ;
+C 82 ; WX 722 ; N R ; B 88 0 684 718 ;
+C 83 ; WX 667 ; N S ; B 49 -19 620 737 ;
+C 84 ; WX 611 ; N T ; B 14 0 597 718 ;
+C 85 ; WX 722 ; N U ; B 79 -19 644 718 ;
+C 86 ; WX 667 ; N V ; B 20 0 647 718 ;
+C 87 ; WX 944 ; N W ; B 16 0 928 718 ;
+C 88 ; WX 667 ; N X ; B 19 0 648 718 ;
+C 89 ; WX 667 ; N Y ; B 14 0 653 718 ;
+C 90 ; WX 611 ; N Z ; B 23 0 588 718 ;
+C 91 ; WX 278 ; N bracketleft ; B 63 -196 250 722 ;
+C 92 ; WX 278 ; N backslash ; B -17 -19 295 737 ;
+C 93 ; WX 278 ; N bracketright ; B 28 -196 215 722 ;
+C 94 ; WX 469 ; N asciicircum ; B -14 264 483 688 ;
+C 95 ; WX 556 ; N underscore ; B 0 -125 556 -75 ;
+C 96 ; WX 222 ; N quoteleft ; B 65 470 169 725 ;
+C 97 ; WX 556 ; N a ; B 36 -15 530 538 ;
+C 98 ; WX 556 ; N b ; B 58 -15 517 718 ;
+C 99 ; WX 500 ; N c ; B 30 -15 477 538 ;
+C 100 ; WX 556 ; N d ; B 35 -15 499 718 ;
+C 101 ; WX 556 ; N e ; B 40 -15 516 538 ;
+C 102 ; WX 278 ; N f ; B 14 0 262 728 ; L i fi ; L l fl ;
+C 103 ; WX 556 ; N g ; B 40 -220 499 538 ;
+C 104 ; WX 556 ; N h ; B 65 0 491 718 ;
+C 105 ; WX 222 ; N i ; B 67 0 155 718 ;
+C 106 ; WX 222 ; N j ; B -16 -210 155 718 ;
+C 107 ; WX 500 ; N k ; B 67 0 501 718 ;
+C 108 ; WX 222 ; N l ; B 67 0 155 718 ;
+C 109 ; WX 833 ; N m ; B 65 0 769 538 ;
+C 110 ; WX 556 ; N n ; B 65 0 491 538 ;
+C 111 ; WX 556 ; N o ; B 35 -14 521 538 ;
+C 112 ; WX 556 ; N p ; B 58 -207 517 538 ;
+C 113 ; WX 556 ; N q ; B 35 -207 494 538 ;
+C 114 ; WX 333 ; N r ; B 77 0 332 538 ;
+C 115 ; WX 500 ; N s ; B 32 -15 464 538 ;
+C 116 ; WX 278 ; N t ; B 14 -7 257 669 ;
+C 117 ; WX 556 ; N u ; B 68 -15 489 523 ;
+C 118 ; WX 500 ; N v ; B 8 0 492 523 ;
+C 119 ; WX 722 ; N w ; B 14 0 709 523 ;
+C 120 ; WX 500 ; N x ; B 11 0 490 523 ;
+C 121 ; WX 500 ; N y ; B 11 -214 489 523 ;
+C 122 ; WX 500 ; N z ; B 31 0 469 523 ;
+C 123 ; WX 334 ; N braceleft ; B 42 -196 292 722 ;
+C 124 ; WX 260 ; N bar ; B 94 -225 167 775 ;
+C 125 ; WX 334 ; N braceright ; B 42 -196 292 722 ;
+C 126 ; WX 584 ; N asciitilde ; B 61 180 523 326 ;
+C 161 ; WX 333 ; N exclamdown ; B 118 -195 215 523 ;
+C 162 ; WX 556 ; N cent ; B 51 -115 513 623 ;
+C 163 ; WX 556 ; N sterling ; B 33 -16 539 718 ;
+C 164 ; WX 167 ; N fraction ; B -166 -19 333 703 ;
+C 165 ; WX 556 ; N yen ; B 3 0 553 688 ;
+C 166 ; WX 556 ; N florin ; B -11 -207 501 737 ;
+C 167 ; WX 556 ; N section ; B 43 -191 512 737 ;
+C 168 ; WX 556 ; N currency ; B 28 99 528 603 ;
+C 169 ; WX 191 ; N quotesingle ; B 59 463 132 718 ;
+C 170 ; WX 333 ; N quotedblleft ; B 38 470 307 725 ;
+C 171 ; WX 556 ; N guillemotleft ; B 97 108 459 446 ;
+C 172 ; WX 333 ; N guilsinglleft ; B 88 108 245 446 ;
+C 173 ; WX 333 ; N guilsinglright ; B 88 108 245 446 ;
+C 174 ; WX 500 ; N fi ; B 14 0 434 728 ;
+C 175 ; WX 500 ; N fl ; B 14 0 432 728 ;
+C 177 ; WX 556 ; N endash ; B 0 240 556 313 ;
+C 178 ; WX 556 ; N dagger ; B 43 -159 514 718 ;
+C 179 ; WX 556 ; N daggerdbl ; B 43 -159 514 718 ;
+C 180 ; WX 278 ; N periodcentered ; B 77 190 202 315 ;
+C 182 ; WX 537 ; N paragraph ; B 18 -173 497 718 ;
+C 183 ; WX 350 ; N bullet ; B 18 202 333 517 ;
+C 184 ; WX 222 ; N quotesinglbase ; B 53 -149 157 106 ;
+C 185 ; WX 333 ; N quotedblbase ; B 26 -149 295 106 ;
+C 186 ; WX 333 ; N quotedblright ; B 26 463 295 718 ;
+C 187 ; WX 556 ; N guillemotright ; B 97 108 459 446 ;
+C 188 ; WX 1000 ; N ellipsis ; B 115 0 885 106 ;
+C 189 ; WX 1000 ; N perthousand ; B 7 -19 994 703 ;
+C 191 ; WX 611 ; N questiondown ; B 91 -201 527 525 ;
+C 193 ; WX 333 ; N grave ; B 14 593 211 734 ;
+C 194 ; WX 333 ; N acute ; B 122 593 319 734 ;
+C 195 ; WX 333 ; N circumflex ; B 21 593 312 734 ;
+C 196 ; WX 333 ; N tilde ; B -4 606 337 722 ;
+C 197 ; WX 333 ; N macron ; B 10 627 323 684 ;
+C 198 ; WX 333 ; N breve ; B 13 595 321 731 ;
+C 199 ; WX 333 ; N dotaccent ; B 121 604 212 706 ;
+C 200 ; WX 333 ; N dieresis ; B 40 604 293 706 ;
+C 202 ; WX 333 ; N ring ; B 75 572 259 756 ;
+C 203 ; WX 333 ; N cedilla ; B 45 -225 259 0 ;
+C 205 ; WX 333 ; N hungarumlaut ; B 31 593 409 734 ;
+C 206 ; WX 333 ; N ogonek ; B 73 -225 287 0 ;
+C 207 ; WX 333 ; N caron ; B 21 593 312 734 ;
+C 208 ; WX 1000 ; N emdash ; B 0 240 1000 313 ;
+C 225 ; WX 1000 ; N AE ; B 8 0 951 718 ;
+C 227 ; WX 370 ; N ordfeminine ; B 24 405 346 737 ;
+C 232 ; WX 556 ; N Lslash ; B -20 0 537 718 ;
+C 233 ; WX 778 ; N Oslash ; B 39 -19 740 737 ;
+C 234 ; WX 1000 ; N OE ; B 36 -19 965 737 ;
+C 235 ; WX 365 ; N ordmasculine ; B 25 405 341 737 ;
+C 241 ; WX 889 ; N ae ; B 36 -15 847 538 ;
+C 245 ; WX 278 ; N dotlessi ; B 95 0 183 523 ;
+C 248 ; WX 222 ; N lslash ; B -20 0 242 718 ;
+C 249 ; WX 611 ; N oslash ; B 28 -22 537 545 ;
+C 250 ; WX 944 ; N oe ; B 35 -15 902 538 ;
+C 251 ; WX 611 ; N germandbls ; B 67 -15 571 728 ;
+C -1 ; WX 278 ; N Idieresis ; B 13 0 266 901 ;
+C -1 ; WX 556 ; N eacute ; B 40 -15 516 734 ;
+C -1 ; WX 556 ; N abreve ; B 36 -15 530 731 ;
+C -1 ; WX 556 ; N uhungarumlaut ; B 68 -15 521 734 ;
+C -1 ; WX 556 ; N ecaron ; B 40 -15 516 734 ;
+C -1 ; WX 667 ; N Ydieresis ; B 14 0 653 901 ;
+C -1 ; WX 584 ; N divide ; B 39 -19 545 524 ;
+C -1 ; WX 667 ; N Yacute ; B 14 0 653 929 ;
+C -1 ; WX 667 ; N Acircumflex ; B 14 0 654 929 ;
+C -1 ; WX 556 ; N aacute ; B 36 -15 530 734 ;
+C -1 ; WX 722 ; N Ucircumflex ; B 79 -19 644 929 ;
+C -1 ; WX 500 ; N yacute ; B 11 -214 489 734 ;
+C -1 ; WX 500 ; N scommaaccent ; B 32 -225 464 538 ;
+C -1 ; WX 556 ; N ecircumflex ; B 40 -15 516 734 ;
+C -1 ; WX 722 ; N Uring ; B 79 -19 644 931 ;
+C -1 ; WX 722 ; N Udieresis ; B 79 -19 644 901 ;
+C -1 ; WX 556 ; N aogonek ; B 36 -220 547 538 ;
+C -1 ; WX 722 ; N Uacute ; B 79 -19 644 929 ;
+C -1 ; WX 556 ; N uogonek ; B 68 -225 519 523 ;
+C -1 ; WX 667 ; N Edieresis ; B 86 0 616 901 ;
+C -1 ; WX 722 ; N Dcroat ; B 0 0 674 718 ;
+C -1 ; WX 250 ; N commaaccent ; B 87 -225 181 -40 ;
+C -1 ; WX 737 ; N copyright ; B -14 -19 752 737 ;
+C -1 ; WX 667 ; N Emacron ; B 86 0 616 879 ;
+C -1 ; WX 500 ; N ccaron ; B 30 -15 477 734 ;
+C -1 ; WX 556 ; N aring ; B 36 -15 530 756 ;
+C -1 ; WX 722 ; N Ncommaaccent ; B 76 -225 646 718 ;
+C -1 ; WX 222 ; N lacute ; B 67 0 264 929 ;
+C -1 ; WX 556 ; N agrave ; B 36 -15 530 734 ;
+C -1 ; WX 611 ; N Tcommaaccent ; B 14 -225 597 718 ;
+C -1 ; WX 722 ; N Cacute ; B 44 -19 681 929 ;
+C -1 ; WX 556 ; N atilde ; B 36 -15 530 722 ;
+C -1 ; WX 667 ; N Edotaccent ; B 86 0 616 901 ;
+C -1 ; WX 500 ; N scaron ; B 32 -15 464 734 ;
+C -1 ; WX 500 ; N scedilla ; B 32 -225 464 538 ;
+C -1 ; WX 278 ; N iacute ; B 95 0 292 734 ;
+C -1 ; WX 471 ; N lozenge ; B 10 0 462 728 ;
+C -1 ; WX 722 ; N Rcaron ; B 88 0 684 929 ;
+C -1 ; WX 778 ; N Gcommaaccent ; B 48 -225 704 737 ;
+C -1 ; WX 556 ; N ucircumflex ; B 68 -15 489 734 ;
+C -1 ; WX 556 ; N acircumflex ; B 36 -15 530 734 ;
+C -1 ; WX 667 ; N Amacron ; B 14 0 654 879 ;
+C -1 ; WX 333 ; N rcaron ; B 61 0 352 734 ;
+C -1 ; WX 500 ; N ccedilla ; B 30 -225 477 538 ;
+C -1 ; WX 611 ; N Zdotaccent ; B 23 0 588 901 ;
+C -1 ; WX 667 ; N Thorn ; B 86 0 622 718 ;
+C -1 ; WX 778 ; N Omacron ; B 39 -19 739 879 ;
+C -1 ; WX 722 ; N Racute ; B 88 0 684 929 ;
+C -1 ; WX 667 ; N Sacute ; B 49 -19 620 929 ;
+C -1 ; WX 643 ; N dcaron ; B 35 -15 655 718 ;
+C -1 ; WX 722 ; N Umacron ; B 79 -19 644 879 ;
+C -1 ; WX 556 ; N uring ; B 68 -15 489 756 ;
+C -1 ; WX 333 ; N threesuperior ; B 5 270 325 703 ;
+C -1 ; WX 778 ; N Ograve ; B 39 -19 739 929 ;
+C -1 ; WX 667 ; N Agrave ; B 14 0 654 929 ;
+C -1 ; WX 667 ; N Abreve ; B 14 0 654 926 ;
+C -1 ; WX 584 ; N multiply ; B 39 0 545 506 ;
+C -1 ; WX 556 ; N uacute ; B 68 -15 489 734 ;
+C -1 ; WX 611 ; N Tcaron ; B 14 0 597 929 ;
+C -1 ; WX 476 ; N partialdiff ; B 13 -38 463 714 ;
+C -1 ; WX 500 ; N ydieresis ; B 11 -214 489 706 ;
+C -1 ; WX 722 ; N Nacute ; B 76 0 646 929 ;
+C -1 ; WX 278 ; N icircumflex ; B -6 0 285 734 ;
+C -1 ; WX 667 ; N Ecircumflex ; B 86 0 616 929 ;
+C -1 ; WX 556 ; N adieresis ; B 36 -15 530 706 ;
+C -1 ; WX 556 ; N edieresis ; B 40 -15 516 706 ;
+C -1 ; WX 500 ; N cacute ; B 30 -15 477 734 ;
+C -1 ; WX 556 ; N nacute ; B 65 0 491 734 ;
+C -1 ; WX 556 ; N umacron ; B 68 -15 489 684 ;
+C -1 ; WX 722 ; N Ncaron ; B 76 0 646 929 ;
+C -1 ; WX 278 ; N Iacute ; B 91 0 292 929 ;
+C -1 ; WX 584 ; N plusminus ; B 39 0 545 506 ;
+C -1 ; WX 260 ; N brokenbar ; B 94 -150 167 700 ;
+C -1 ; WX 737 ; N registered ; B -14 -19 752 737 ;
+C -1 ; WX 778 ; N Gbreve ; B 48 -19 704 926 ;
+C -1 ; WX 278 ; N Idotaccent ; B 91 0 188 901 ;
+C -1 ; WX 600 ; N summation ; B 15 -10 586 706 ;
+C -1 ; WX 667 ; N Egrave ; B 86 0 616 929 ;
+C -1 ; WX 333 ; N racute ; B 77 0 332 734 ;
+C -1 ; WX 556 ; N omacron ; B 35 -14 521 684 ;
+C -1 ; WX 611 ; N Zacute ; B 23 0 588 929 ;
+C -1 ; WX 611 ; N Zcaron ; B 23 0 588 929 ;
+C -1 ; WX 549 ; N greaterequal ; B 26 0 523 674 ;
+C -1 ; WX 722 ; N Eth ; B 0 0 674 718 ;
+C -1 ; WX 722 ; N Ccedilla ; B 44 -225 681 737 ;
+C -1 ; WX 222 ; N lcommaaccent ; B 67 -225 167 718 ;
+C -1 ; WX 317 ; N tcaron ; B 14 -7 329 808 ;
+C -1 ; WX 556 ; N eogonek ; B 40 -225 516 538 ;
+C -1 ; WX 722 ; N Uogonek ; B 79 -225 644 718 ;
+C -1 ; WX 667 ; N Aacute ; B 14 0 654 929 ;
+C -1 ; WX 667 ; N Adieresis ; B 14 0 654 901 ;
+C -1 ; WX 556 ; N egrave ; B 40 -15 516 734 ;
+C -1 ; WX 500 ; N zacute ; B 31 0 469 734 ;
+C -1 ; WX 222 ; N iogonek ; B -31 -225 183 718 ;
+C -1 ; WX 778 ; N Oacute ; B 39 -19 739 929 ;
+C -1 ; WX 556 ; N oacute ; B 35 -14 521 734 ;
+C -1 ; WX 556 ; N amacron ; B 36 -15 530 684 ;
+C -1 ; WX 500 ; N sacute ; B 32 -15 464 734 ;
+C -1 ; WX 278 ; N idieresis ; B 13 0 266 706 ;
+C -1 ; WX 778 ; N Ocircumflex ; B 39 -19 739 929 ;
+C -1 ; WX 722 ; N Ugrave ; B 79 -19 644 929 ;
+C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ;
+C -1 ; WX 556 ; N thorn ; B 58 -207 517 718 ;
+C -1 ; WX 333 ; N twosuperior ; B 4 281 323 703 ;
+C -1 ; WX 778 ; N Odieresis ; B 39 -19 739 901 ;
+C -1 ; WX 556 ; N mu ; B 68 -207 489 523 ;
+C -1 ; WX 278 ; N igrave ; B -13 0 184 734 ;
+C -1 ; WX 556 ; N ohungarumlaut ; B 35 -14 521 734 ;
+C -1 ; WX 667 ; N Eogonek ; B 86 -220 633 718 ;
+C -1 ; WX 556 ; N dcroat ; B 35 -15 550 718 ;
+C -1 ; WX 834 ; N threequarters ; B 45 -19 810 703 ;
+C -1 ; WX 667 ; N Scedilla ; B 49 -225 620 737 ;
+C -1 ; WX 299 ; N lcaron ; B 67 0 311 718 ;
+C -1 ; WX 667 ; N Kcommaaccent ; B 76 -225 663 718 ;
+C -1 ; WX 556 ; N Lacute ; B 76 0 537 929 ;
+C -1 ; WX 1000 ; N trademark ; B 46 306 903 718 ;
+C -1 ; WX 556 ; N edotaccent ; B 40 -15 516 706 ;
+C -1 ; WX 278 ; N Igrave ; B -13 0 188 929 ;
+C -1 ; WX 278 ; N Imacron ; B -17 0 296 879 ;
+C -1 ; WX 556 ; N Lcaron ; B 76 0 537 718 ;
+C -1 ; WX 834 ; N onehalf ; B 43 -19 773 703 ;
+C -1 ; WX 549 ; N lessequal ; B 26 0 523 674 ;
+C -1 ; WX 556 ; N ocircumflex ; B 35 -14 521 734 ;
+C -1 ; WX 556 ; N ntilde ; B 65 0 491 722 ;
+C -1 ; WX 722 ; N Uhungarumlaut ; B 79 -19 644 929 ;
+C -1 ; WX 667 ; N Eacute ; B 86 0 616 929 ;
+C -1 ; WX 556 ; N emacron ; B 40 -15 516 684 ;
+C -1 ; WX 556 ; N gbreve ; B 40 -220 499 731 ;
+C -1 ; WX 834 ; N onequarter ; B 73 -19 756 703 ;
+C -1 ; WX 667 ; N Scaron ; B 49 -19 620 929 ;
+C -1 ; WX 667 ; N Scommaaccent ; B 49 -225 620 737 ;
+C -1 ; WX 778 ; N Ohungarumlaut ; B 39 -19 739 929 ;
+C -1 ; WX 400 ; N degree ; B 54 411 346 703 ;
+C -1 ; WX 556 ; N ograve ; B 35 -14 521 734 ;
+C -1 ; WX 722 ; N Ccaron ; B 44 -19 681 929 ;
+C -1 ; WX 556 ; N ugrave ; B 68 -15 489 734 ;
+C -1 ; WX 453 ; N radical ; B -4 -80 458 762 ;
+C -1 ; WX 722 ; N Dcaron ; B 81 0 674 929 ;
+C -1 ; WX 333 ; N rcommaaccent ; B 77 -225 332 538 ;
+C -1 ; WX 722 ; N Ntilde ; B 76 0 646 917 ;
+C -1 ; WX 556 ; N otilde ; B 35 -14 521 722 ;
+C -1 ; WX 722 ; N Rcommaaccent ; B 88 -225 684 718 ;
+C -1 ; WX 556 ; N Lcommaaccent ; B 76 -225 537 718 ;
+C -1 ; WX 667 ; N Atilde ; B 14 0 654 917 ;
+C -1 ; WX 667 ; N Aogonek ; B 14 -225 654 718 ;
+C -1 ; WX 667 ; N Aring ; B 14 0 654 931 ;
+C -1 ; WX 778 ; N Otilde ; B 39 -19 739 917 ;
+C -1 ; WX 500 ; N zdotaccent ; B 31 0 469 706 ;
+C -1 ; WX 667 ; N Ecaron ; B 86 0 616 929 ;
+C -1 ; WX 278 ; N Iogonek ; B -3 -225 211 718 ;
+C -1 ; WX 500 ; N kcommaaccent ; B 67 -225 501 718 ;
+C -1 ; WX 584 ; N minus ; B 39 216 545 289 ;
+C -1 ; WX 278 ; N Icircumflex ; B -6 0 285 929 ;
+C -1 ; WX 556 ; N ncaron ; B 65 0 491 734 ;
+C -1 ; WX 278 ; N tcommaaccent ; B 14 -225 257 669 ;
+C -1 ; WX 584 ; N logicalnot ; B 39 108 545 390 ;
+C -1 ; WX 556 ; N odieresis ; B 35 -14 521 706 ;
+C -1 ; WX 556 ; N udieresis ; B 68 -15 489 706 ;
+C -1 ; WX 549 ; N notequal ; B 12 -35 537 551 ;
+C -1 ; WX 556 ; N gcommaaccent ; B 40 -220 499 822 ;
+C -1 ; WX 556 ; N eth ; B 35 -15 522 737 ;
+C -1 ; WX 500 ; N zcaron ; B 31 0 469 734 ;
+C -1 ; WX 556 ; N ncommaaccent ; B 65 -225 491 538 ;
+C -1 ; WX 333 ; N onesuperior ; B 43 281 222 703 ;
+C -1 ; WX 278 ; N imacron ; B 5 0 272 684 ;
+C -1 ; WX 556 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+StartKernData
+StartKernPairs 2705
+KPX A C -30
+KPX A Cacute -30
+KPX A Ccaron -30
+KPX A Ccedilla -30
+KPX A G -30
+KPX A Gbreve -30
+KPX A Gcommaaccent -30
+KPX A O -30
+KPX A Oacute -30
+KPX A Ocircumflex -30
+KPX A Odieresis -30
+KPX A Ograve -30
+KPX A Ohungarumlaut -30
+KPX A Omacron -30
+KPX A Oslash -30
+KPX A Otilde -30
+KPX A Q -30
+KPX A T -120
+KPX A Tcaron -120
+KPX A Tcommaaccent -120
+KPX A U -50
+KPX A Uacute -50
+KPX A Ucircumflex -50
+KPX A Udieresis -50
+KPX A Ugrave -50
+KPX A Uhungarumlaut -50
+KPX A Umacron -50
+KPX A Uogonek -50
+KPX A Uring -50
+KPX A V -70
+KPX A W -50
+KPX A Y -100
+KPX A Yacute -100
+KPX A Ydieresis -100
+KPX A u -30
+KPX A uacute -30
+KPX A ucircumflex -30
+KPX A udieresis -30
+KPX A ugrave -30
+KPX A uhungarumlaut -30
+KPX A umacron -30
+KPX A uogonek -30
+KPX A uring -30
+KPX A v -40
+KPX A w -40
+KPX A y -40
+KPX A yacute -40
+KPX A ydieresis -40
+KPX Aacute C -30
+KPX Aacute Cacute -30
+KPX Aacute Ccaron -30
+KPX Aacute Ccedilla -30
+KPX Aacute G -30
+KPX Aacute Gbreve -30
+KPX Aacute Gcommaaccent -30
+KPX Aacute O -30
+KPX Aacute Oacute -30
+KPX Aacute Ocircumflex -30
+KPX Aacute Odieresis -30
+KPX Aacute Ograve -30
+KPX Aacute Ohungarumlaut -30
+KPX Aacute Omacron -30
+KPX Aacute Oslash -30
+KPX Aacute Otilde -30
+KPX Aacute Q -30
+KPX Aacute T -120
+KPX Aacute Tcaron -120
+KPX Aacute Tcommaaccent -120
+KPX Aacute U -50
+KPX Aacute Uacute -50
+KPX Aacute Ucircumflex -50
+KPX Aacute Udieresis -50
+KPX Aacute Ugrave -50
+KPX Aacute Uhungarumlaut -50
+KPX Aacute Umacron -50
+KPX Aacute Uogonek -50
+KPX Aacute Uring -50
+KPX Aacute V -70
+KPX Aacute W -50
+KPX Aacute Y -100
+KPX Aacute Yacute -100
+KPX Aacute Ydieresis -100
+KPX Aacute u -30
+KPX Aacute uacute -30
+KPX Aacute ucircumflex -30
+KPX Aacute udieresis -30
+KPX Aacute ugrave -30
+KPX Aacute uhungarumlaut -30
+KPX Aacute umacron -30
+KPX Aacute uogonek -30
+KPX Aacute uring -30
+KPX Aacute v -40
+KPX Aacute w -40
+KPX Aacute y -40
+KPX Aacute yacute -40
+KPX Aacute ydieresis -40
+KPX Abreve C -30
+KPX Abreve Cacute -30
+KPX Abreve Ccaron -30
+KPX Abreve Ccedilla -30
+KPX Abreve G -30
+KPX Abreve Gbreve -30
+KPX Abreve Gcommaaccent -30
+KPX Abreve O -30
+KPX Abreve Oacute -30
+KPX Abreve Ocircumflex -30
+KPX Abreve Odieresis -30
+KPX Abreve Ograve -30
+KPX Abreve Ohungarumlaut -30
+KPX Abreve Omacron -30
+KPX Abreve Oslash -30
+KPX Abreve Otilde -30
+KPX Abreve Q -30
+KPX Abreve T -120
+KPX Abreve Tcaron -120
+KPX Abreve Tcommaaccent -120
+KPX Abreve U -50
+KPX Abreve Uacute -50
+KPX Abreve Ucircumflex -50
+KPX Abreve Udieresis -50
+KPX Abreve Ugrave -50
+KPX Abreve Uhungarumlaut -50
+KPX Abreve Umacron -50
+KPX Abreve Uogonek -50
+KPX Abreve Uring -50
+KPX Abreve V -70
+KPX Abreve W -50
+KPX Abreve Y -100
+KPX Abreve Yacute -100
+KPX Abreve Ydieresis -100
+KPX Abreve u -30
+KPX Abreve uacute -30
+KPX Abreve ucircumflex -30
+KPX Abreve udieresis -30
+KPX Abreve ugrave -30
+KPX Abreve uhungarumlaut -30
+KPX Abreve umacron -30
+KPX Abreve uogonek -30
+KPX Abreve uring -30
+KPX Abreve v -40
+KPX Abreve w -40
+KPX Abreve y -40
+KPX Abreve yacute -40
+KPX Abreve ydieresis -40
+KPX Acircumflex C -30
+KPX Acircumflex Cacute -30
+KPX Acircumflex Ccaron -30
+KPX Acircumflex Ccedilla -30
+KPX Acircumflex G -30
+KPX Acircumflex Gbreve -30
+KPX Acircumflex Gcommaaccent -30
+KPX Acircumflex O -30
+KPX Acircumflex Oacute -30
+KPX Acircumflex Ocircumflex -30
+KPX Acircumflex Odieresis -30
+KPX Acircumflex Ograve -30
+KPX Acircumflex Ohungarumlaut -30
+KPX Acircumflex Omacron -30
+KPX Acircumflex Oslash -30
+KPX Acircumflex Otilde -30
+KPX Acircumflex Q -30
+KPX Acircumflex T -120
+KPX Acircumflex Tcaron -120
+KPX Acircumflex Tcommaaccent -120
+KPX Acircumflex U -50
+KPX Acircumflex Uacute -50
+KPX Acircumflex Ucircumflex -50
+KPX Acircumflex Udieresis -50
+KPX Acircumflex Ugrave -50
+KPX Acircumflex Uhungarumlaut -50
+KPX Acircumflex Umacron -50
+KPX Acircumflex Uogonek -50
+KPX Acircumflex Uring -50
+KPX Acircumflex V -70
+KPX Acircumflex W -50
+KPX Acircumflex Y -100
+KPX Acircumflex Yacute -100
+KPX Acircumflex Ydieresis -100
+KPX Acircumflex u -30
+KPX Acircumflex uacute -30
+KPX Acircumflex ucircumflex -30
+KPX Acircumflex udieresis -30
+KPX Acircumflex ugrave -30
+KPX Acircumflex uhungarumlaut -30
+KPX Acircumflex umacron -30
+KPX Acircumflex uogonek -30
+KPX Acircumflex uring -30
+KPX Acircumflex v -40
+KPX Acircumflex w -40
+KPX Acircumflex y -40
+KPX Acircumflex yacute -40
+KPX Acircumflex ydieresis -40
+KPX Adieresis C -30
+KPX Adieresis Cacute -30
+KPX Adieresis Ccaron -30
+KPX Adieresis Ccedilla -30
+KPX Adieresis G -30
+KPX Adieresis Gbreve -30
+KPX Adieresis Gcommaaccent -30
+KPX Adieresis O -30
+KPX Adieresis Oacute -30
+KPX Adieresis Ocircumflex -30
+KPX Adieresis Odieresis -30
+KPX Adieresis Ograve -30
+KPX Adieresis Ohungarumlaut -30
+KPX Adieresis Omacron -30
+KPX Adieresis Oslash -30
+KPX Adieresis Otilde -30
+KPX Adieresis Q -30
+KPX Adieresis T -120
+KPX Adieresis Tcaron -120
+KPX Adieresis Tcommaaccent -120
+KPX Adieresis U -50
+KPX Adieresis Uacute -50
+KPX Adieresis Ucircumflex -50
+KPX Adieresis Udieresis -50
+KPX Adieresis Ugrave -50
+KPX Adieresis Uhungarumlaut -50
+KPX Adieresis Umacron -50
+KPX Adieresis Uogonek -50
+KPX Adieresis Uring -50
+KPX Adieresis V -70
+KPX Adieresis W -50
+KPX Adieresis Y -100
+KPX Adieresis Yacute -100
+KPX Adieresis Ydieresis -100
+KPX Adieresis u -30
+KPX Adieresis uacute -30
+KPX Adieresis ucircumflex -30
+KPX Adieresis udieresis -30
+KPX Adieresis ugrave -30
+KPX Adieresis uhungarumlaut -30
+KPX Adieresis umacron -30
+KPX Adieresis uogonek -30
+KPX Adieresis uring -30
+KPX Adieresis v -40
+KPX Adieresis w -40
+KPX Adieresis y -40
+KPX Adieresis yacute -40
+KPX Adieresis ydieresis -40
+KPX Agrave C -30
+KPX Agrave Cacute -30
+KPX Agrave Ccaron -30
+KPX Agrave Ccedilla -30
+KPX Agrave G -30
+KPX Agrave Gbreve -30
+KPX Agrave Gcommaaccent -30
+KPX Agrave O -30
+KPX Agrave Oacute -30
+KPX Agrave Ocircumflex -30
+KPX Agrave Odieresis -30
+KPX Agrave Ograve -30
+KPX Agrave Ohungarumlaut -30
+KPX Agrave Omacron -30
+KPX Agrave Oslash -30
+KPX Agrave Otilde -30
+KPX Agrave Q -30
+KPX Agrave T -120
+KPX Agrave Tcaron -120
+KPX Agrave Tcommaaccent -120
+KPX Agrave U -50
+KPX Agrave Uacute -50
+KPX Agrave Ucircumflex -50
+KPX Agrave Udieresis -50
+KPX Agrave Ugrave -50
+KPX Agrave Uhungarumlaut -50
+KPX Agrave Umacron -50
+KPX Agrave Uogonek -50
+KPX Agrave Uring -50
+KPX Agrave V -70
+KPX Agrave W -50
+KPX Agrave Y -100
+KPX Agrave Yacute -100
+KPX Agrave Ydieresis -100
+KPX Agrave u -30
+KPX Agrave uacute -30
+KPX Agrave ucircumflex -30
+KPX Agrave udieresis -30
+KPX Agrave ugrave -30
+KPX Agrave uhungarumlaut -30
+KPX Agrave umacron -30
+KPX Agrave uogonek -30
+KPX Agrave uring -30
+KPX Agrave v -40
+KPX Agrave w -40
+KPX Agrave y -40
+KPX Agrave yacute -40
+KPX Agrave ydieresis -40
+KPX Amacron C -30
+KPX Amacron Cacute -30
+KPX Amacron Ccaron -30
+KPX Amacron Ccedilla -30
+KPX Amacron G -30
+KPX Amacron Gbreve -30
+KPX Amacron Gcommaaccent -30
+KPX Amacron O -30
+KPX Amacron Oacute -30
+KPX Amacron Ocircumflex -30
+KPX Amacron Odieresis -30
+KPX Amacron Ograve -30
+KPX Amacron Ohungarumlaut -30
+KPX Amacron Omacron -30
+KPX Amacron Oslash -30
+KPX Amacron Otilde -30
+KPX Amacron Q -30
+KPX Amacron T -120
+KPX Amacron Tcaron -120
+KPX Amacron Tcommaaccent -120
+KPX Amacron U -50
+KPX Amacron Uacute -50
+KPX Amacron Ucircumflex -50
+KPX Amacron Udieresis -50
+KPX Amacron Ugrave -50
+KPX Amacron Uhungarumlaut -50
+KPX Amacron Umacron -50
+KPX Amacron Uogonek -50
+KPX Amacron Uring -50
+KPX Amacron V -70
+KPX Amacron W -50
+KPX Amacron Y -100
+KPX Amacron Yacute -100
+KPX Amacron Ydieresis -100
+KPX Amacron u -30
+KPX Amacron uacute -30
+KPX Amacron ucircumflex -30
+KPX Amacron udieresis -30
+KPX Amacron ugrave -30
+KPX Amacron uhungarumlaut -30
+KPX Amacron umacron -30
+KPX Amacron uogonek -30
+KPX Amacron uring -30
+KPX Amacron v -40
+KPX Amacron w -40
+KPX Amacron y -40
+KPX Amacron yacute -40
+KPX Amacron ydieresis -40
+KPX Aogonek C -30
+KPX Aogonek Cacute -30
+KPX Aogonek Ccaron -30
+KPX Aogonek Ccedilla -30
+KPX Aogonek G -30
+KPX Aogonek Gbreve -30
+KPX Aogonek Gcommaaccent -30
+KPX Aogonek O -30
+KPX Aogonek Oacute -30
+KPX Aogonek Ocircumflex -30
+KPX Aogonek Odieresis -30
+KPX Aogonek Ograve -30
+KPX Aogonek Ohungarumlaut -30
+KPX Aogonek Omacron -30
+KPX Aogonek Oslash -30
+KPX Aogonek Otilde -30
+KPX Aogonek Q -30
+KPX Aogonek T -120
+KPX Aogonek Tcaron -120
+KPX Aogonek Tcommaaccent -120
+KPX Aogonek U -50
+KPX Aogonek Uacute -50
+KPX Aogonek Ucircumflex -50
+KPX Aogonek Udieresis -50
+KPX Aogonek Ugrave -50
+KPX Aogonek Uhungarumlaut -50
+KPX Aogonek Umacron -50
+KPX Aogonek Uogonek -50
+KPX Aogonek Uring -50
+KPX Aogonek V -70
+KPX Aogonek W -50
+KPX Aogonek Y -100
+KPX Aogonek Yacute -100
+KPX Aogonek Ydieresis -100
+KPX Aogonek u -30
+KPX Aogonek uacute -30
+KPX Aogonek ucircumflex -30
+KPX Aogonek udieresis -30
+KPX Aogonek ugrave -30
+KPX Aogonek uhungarumlaut -30
+KPX Aogonek umacron -30
+KPX Aogonek uogonek -30
+KPX Aogonek uring -30
+KPX Aogonek v -40
+KPX Aogonek w -40
+KPX Aogonek y -40
+KPX Aogonek yacute -40
+KPX Aogonek ydieresis -40
+KPX Aring C -30
+KPX Aring Cacute -30
+KPX Aring Ccaron -30
+KPX Aring Ccedilla -30
+KPX Aring G -30
+KPX Aring Gbreve -30
+KPX Aring Gcommaaccent -30
+KPX Aring O -30
+KPX Aring Oacute -30
+KPX Aring Ocircumflex -30
+KPX Aring Odieresis -30
+KPX Aring Ograve -30
+KPX Aring Ohungarumlaut -30
+KPX Aring Omacron -30
+KPX Aring Oslash -30
+KPX Aring Otilde -30
+KPX Aring Q -30
+KPX Aring T -120
+KPX Aring Tcaron -120
+KPX Aring Tcommaaccent -120
+KPX Aring U -50
+KPX Aring Uacute -50
+KPX Aring Ucircumflex -50
+KPX Aring Udieresis -50
+KPX Aring Ugrave -50
+KPX Aring Uhungarumlaut -50
+KPX Aring Umacron -50
+KPX Aring Uogonek -50
+KPX Aring Uring -50
+KPX Aring V -70
+KPX Aring W -50
+KPX Aring Y -100
+KPX Aring Yacute -100
+KPX Aring Ydieresis -100
+KPX Aring u -30
+KPX Aring uacute -30
+KPX Aring ucircumflex -30
+KPX Aring udieresis -30
+KPX Aring ugrave -30
+KPX Aring uhungarumlaut -30
+KPX Aring umacron -30
+KPX Aring uogonek -30
+KPX Aring uring -30
+KPX Aring v -40
+KPX Aring w -40
+KPX Aring y -40
+KPX Aring yacute -40
+KPX Aring ydieresis -40
+KPX Atilde C -30
+KPX Atilde Cacute -30
+KPX Atilde Ccaron -30
+KPX Atilde Ccedilla -30
+KPX Atilde G -30
+KPX Atilde Gbreve -30
+KPX Atilde Gcommaaccent -30
+KPX Atilde O -30
+KPX Atilde Oacute -30
+KPX Atilde Ocircumflex -30
+KPX Atilde Odieresis -30
+KPX Atilde Ograve -30
+KPX Atilde Ohungarumlaut -30
+KPX Atilde Omacron -30
+KPX Atilde Oslash -30
+KPX Atilde Otilde -30
+KPX Atilde Q -30
+KPX Atilde T -120
+KPX Atilde Tcaron -120
+KPX Atilde Tcommaaccent -120
+KPX Atilde U -50
+KPX Atilde Uacute -50
+KPX Atilde Ucircumflex -50
+KPX Atilde Udieresis -50
+KPX Atilde Ugrave -50
+KPX Atilde Uhungarumlaut -50
+KPX Atilde Umacron -50
+KPX Atilde Uogonek -50
+KPX Atilde Uring -50
+KPX Atilde V -70
+KPX Atilde W -50
+KPX Atilde Y -100
+KPX Atilde Yacute -100
+KPX Atilde Ydieresis -100
+KPX Atilde u -30
+KPX Atilde uacute -30
+KPX Atilde ucircumflex -30
+KPX Atilde udieresis -30
+KPX Atilde ugrave -30
+KPX Atilde uhungarumlaut -30
+KPX Atilde umacron -30
+KPX Atilde uogonek -30
+KPX Atilde uring -30
+KPX Atilde v -40
+KPX Atilde w -40
+KPX Atilde y -40
+KPX Atilde yacute -40
+KPX Atilde ydieresis -40
+KPX B U -10
+KPX B Uacute -10
+KPX B Ucircumflex -10
+KPX B Udieresis -10
+KPX B Ugrave -10
+KPX B Uhungarumlaut -10
+KPX B Umacron -10
+KPX B Uogonek -10
+KPX B Uring -10
+KPX B comma -20
+KPX B period -20
+KPX C comma -30
+KPX C period -30
+KPX Cacute comma -30
+KPX Cacute period -30
+KPX Ccaron comma -30
+KPX Ccaron period -30
+KPX Ccedilla comma -30
+KPX Ccedilla period -30
+KPX D A -40
+KPX D Aacute -40
+KPX D Abreve -40
+KPX D Acircumflex -40
+KPX D Adieresis -40
+KPX D Agrave -40
+KPX D Amacron -40
+KPX D Aogonek -40
+KPX D Aring -40
+KPX D Atilde -40
+KPX D V -70
+KPX D W -40
+KPX D Y -90
+KPX D Yacute -90
+KPX D Ydieresis -90
+KPX D comma -70
+KPX D period -70
+KPX Dcaron A -40
+KPX Dcaron Aacute -40
+KPX Dcaron Abreve -40
+KPX Dcaron Acircumflex -40
+KPX Dcaron Adieresis -40
+KPX Dcaron Agrave -40
+KPX Dcaron Amacron -40
+KPX Dcaron Aogonek -40
+KPX Dcaron Aring -40
+KPX Dcaron Atilde -40
+KPX Dcaron V -70
+KPX Dcaron W -40
+KPX Dcaron Y -90
+KPX Dcaron Yacute -90
+KPX Dcaron Ydieresis -90
+KPX Dcaron comma -70
+KPX Dcaron period -70
+KPX Dcroat A -40
+KPX Dcroat Aacute -40
+KPX Dcroat Abreve -40
+KPX Dcroat Acircumflex -40
+KPX Dcroat Adieresis -40
+KPX Dcroat Agrave -40
+KPX Dcroat Amacron -40
+KPX Dcroat Aogonek -40
+KPX Dcroat Aring -40
+KPX Dcroat Atilde -40
+KPX Dcroat V -70
+KPX Dcroat W -40
+KPX Dcroat Y -90
+KPX Dcroat Yacute -90
+KPX Dcroat Ydieresis -90
+KPX Dcroat comma -70
+KPX Dcroat period -70
+KPX F A -80
+KPX F Aacute -80
+KPX F Abreve -80
+KPX F Acircumflex -80
+KPX F Adieresis -80
+KPX F Agrave -80
+KPX F Amacron -80
+KPX F Aogonek -80
+KPX F Aring -80
+KPX F Atilde -80
+KPX F a -50
+KPX F aacute -50
+KPX F abreve -50
+KPX F acircumflex -50
+KPX F adieresis -50
+KPX F agrave -50
+KPX F amacron -50
+KPX F aogonek -50
+KPX F aring -50
+KPX F atilde -50
+KPX F comma -150
+KPX F e -30
+KPX F eacute -30
+KPX F ecaron -30
+KPX F ecircumflex -30
+KPX F edieresis -30
+KPX F edotaccent -30
+KPX F egrave -30
+KPX F emacron -30
+KPX F eogonek -30
+KPX F o -30
+KPX F oacute -30
+KPX F ocircumflex -30
+KPX F odieresis -30
+KPX F ograve -30
+KPX F ohungarumlaut -30
+KPX F omacron -30
+KPX F oslash -30
+KPX F otilde -30
+KPX F period -150
+KPX F r -45
+KPX F racute -45
+KPX F rcaron -45
+KPX F rcommaaccent -45
+KPX J A -20
+KPX J Aacute -20
+KPX J Abreve -20
+KPX J Acircumflex -20
+KPX J Adieresis -20
+KPX J Agrave -20
+KPX J Amacron -20
+KPX J Aogonek -20
+KPX J Aring -20
+KPX J Atilde -20
+KPX J a -20
+KPX J aacute -20
+KPX J abreve -20
+KPX J acircumflex -20
+KPX J adieresis -20
+KPX J agrave -20
+KPX J amacron -20
+KPX J aogonek -20
+KPX J aring -20
+KPX J atilde -20
+KPX J comma -30
+KPX J period -30
+KPX J u -20
+KPX J uacute -20
+KPX J ucircumflex -20
+KPX J udieresis -20
+KPX J ugrave -20
+KPX J uhungarumlaut -20
+KPX J umacron -20
+KPX J uogonek -20
+KPX J uring -20
+KPX K O -50
+KPX K Oacute -50
+KPX K Ocircumflex -50
+KPX K Odieresis -50
+KPX K Ograve -50
+KPX K Ohungarumlaut -50
+KPX K Omacron -50
+KPX K Oslash -50
+KPX K Otilde -50
+KPX K e -40
+KPX K eacute -40
+KPX K ecaron -40
+KPX K ecircumflex -40
+KPX K edieresis -40
+KPX K edotaccent -40
+KPX K egrave -40
+KPX K emacron -40
+KPX K eogonek -40
+KPX K o -40
+KPX K oacute -40
+KPX K ocircumflex -40
+KPX K odieresis -40
+KPX K ograve -40
+KPX K ohungarumlaut -40
+KPX K omacron -40
+KPX K oslash -40
+KPX K otilde -40
+KPX K u -30
+KPX K uacute -30
+KPX K ucircumflex -30
+KPX K udieresis -30
+KPX K ugrave -30
+KPX K uhungarumlaut -30
+KPX K umacron -30
+KPX K uogonek -30
+KPX K uring -30
+KPX K y -50
+KPX K yacute -50
+KPX K ydieresis -50
+KPX Kcommaaccent O -50
+KPX Kcommaaccent Oacute -50
+KPX Kcommaaccent Ocircumflex -50
+KPX Kcommaaccent Odieresis -50
+KPX Kcommaaccent Ograve -50
+KPX Kcommaaccent Ohungarumlaut -50
+KPX Kcommaaccent Omacron -50
+KPX Kcommaaccent Oslash -50
+KPX Kcommaaccent Otilde -50
+KPX Kcommaaccent e -40
+KPX Kcommaaccent eacute -40
+KPX Kcommaaccent ecaron -40
+KPX Kcommaaccent ecircumflex -40
+KPX Kcommaaccent edieresis -40
+KPX Kcommaaccent edotaccent -40
+KPX Kcommaaccent egrave -40
+KPX Kcommaaccent emacron -40
+KPX Kcommaaccent eogonek -40
+KPX Kcommaaccent o -40
+KPX Kcommaaccent oacute -40
+KPX Kcommaaccent ocircumflex -40
+KPX Kcommaaccent odieresis -40
+KPX Kcommaaccent ograve -40
+KPX Kcommaaccent ohungarumlaut -40
+KPX Kcommaaccent omacron -40
+KPX Kcommaaccent oslash -40
+KPX Kcommaaccent otilde -40
+KPX Kcommaaccent u -30
+KPX Kcommaaccent uacute -30
+KPX Kcommaaccent ucircumflex -30
+KPX Kcommaaccent udieresis -30
+KPX Kcommaaccent ugrave -30
+KPX Kcommaaccent uhungarumlaut -30
+KPX Kcommaaccent umacron -30
+KPX Kcommaaccent uogonek -30
+KPX Kcommaaccent uring -30
+KPX Kcommaaccent y -50
+KPX Kcommaaccent yacute -50
+KPX Kcommaaccent ydieresis -50
+KPX L T -110
+KPX L Tcaron -110
+KPX L Tcommaaccent -110
+KPX L V -110
+KPX L W -70
+KPX L Y -140
+KPX L Yacute -140
+KPX L Ydieresis -140
+KPX L quotedblright -140
+KPX L quoteright -160
+KPX L y -30
+KPX L yacute -30
+KPX L ydieresis -30
+KPX Lacute T -110
+KPX Lacute Tcaron -110
+KPX Lacute Tcommaaccent -110
+KPX Lacute V -110
+KPX Lacute W -70
+KPX Lacute Y -140
+KPX Lacute Yacute -140
+KPX Lacute Ydieresis -140
+KPX Lacute quotedblright -140
+KPX Lacute quoteright -160
+KPX Lacute y -30
+KPX Lacute yacute -30
+KPX Lacute ydieresis -30
+KPX Lcaron T -110
+KPX Lcaron Tcaron -110
+KPX Lcaron Tcommaaccent -110
+KPX Lcaron V -110
+KPX Lcaron W -70
+KPX Lcaron Y -140
+KPX Lcaron Yacute -140
+KPX Lcaron Ydieresis -140
+KPX Lcaron quotedblright -140
+KPX Lcaron quoteright -160
+KPX Lcaron y -30
+KPX Lcaron yacute -30
+KPX Lcaron ydieresis -30
+KPX Lcommaaccent T -110
+KPX Lcommaaccent Tcaron -110
+KPX Lcommaaccent Tcommaaccent -110
+KPX Lcommaaccent V -110
+KPX Lcommaaccent W -70
+KPX Lcommaaccent Y -140
+KPX Lcommaaccent Yacute -140
+KPX Lcommaaccent Ydieresis -140
+KPX Lcommaaccent quotedblright -140
+KPX Lcommaaccent quoteright -160
+KPX Lcommaaccent y -30
+KPX Lcommaaccent yacute -30
+KPX Lcommaaccent ydieresis -30
+KPX Lslash T -110
+KPX Lslash Tcaron -110
+KPX Lslash Tcommaaccent -110
+KPX Lslash V -110
+KPX Lslash W -70
+KPX Lslash Y -140
+KPX Lslash Yacute -140
+KPX Lslash Ydieresis -140
+KPX Lslash quotedblright -140
+KPX Lslash quoteright -160
+KPX Lslash y -30
+KPX Lslash yacute -30
+KPX Lslash ydieresis -30
+KPX O A -20
+KPX O Aacute -20
+KPX O Abreve -20
+KPX O Acircumflex -20
+KPX O Adieresis -20
+KPX O Agrave -20
+KPX O Amacron -20
+KPX O Aogonek -20
+KPX O Aring -20
+KPX O Atilde -20
+KPX O T -40
+KPX O Tcaron -40
+KPX O Tcommaaccent -40
+KPX O V -50
+KPX O W -30
+KPX O X -60
+KPX O Y -70
+KPX O Yacute -70
+KPX O Ydieresis -70
+KPX O comma -40
+KPX O period -40
+KPX Oacute A -20
+KPX Oacute Aacute -20
+KPX Oacute Abreve -20
+KPX Oacute Acircumflex -20
+KPX Oacute Adieresis -20
+KPX Oacute Agrave -20
+KPX Oacute Amacron -20
+KPX Oacute Aogonek -20
+KPX Oacute Aring -20
+KPX Oacute Atilde -20
+KPX Oacute T -40
+KPX Oacute Tcaron -40
+KPX Oacute Tcommaaccent -40
+KPX Oacute V -50
+KPX Oacute W -30
+KPX Oacute X -60
+KPX Oacute Y -70
+KPX Oacute Yacute -70
+KPX Oacute Ydieresis -70
+KPX Oacute comma -40
+KPX Oacute period -40
+KPX Ocircumflex A -20
+KPX Ocircumflex Aacute -20
+KPX Ocircumflex Abreve -20
+KPX Ocircumflex Acircumflex -20
+KPX Ocircumflex Adieresis -20
+KPX Ocircumflex Agrave -20
+KPX Ocircumflex Amacron -20
+KPX Ocircumflex Aogonek -20
+KPX Ocircumflex Aring -20
+KPX Ocircumflex Atilde -20
+KPX Ocircumflex T -40
+KPX Ocircumflex Tcaron -40
+KPX Ocircumflex Tcommaaccent -40
+KPX Ocircumflex V -50
+KPX Ocircumflex W -30
+KPX Ocircumflex X -60
+KPX Ocircumflex Y -70
+KPX Ocircumflex Yacute -70
+KPX Ocircumflex Ydieresis -70
+KPX Ocircumflex comma -40
+KPX Ocircumflex period -40
+KPX Odieresis A -20
+KPX Odieresis Aacute -20
+KPX Odieresis Abreve -20
+KPX Odieresis Acircumflex -20
+KPX Odieresis Adieresis -20
+KPX Odieresis Agrave -20
+KPX Odieresis Amacron -20
+KPX Odieresis Aogonek -20
+KPX Odieresis Aring -20
+KPX Odieresis Atilde -20
+KPX Odieresis T -40
+KPX Odieresis Tcaron -40
+KPX Odieresis Tcommaaccent -40
+KPX Odieresis V -50
+KPX Odieresis W -30
+KPX Odieresis X -60
+KPX Odieresis Y -70
+KPX Odieresis Yacute -70
+KPX Odieresis Ydieresis -70
+KPX Odieresis comma -40
+KPX Odieresis period -40
+KPX Ograve A -20
+KPX Ograve Aacute -20
+KPX Ograve Abreve -20
+KPX Ograve Acircumflex -20
+KPX Ograve Adieresis -20
+KPX Ograve Agrave -20
+KPX Ograve Amacron -20
+KPX Ograve Aogonek -20
+KPX Ograve Aring -20
+KPX Ograve Atilde -20
+KPX Ograve T -40
+KPX Ograve Tcaron -40
+KPX Ograve Tcommaaccent -40
+KPX Ograve V -50
+KPX Ograve W -30
+KPX Ograve X -60
+KPX Ograve Y -70
+KPX Ograve Yacute -70
+KPX Ograve Ydieresis -70
+KPX Ograve comma -40
+KPX Ograve period -40
+KPX Ohungarumlaut A -20
+KPX Ohungarumlaut Aacute -20
+KPX Ohungarumlaut Abreve -20
+KPX Ohungarumlaut Acircumflex -20
+KPX Ohungarumlaut Adieresis -20
+KPX Ohungarumlaut Agrave -20
+KPX Ohungarumlaut Amacron -20
+KPX Ohungarumlaut Aogonek -20
+KPX Ohungarumlaut Aring -20
+KPX Ohungarumlaut Atilde -20
+KPX Ohungarumlaut T -40
+KPX Ohungarumlaut Tcaron -40
+KPX Ohungarumlaut Tcommaaccent -40
+KPX Ohungarumlaut V -50
+KPX Ohungarumlaut W -30
+KPX Ohungarumlaut X -60
+KPX Ohungarumlaut Y -70
+KPX Ohungarumlaut Yacute -70
+KPX Ohungarumlaut Ydieresis -70
+KPX Ohungarumlaut comma -40
+KPX Ohungarumlaut period -40
+KPX Omacron A -20
+KPX Omacron Aacute -20
+KPX Omacron Abreve -20
+KPX Omacron Acircumflex -20
+KPX Omacron Adieresis -20
+KPX Omacron Agrave -20
+KPX Omacron Amacron -20
+KPX Omacron Aogonek -20
+KPX Omacron Aring -20
+KPX Omacron Atilde -20
+KPX Omacron T -40
+KPX Omacron Tcaron -40
+KPX Omacron Tcommaaccent -40
+KPX Omacron V -50
+KPX Omacron W -30
+KPX Omacron X -60
+KPX Omacron Y -70
+KPX Omacron Yacute -70
+KPX Omacron Ydieresis -70
+KPX Omacron comma -40
+KPX Omacron period -40
+KPX Oslash A -20
+KPX Oslash Aacute -20
+KPX Oslash Abreve -20
+KPX Oslash Acircumflex -20
+KPX Oslash Adieresis -20
+KPX Oslash Agrave -20
+KPX Oslash Amacron -20
+KPX Oslash Aogonek -20
+KPX Oslash Aring -20
+KPX Oslash Atilde -20
+KPX Oslash T -40
+KPX Oslash Tcaron -40
+KPX Oslash Tcommaaccent -40
+KPX Oslash V -50
+KPX Oslash W -30
+KPX Oslash X -60
+KPX Oslash Y -70
+KPX Oslash Yacute -70
+KPX Oslash Ydieresis -70
+KPX Oslash comma -40
+KPX Oslash period -40
+KPX Otilde A -20
+KPX Otilde Aacute -20
+KPX Otilde Abreve -20
+KPX Otilde Acircumflex -20
+KPX Otilde Adieresis -20
+KPX Otilde Agrave -20
+KPX Otilde Amacron -20
+KPX Otilde Aogonek -20
+KPX Otilde Aring -20
+KPX Otilde Atilde -20
+KPX Otilde T -40
+KPX Otilde Tcaron -40
+KPX Otilde Tcommaaccent -40
+KPX Otilde V -50
+KPX Otilde W -30
+KPX Otilde X -60
+KPX Otilde Y -70
+KPX Otilde Yacute -70
+KPX Otilde Ydieresis -70
+KPX Otilde comma -40
+KPX Otilde period -40
+KPX P A -120
+KPX P Aacute -120
+KPX P Abreve -120
+KPX P Acircumflex -120
+KPX P Adieresis -120
+KPX P Agrave -120
+KPX P Amacron -120
+KPX P Aogonek -120
+KPX P Aring -120
+KPX P Atilde -120
+KPX P a -40
+KPX P aacute -40
+KPX P abreve -40
+KPX P acircumflex -40
+KPX P adieresis -40
+KPX P agrave -40
+KPX P amacron -40
+KPX P aogonek -40
+KPX P aring -40
+KPX P atilde -40
+KPX P comma -180
+KPX P e -50
+KPX P eacute -50
+KPX P ecaron -50
+KPX P ecircumflex -50
+KPX P edieresis -50
+KPX P edotaccent -50
+KPX P egrave -50
+KPX P emacron -50
+KPX P eogonek -50
+KPX P o -50
+KPX P oacute -50
+KPX P ocircumflex -50
+KPX P odieresis -50
+KPX P ograve -50
+KPX P ohungarumlaut -50
+KPX P omacron -50
+KPX P oslash -50
+KPX P otilde -50
+KPX P period -180
+KPX Q U -10
+KPX Q Uacute -10
+KPX Q Ucircumflex -10
+KPX Q Udieresis -10
+KPX Q Ugrave -10
+KPX Q Uhungarumlaut -10
+KPX Q Umacron -10
+KPX Q Uogonek -10
+KPX Q Uring -10
+KPX R O -20
+KPX R Oacute -20
+KPX R Ocircumflex -20
+KPX R Odieresis -20
+KPX R Ograve -20
+KPX R Ohungarumlaut -20
+KPX R Omacron -20
+KPX R Oslash -20
+KPX R Otilde -20
+KPX R T -30
+KPX R Tcaron -30
+KPX R Tcommaaccent -30
+KPX R U -40
+KPX R Uacute -40
+KPX R Ucircumflex -40
+KPX R Udieresis -40
+KPX R Ugrave -40
+KPX R Uhungarumlaut -40
+KPX R Umacron -40
+KPX R Uogonek -40
+KPX R Uring -40
+KPX R V -50
+KPX R W -30
+KPX R Y -50
+KPX R Yacute -50
+KPX R Ydieresis -50
+KPX Racute O -20
+KPX Racute Oacute -20
+KPX Racute Ocircumflex -20
+KPX Racute Odieresis -20
+KPX Racute Ograve -20
+KPX Racute Ohungarumlaut -20
+KPX Racute Omacron -20
+KPX Racute Oslash -20
+KPX Racute Otilde -20
+KPX Racute T -30
+KPX Racute Tcaron -30
+KPX Racute Tcommaaccent -30
+KPX Racute U -40
+KPX Racute Uacute -40
+KPX Racute Ucircumflex -40
+KPX Racute Udieresis -40
+KPX Racute Ugrave -40
+KPX Racute Uhungarumlaut -40
+KPX Racute Umacron -40
+KPX Racute Uogonek -40
+KPX Racute Uring -40
+KPX Racute V -50
+KPX Racute W -30
+KPX Racute Y -50
+KPX Racute Yacute -50
+KPX Racute Ydieresis -50
+KPX Rcaron O -20
+KPX Rcaron Oacute -20
+KPX Rcaron Ocircumflex -20
+KPX Rcaron Odieresis -20
+KPX Rcaron Ograve -20
+KPX Rcaron Ohungarumlaut -20
+KPX Rcaron Omacron -20
+KPX Rcaron Oslash -20
+KPX Rcaron Otilde -20
+KPX Rcaron T -30
+KPX Rcaron Tcaron -30
+KPX Rcaron Tcommaaccent -30
+KPX Rcaron U -40
+KPX Rcaron Uacute -40
+KPX Rcaron Ucircumflex -40
+KPX Rcaron Udieresis -40
+KPX Rcaron Ugrave -40
+KPX Rcaron Uhungarumlaut -40
+KPX Rcaron Umacron -40
+KPX Rcaron Uogonek -40
+KPX Rcaron Uring -40
+KPX Rcaron V -50
+KPX Rcaron W -30
+KPX Rcaron Y -50
+KPX Rcaron Yacute -50
+KPX Rcaron Ydieresis -50
+KPX Rcommaaccent O -20
+KPX Rcommaaccent Oacute -20
+KPX Rcommaaccent Ocircumflex -20
+KPX Rcommaaccent Odieresis -20
+KPX Rcommaaccent Ograve -20
+KPX Rcommaaccent Ohungarumlaut -20
+KPX Rcommaaccent Omacron -20
+KPX Rcommaaccent Oslash -20
+KPX Rcommaaccent Otilde -20
+KPX Rcommaaccent T -30
+KPX Rcommaaccent Tcaron -30
+KPX Rcommaaccent Tcommaaccent -30
+KPX Rcommaaccent U -40
+KPX Rcommaaccent Uacute -40
+KPX Rcommaaccent Ucircumflex -40
+KPX Rcommaaccent Udieresis -40
+KPX Rcommaaccent Ugrave -40
+KPX Rcommaaccent Uhungarumlaut -40
+KPX Rcommaaccent Umacron -40
+KPX Rcommaaccent Uogonek -40
+KPX Rcommaaccent Uring -40
+KPX Rcommaaccent V -50
+KPX Rcommaaccent W -30
+KPX Rcommaaccent Y -50
+KPX Rcommaaccent Yacute -50
+KPX Rcommaaccent Ydieresis -50
+KPX S comma -20
+KPX S period -20
+KPX Sacute comma -20
+KPX Sacute period -20
+KPX Scaron comma -20
+KPX Scaron period -20
+KPX Scedilla comma -20
+KPX Scedilla period -20
+KPX Scommaaccent comma -20
+KPX Scommaaccent period -20
+KPX T A -120
+KPX T Aacute -120
+KPX T Abreve -120
+KPX T Acircumflex -120
+KPX T Adieresis -120
+KPX T Agrave -120
+KPX T Amacron -120
+KPX T Aogonek -120
+KPX T Aring -120
+KPX T Atilde -120
+KPX T O -40
+KPX T Oacute -40
+KPX T Ocircumflex -40
+KPX T Odieresis -40
+KPX T Ograve -40
+KPX T Ohungarumlaut -40
+KPX T Omacron -40
+KPX T Oslash -40
+KPX T Otilde -40
+KPX T a -120
+KPX T aacute -120
+KPX T abreve -60
+KPX T acircumflex -120
+KPX T adieresis -120
+KPX T agrave -120
+KPX T amacron -60
+KPX T aogonek -120
+KPX T aring -120
+KPX T atilde -60
+KPX T colon -20
+KPX T comma -120
+KPX T e -120
+KPX T eacute -120
+KPX T ecaron -120
+KPX T ecircumflex -120
+KPX T edieresis -120
+KPX T edotaccent -120
+KPX T egrave -60
+KPX T emacron -60
+KPX T eogonek -120
+KPX T hyphen -140
+KPX T o -120
+KPX T oacute -120
+KPX T ocircumflex -120
+KPX T odieresis -120
+KPX T ograve -120
+KPX T ohungarumlaut -120
+KPX T omacron -60
+KPX T oslash -120
+KPX T otilde -60
+KPX T period -120
+KPX T r -120
+KPX T racute -120
+KPX T rcaron -120
+KPX T rcommaaccent -120
+KPX T semicolon -20
+KPX T u -120
+KPX T uacute -120
+KPX T ucircumflex -120
+KPX T udieresis -120
+KPX T ugrave -120
+KPX T uhungarumlaut -120
+KPX T umacron -60
+KPX T uogonek -120
+KPX T uring -120
+KPX T w -120
+KPX T y -120
+KPX T yacute -120
+KPX T ydieresis -60
+KPX Tcaron A -120
+KPX Tcaron Aacute -120
+KPX Tcaron Abreve -120
+KPX Tcaron Acircumflex -120
+KPX Tcaron Adieresis -120
+KPX Tcaron Agrave -120
+KPX Tcaron Amacron -120
+KPX Tcaron Aogonek -120
+KPX Tcaron Aring -120
+KPX Tcaron Atilde -120
+KPX Tcaron O -40
+KPX Tcaron Oacute -40
+KPX Tcaron Ocircumflex -40
+KPX Tcaron Odieresis -40
+KPX Tcaron Ograve -40
+KPX Tcaron Ohungarumlaut -40
+KPX Tcaron Omacron -40
+KPX Tcaron Oslash -40
+KPX Tcaron Otilde -40
+KPX Tcaron a -120
+KPX Tcaron aacute -120
+KPX Tcaron abreve -60
+KPX Tcaron acircumflex -120
+KPX Tcaron adieresis -120
+KPX Tcaron agrave -120
+KPX Tcaron amacron -60
+KPX Tcaron aogonek -120
+KPX Tcaron aring -120
+KPX Tcaron atilde -60
+KPX Tcaron colon -20
+KPX Tcaron comma -120
+KPX Tcaron e -120
+KPX Tcaron eacute -120
+KPX Tcaron ecaron -120
+KPX Tcaron ecircumflex -120
+KPX Tcaron edieresis -120
+KPX Tcaron edotaccent -120
+KPX Tcaron egrave -60
+KPX Tcaron emacron -60
+KPX Tcaron eogonek -120
+KPX Tcaron hyphen -140
+KPX Tcaron o -120
+KPX Tcaron oacute -120
+KPX Tcaron ocircumflex -120
+KPX Tcaron odieresis -120
+KPX Tcaron ograve -120
+KPX Tcaron ohungarumlaut -120
+KPX Tcaron omacron -60
+KPX Tcaron oslash -120
+KPX Tcaron otilde -60
+KPX Tcaron period -120
+KPX Tcaron r -120
+KPX Tcaron racute -120
+KPX Tcaron rcaron -120
+KPX Tcaron rcommaaccent -120
+KPX Tcaron semicolon -20
+KPX Tcaron u -120
+KPX Tcaron uacute -120
+KPX Tcaron ucircumflex -120
+KPX Tcaron udieresis -120
+KPX Tcaron ugrave -120
+KPX Tcaron uhungarumlaut -120
+KPX Tcaron umacron -60
+KPX Tcaron uogonek -120
+KPX Tcaron uring -120
+KPX Tcaron w -120
+KPX Tcaron y -120
+KPX Tcaron yacute -120
+KPX Tcaron ydieresis -60
+KPX Tcommaaccent A -120
+KPX Tcommaaccent Aacute -120
+KPX Tcommaaccent Abreve -120
+KPX Tcommaaccent Acircumflex -120
+KPX Tcommaaccent Adieresis -120
+KPX Tcommaaccent Agrave -120
+KPX Tcommaaccent Amacron -120
+KPX Tcommaaccent Aogonek -120
+KPX Tcommaaccent Aring -120
+KPX Tcommaaccent Atilde -120
+KPX Tcommaaccent O -40
+KPX Tcommaaccent Oacute -40
+KPX Tcommaaccent Ocircumflex -40
+KPX Tcommaaccent Odieresis -40
+KPX Tcommaaccent Ograve -40
+KPX Tcommaaccent Ohungarumlaut -40
+KPX Tcommaaccent Omacron -40
+KPX Tcommaaccent Oslash -40
+KPX Tcommaaccent Otilde -40
+KPX Tcommaaccent a -120
+KPX Tcommaaccent aacute -120
+KPX Tcommaaccent abreve -60
+KPX Tcommaaccent acircumflex -120
+KPX Tcommaaccent adieresis -120
+KPX Tcommaaccent agrave -120
+KPX Tcommaaccent amacron -60
+KPX Tcommaaccent aogonek -120
+KPX Tcommaaccent aring -120
+KPX Tcommaaccent atilde -60
+KPX Tcommaaccent colon -20
+KPX Tcommaaccent comma -120
+KPX Tcommaaccent e -120
+KPX Tcommaaccent eacute -120
+KPX Tcommaaccent ecaron -120
+KPX Tcommaaccent ecircumflex -120
+KPX Tcommaaccent edieresis -120
+KPX Tcommaaccent edotaccent -120
+KPX Tcommaaccent egrave -60
+KPX Tcommaaccent emacron -60
+KPX Tcommaaccent eogonek -120
+KPX Tcommaaccent hyphen -140
+KPX Tcommaaccent o -120
+KPX Tcommaaccent oacute -120
+KPX Tcommaaccent ocircumflex -120
+KPX Tcommaaccent odieresis -120
+KPX Tcommaaccent ograve -120
+KPX Tcommaaccent ohungarumlaut -120
+KPX Tcommaaccent omacron -60
+KPX Tcommaaccent oslash -120
+KPX Tcommaaccent otilde -60
+KPX Tcommaaccent period -120
+KPX Tcommaaccent r -120
+KPX Tcommaaccent racute -120
+KPX Tcommaaccent rcaron -120
+KPX Tcommaaccent rcommaaccent -120
+KPX Tcommaaccent semicolon -20
+KPX Tcommaaccent u -120
+KPX Tcommaaccent uacute -120
+KPX Tcommaaccent ucircumflex -120
+KPX Tcommaaccent udieresis -120
+KPX Tcommaaccent ugrave -120
+KPX Tcommaaccent uhungarumlaut -120
+KPX Tcommaaccent umacron -60
+KPX Tcommaaccent uogonek -120
+KPX Tcommaaccent uring -120
+KPX Tcommaaccent w -120
+KPX Tcommaaccent y -120
+KPX Tcommaaccent yacute -120
+KPX Tcommaaccent ydieresis -60
+KPX U A -40
+KPX U Aacute -40
+KPX U Abreve -40
+KPX U Acircumflex -40
+KPX U Adieresis -40
+KPX U Agrave -40
+KPX U Amacron -40
+KPX U Aogonek -40
+KPX U Aring -40
+KPX U Atilde -40
+KPX U comma -40
+KPX U period -40
+KPX Uacute A -40
+KPX Uacute Aacute -40
+KPX Uacute Abreve -40
+KPX Uacute Acircumflex -40
+KPX Uacute Adieresis -40
+KPX Uacute Agrave -40
+KPX Uacute Amacron -40
+KPX Uacute Aogonek -40
+KPX Uacute Aring -40
+KPX Uacute Atilde -40
+KPX Uacute comma -40
+KPX Uacute period -40
+KPX Ucircumflex A -40
+KPX Ucircumflex Aacute -40
+KPX Ucircumflex Abreve -40
+KPX Ucircumflex Acircumflex -40
+KPX Ucircumflex Adieresis -40
+KPX Ucircumflex Agrave -40
+KPX Ucircumflex Amacron -40
+KPX Ucircumflex Aogonek -40
+KPX Ucircumflex Aring -40
+KPX Ucircumflex Atilde -40
+KPX Ucircumflex comma -40
+KPX Ucircumflex period -40
+KPX Udieresis A -40
+KPX Udieresis Aacute -40
+KPX Udieresis Abreve -40
+KPX Udieresis Acircumflex -40
+KPX Udieresis Adieresis -40
+KPX Udieresis Agrave -40
+KPX Udieresis Amacron -40
+KPX Udieresis Aogonek -40
+KPX Udieresis Aring -40
+KPX Udieresis Atilde -40
+KPX Udieresis comma -40
+KPX Udieresis period -40
+KPX Ugrave A -40
+KPX Ugrave Aacute -40
+KPX Ugrave Abreve -40
+KPX Ugrave Acircumflex -40
+KPX Ugrave Adieresis -40
+KPX Ugrave Agrave -40
+KPX Ugrave Amacron -40
+KPX Ugrave Aogonek -40
+KPX Ugrave Aring -40
+KPX Ugrave Atilde -40
+KPX Ugrave comma -40
+KPX Ugrave period -40
+KPX Uhungarumlaut A -40
+KPX Uhungarumlaut Aacute -40
+KPX Uhungarumlaut Abreve -40
+KPX Uhungarumlaut Acircumflex -40
+KPX Uhungarumlaut Adieresis -40
+KPX Uhungarumlaut Agrave -40
+KPX Uhungarumlaut Amacron -40
+KPX Uhungarumlaut Aogonek -40
+KPX Uhungarumlaut Aring -40
+KPX Uhungarumlaut Atilde -40
+KPX Uhungarumlaut comma -40
+KPX Uhungarumlaut period -40
+KPX Umacron A -40
+KPX Umacron Aacute -40
+KPX Umacron Abreve -40
+KPX Umacron Acircumflex -40
+KPX Umacron Adieresis -40
+KPX Umacron Agrave -40
+KPX Umacron Amacron -40
+KPX Umacron Aogonek -40
+KPX Umacron Aring -40
+KPX Umacron Atilde -40
+KPX Umacron comma -40
+KPX Umacron period -40
+KPX Uogonek A -40
+KPX Uogonek Aacute -40
+KPX Uogonek Abreve -40
+KPX Uogonek Acircumflex -40
+KPX Uogonek Adieresis -40
+KPX Uogonek Agrave -40
+KPX Uogonek Amacron -40
+KPX Uogonek Aogonek -40
+KPX Uogonek Aring -40
+KPX Uogonek Atilde -40
+KPX Uogonek comma -40
+KPX Uogonek period -40
+KPX Uring A -40
+KPX Uring Aacute -40
+KPX Uring Abreve -40
+KPX Uring Acircumflex -40
+KPX Uring Adieresis -40
+KPX Uring Agrave -40
+KPX Uring Amacron -40
+KPX Uring Aogonek -40
+KPX Uring Aring -40
+KPX Uring Atilde -40
+KPX Uring comma -40
+KPX Uring period -40
+KPX V A -80
+KPX V Aacute -80
+KPX V Abreve -80
+KPX V Acircumflex -80
+KPX V Adieresis -80
+KPX V Agrave -80
+KPX V Amacron -80
+KPX V Aogonek -80
+KPX V Aring -80
+KPX V Atilde -80
+KPX V G -40
+KPX V Gbreve -40
+KPX V Gcommaaccent -40
+KPX V O -40
+KPX V Oacute -40
+KPX V Ocircumflex -40
+KPX V Odieresis -40
+KPX V Ograve -40
+KPX V Ohungarumlaut -40
+KPX V Omacron -40
+KPX V Oslash -40
+KPX V Otilde -40
+KPX V a -70
+KPX V aacute -70
+KPX V abreve -70
+KPX V acircumflex -70
+KPX V adieresis -70
+KPX V agrave -70
+KPX V amacron -70
+KPX V aogonek -70
+KPX V aring -70
+KPX V atilde -70
+KPX V colon -40
+KPX V comma -125
+KPX V e -80
+KPX V eacute -80
+KPX V ecaron -80
+KPX V ecircumflex -80
+KPX V edieresis -80
+KPX V edotaccent -80
+KPX V egrave -80
+KPX V emacron -80
+KPX V eogonek -80
+KPX V hyphen -80
+KPX V o -80
+KPX V oacute -80
+KPX V ocircumflex -80
+KPX V odieresis -80
+KPX V ograve -80
+KPX V ohungarumlaut -80
+KPX V omacron -80
+KPX V oslash -80
+KPX V otilde -80
+KPX V period -125
+KPX V semicolon -40
+KPX V u -70
+KPX V uacute -70
+KPX V ucircumflex -70
+KPX V udieresis -70
+KPX V ugrave -70
+KPX V uhungarumlaut -70
+KPX V umacron -70
+KPX V uogonek -70
+KPX V uring -70
+KPX W A -50
+KPX W Aacute -50
+KPX W Abreve -50
+KPX W Acircumflex -50
+KPX W Adieresis -50
+KPX W Agrave -50
+KPX W Amacron -50
+KPX W Aogonek -50
+KPX W Aring -50
+KPX W Atilde -50
+KPX W O -20
+KPX W Oacute -20
+KPX W Ocircumflex -20
+KPX W Odieresis -20
+KPX W Ograve -20
+KPX W Ohungarumlaut -20
+KPX W Omacron -20
+KPX W Oslash -20
+KPX W Otilde -20
+KPX W a -40
+KPX W aacute -40
+KPX W abreve -40
+KPX W acircumflex -40
+KPX W adieresis -40
+KPX W agrave -40
+KPX W amacron -40
+KPX W aogonek -40
+KPX W aring -40
+KPX W atilde -40
+KPX W comma -80
+KPX W e -30
+KPX W eacute -30
+KPX W ecaron -30
+KPX W ecircumflex -30
+KPX W edieresis -30
+KPX W edotaccent -30
+KPX W egrave -30
+KPX W emacron -30
+KPX W eogonek -30
+KPX W hyphen -40
+KPX W o -30
+KPX W oacute -30
+KPX W ocircumflex -30
+KPX W odieresis -30
+KPX W ograve -30
+KPX W ohungarumlaut -30
+KPX W omacron -30
+KPX W oslash -30
+KPX W otilde -30
+KPX W period -80
+KPX W u -30
+KPX W uacute -30
+KPX W ucircumflex -30
+KPX W udieresis -30
+KPX W ugrave -30
+KPX W uhungarumlaut -30
+KPX W umacron -30
+KPX W uogonek -30
+KPX W uring -30
+KPX W y -20
+KPX W yacute -20
+KPX W ydieresis -20
+KPX Y A -110
+KPX Y Aacute -110
+KPX Y Abreve -110
+KPX Y Acircumflex -110
+KPX Y Adieresis -110
+KPX Y Agrave -110
+KPX Y Amacron -110
+KPX Y Aogonek -110
+KPX Y Aring -110
+KPX Y Atilde -110
+KPX Y O -85
+KPX Y Oacute -85
+KPX Y Ocircumflex -85
+KPX Y Odieresis -85
+KPX Y Ograve -85
+KPX Y Ohungarumlaut -85
+KPX Y Omacron -85
+KPX Y Oslash -85
+KPX Y Otilde -85
+KPX Y a -140
+KPX Y aacute -140
+KPX Y abreve -70
+KPX Y acircumflex -140
+KPX Y adieresis -140
+KPX Y agrave -140
+KPX Y amacron -70
+KPX Y aogonek -140
+KPX Y aring -140
+KPX Y atilde -140
+KPX Y colon -60
+KPX Y comma -140
+KPX Y e -140
+KPX Y eacute -140
+KPX Y ecaron -140
+KPX Y ecircumflex -140
+KPX Y edieresis -140
+KPX Y edotaccent -140
+KPX Y egrave -140
+KPX Y emacron -70
+KPX Y eogonek -140
+KPX Y hyphen -140
+KPX Y i -20
+KPX Y iacute -20
+KPX Y iogonek -20
+KPX Y o -140
+KPX Y oacute -140
+KPX Y ocircumflex -140
+KPX Y odieresis -140
+KPX Y ograve -140
+KPX Y ohungarumlaut -140
+KPX Y omacron -140
+KPX Y oslash -140
+KPX Y otilde -140
+KPX Y period -140
+KPX Y semicolon -60
+KPX Y u -110
+KPX Y uacute -110
+KPX Y ucircumflex -110
+KPX Y udieresis -110
+KPX Y ugrave -110
+KPX Y uhungarumlaut -110
+KPX Y umacron -110
+KPX Y uogonek -110
+KPX Y uring -110
+KPX Yacute A -110
+KPX Yacute Aacute -110
+KPX Yacute Abreve -110
+KPX Yacute Acircumflex -110
+KPX Yacute Adieresis -110
+KPX Yacute Agrave -110
+KPX Yacute Amacron -110
+KPX Yacute Aogonek -110
+KPX Yacute Aring -110
+KPX Yacute Atilde -110
+KPX Yacute O -85
+KPX Yacute Oacute -85
+KPX Yacute Ocircumflex -85
+KPX Yacute Odieresis -85
+KPX Yacute Ograve -85
+KPX Yacute Ohungarumlaut -85
+KPX Yacute Omacron -85
+KPX Yacute Oslash -85
+KPX Yacute Otilde -85
+KPX Yacute a -140
+KPX Yacute aacute -140
+KPX Yacute abreve -70
+KPX Yacute acircumflex -140
+KPX Yacute adieresis -140
+KPX Yacute agrave -140
+KPX Yacute amacron -70
+KPX Yacute aogonek -140
+KPX Yacute aring -140
+KPX Yacute atilde -70
+KPX Yacute colon -60
+KPX Yacute comma -140
+KPX Yacute e -140
+KPX Yacute eacute -140
+KPX Yacute ecaron -140
+KPX Yacute ecircumflex -140
+KPX Yacute edieresis -140
+KPX Yacute edotaccent -140
+KPX Yacute egrave -140
+KPX Yacute emacron -70
+KPX Yacute eogonek -140
+KPX Yacute hyphen -140
+KPX Yacute i -20
+KPX Yacute iacute -20
+KPX Yacute iogonek -20
+KPX Yacute o -140
+KPX Yacute oacute -140
+KPX Yacute ocircumflex -140
+KPX Yacute odieresis -140
+KPX Yacute ograve -140
+KPX Yacute ohungarumlaut -140
+KPX Yacute omacron -70
+KPX Yacute oslash -140
+KPX Yacute otilde -140
+KPX Yacute period -140
+KPX Yacute semicolon -60
+KPX Yacute u -110
+KPX Yacute uacute -110
+KPX Yacute ucircumflex -110
+KPX Yacute udieresis -110
+KPX Yacute ugrave -110
+KPX Yacute uhungarumlaut -110
+KPX Yacute umacron -110
+KPX Yacute uogonek -110
+KPX Yacute uring -110
+KPX Ydieresis A -110
+KPX Ydieresis Aacute -110
+KPX Ydieresis Abreve -110
+KPX Ydieresis Acircumflex -110
+KPX Ydieresis Adieresis -110
+KPX Ydieresis Agrave -110
+KPX Ydieresis Amacron -110
+KPX Ydieresis Aogonek -110
+KPX Ydieresis Aring -110
+KPX Ydieresis Atilde -110
+KPX Ydieresis O -85
+KPX Ydieresis Oacute -85
+KPX Ydieresis Ocircumflex -85
+KPX Ydieresis Odieresis -85
+KPX Ydieresis Ograve -85
+KPX Ydieresis Ohungarumlaut -85
+KPX Ydieresis Omacron -85
+KPX Ydieresis Oslash -85
+KPX Ydieresis Otilde -85
+KPX Ydieresis a -140
+KPX Ydieresis aacute -140
+KPX Ydieresis abreve -70
+KPX Ydieresis acircumflex -140
+KPX Ydieresis adieresis -140
+KPX Ydieresis agrave -140
+KPX Ydieresis amacron -70
+KPX Ydieresis aogonek -140
+KPX Ydieresis aring -140
+KPX Ydieresis atilde -70
+KPX Ydieresis colon -60
+KPX Ydieresis comma -140
+KPX Ydieresis e -140
+KPX Ydieresis eacute -140
+KPX Ydieresis ecaron -140
+KPX Ydieresis ecircumflex -140
+KPX Ydieresis edieresis -140
+KPX Ydieresis edotaccent -140
+KPX Ydieresis egrave -140
+KPX Ydieresis emacron -70
+KPX Ydieresis eogonek -140
+KPX Ydieresis hyphen -140
+KPX Ydieresis i -20
+KPX Ydieresis iacute -20
+KPX Ydieresis iogonek -20
+KPX Ydieresis o -140
+KPX Ydieresis oacute -140
+KPX Ydieresis ocircumflex -140
+KPX Ydieresis odieresis -140
+KPX Ydieresis ograve -140
+KPX Ydieresis ohungarumlaut -140
+KPX Ydieresis omacron -140
+KPX Ydieresis oslash -140
+KPX Ydieresis otilde -140
+KPX Ydieresis period -140
+KPX Ydieresis semicolon -60
+KPX Ydieresis u -110
+KPX Ydieresis uacute -110
+KPX Ydieresis ucircumflex -110
+KPX Ydieresis udieresis -110
+KPX Ydieresis ugrave -110
+KPX Ydieresis uhungarumlaut -110
+KPX Ydieresis umacron -110
+KPX Ydieresis uogonek -110
+KPX Ydieresis uring -110
+KPX a v -20
+KPX a w -20
+KPX a y -30
+KPX a yacute -30
+KPX a ydieresis -30
+KPX aacute v -20
+KPX aacute w -20
+KPX aacute y -30
+KPX aacute yacute -30
+KPX aacute ydieresis -30
+KPX abreve v -20
+KPX abreve w -20
+KPX abreve y -30
+KPX abreve yacute -30
+KPX abreve ydieresis -30
+KPX acircumflex v -20
+KPX acircumflex w -20
+KPX acircumflex y -30
+KPX acircumflex yacute -30
+KPX acircumflex ydieresis -30
+KPX adieresis v -20
+KPX adieresis w -20
+KPX adieresis y -30
+KPX adieresis yacute -30
+KPX adieresis ydieresis -30
+KPX agrave v -20
+KPX agrave w -20
+KPX agrave y -30
+KPX agrave yacute -30
+KPX agrave ydieresis -30
+KPX amacron v -20
+KPX amacron w -20
+KPX amacron y -30
+KPX amacron yacute -30
+KPX amacron ydieresis -30
+KPX aogonek v -20
+KPX aogonek w -20
+KPX aogonek y -30
+KPX aogonek yacute -30
+KPX aogonek ydieresis -30
+KPX aring v -20
+KPX aring w -20
+KPX aring y -30
+KPX aring yacute -30
+KPX aring ydieresis -30
+KPX atilde v -20
+KPX atilde w -20
+KPX atilde y -30
+KPX atilde yacute -30
+KPX atilde ydieresis -30
+KPX b b -10
+KPX b comma -40
+KPX b l -20
+KPX b lacute -20
+KPX b lcommaaccent -20
+KPX b lslash -20
+KPX b period -40
+KPX b u -20
+KPX b uacute -20
+KPX b ucircumflex -20
+KPX b udieresis -20
+KPX b ugrave -20
+KPX b uhungarumlaut -20
+KPX b umacron -20
+KPX b uogonek -20
+KPX b uring -20
+KPX b v -20
+KPX b y -20
+KPX b yacute -20
+KPX b ydieresis -20
+KPX c comma -15
+KPX c k -20
+KPX c kcommaaccent -20
+KPX cacute comma -15
+KPX cacute k -20
+KPX cacute kcommaaccent -20
+KPX ccaron comma -15
+KPX ccaron k -20
+KPX ccaron kcommaaccent -20
+KPX ccedilla comma -15
+KPX ccedilla k -20
+KPX ccedilla kcommaaccent -20
+KPX colon space -50
+KPX comma quotedblright -100
+KPX comma quoteright -100
+KPX e comma -15
+KPX e period -15
+KPX e v -30
+KPX e w -20
+KPX e x -30
+KPX e y -20
+KPX e yacute -20
+KPX e ydieresis -20
+KPX eacute comma -15
+KPX eacute period -15
+KPX eacute v -30
+KPX eacute w -20
+KPX eacute x -30
+KPX eacute y -20
+KPX eacute yacute -20
+KPX eacute ydieresis -20
+KPX ecaron comma -15
+KPX ecaron period -15
+KPX ecaron v -30
+KPX ecaron w -20
+KPX ecaron x -30
+KPX ecaron y -20
+KPX ecaron yacute -20
+KPX ecaron ydieresis -20
+KPX ecircumflex comma -15
+KPX ecircumflex period -15
+KPX ecircumflex v -30
+KPX ecircumflex w -20
+KPX ecircumflex x -30
+KPX ecircumflex y -20
+KPX ecircumflex yacute -20
+KPX ecircumflex ydieresis -20
+KPX edieresis comma -15
+KPX edieresis period -15
+KPX edieresis v -30
+KPX edieresis w -20
+KPX edieresis x -30
+KPX edieresis y -20
+KPX edieresis yacute -20
+KPX edieresis ydieresis -20
+KPX edotaccent comma -15
+KPX edotaccent period -15
+KPX edotaccent v -30
+KPX edotaccent w -20
+KPX edotaccent x -30
+KPX edotaccent y -20
+KPX edotaccent yacute -20
+KPX edotaccent ydieresis -20
+KPX egrave comma -15
+KPX egrave period -15
+KPX egrave v -30
+KPX egrave w -20
+KPX egrave x -30
+KPX egrave y -20
+KPX egrave yacute -20
+KPX egrave ydieresis -20
+KPX emacron comma -15
+KPX emacron period -15
+KPX emacron v -30
+KPX emacron w -20
+KPX emacron x -30
+KPX emacron y -20
+KPX emacron yacute -20
+KPX emacron ydieresis -20
+KPX eogonek comma -15
+KPX eogonek period -15
+KPX eogonek v -30
+KPX eogonek w -20
+KPX eogonek x -30
+KPX eogonek y -20
+KPX eogonek yacute -20
+KPX eogonek ydieresis -20
+KPX f a -30
+KPX f aacute -30
+KPX f abreve -30
+KPX f acircumflex -30
+KPX f adieresis -30
+KPX f agrave -30
+KPX f amacron -30
+KPX f aogonek -30
+KPX f aring -30
+KPX f atilde -30
+KPX f comma -30
+KPX f dotlessi -28
+KPX f e -30
+KPX f eacute -30
+KPX f ecaron -30
+KPX f ecircumflex -30
+KPX f edieresis -30
+KPX f edotaccent -30
+KPX f egrave -30
+KPX f emacron -30
+KPX f eogonek -30
+KPX f o -30
+KPX f oacute -30
+KPX f ocircumflex -30
+KPX f odieresis -30
+KPX f ograve -30
+KPX f ohungarumlaut -30
+KPX f omacron -30
+KPX f oslash -30
+KPX f otilde -30
+KPX f period -30
+KPX f quotedblright 60
+KPX f quoteright 50
+KPX g r -10
+KPX g racute -10
+KPX g rcaron -10
+KPX g rcommaaccent -10
+KPX gbreve r -10
+KPX gbreve racute -10
+KPX gbreve rcaron -10
+KPX gbreve rcommaaccent -10
+KPX gcommaaccent r -10
+KPX gcommaaccent racute -10
+KPX gcommaaccent rcaron -10
+KPX gcommaaccent rcommaaccent -10
+KPX h y -30
+KPX h yacute -30
+KPX h ydieresis -30
+KPX k e -20
+KPX k eacute -20
+KPX k ecaron -20
+KPX k ecircumflex -20
+KPX k edieresis -20
+KPX k edotaccent -20
+KPX k egrave -20
+KPX k emacron -20
+KPX k eogonek -20
+KPX k o -20
+KPX k oacute -20
+KPX k ocircumflex -20
+KPX k odieresis -20
+KPX k ograve -20
+KPX k ohungarumlaut -20
+KPX k omacron -20
+KPX k oslash -20
+KPX k otilde -20
+KPX kcommaaccent e -20
+KPX kcommaaccent eacute -20
+KPX kcommaaccent ecaron -20
+KPX kcommaaccent ecircumflex -20
+KPX kcommaaccent edieresis -20
+KPX kcommaaccent edotaccent -20
+KPX kcommaaccent egrave -20
+KPX kcommaaccent emacron -20
+KPX kcommaaccent eogonek -20
+KPX kcommaaccent o -20
+KPX kcommaaccent oacute -20
+KPX kcommaaccent ocircumflex -20
+KPX kcommaaccent odieresis -20
+KPX kcommaaccent ograve -20
+KPX kcommaaccent ohungarumlaut -20
+KPX kcommaaccent omacron -20
+KPX kcommaaccent oslash -20
+KPX kcommaaccent otilde -20
+KPX m u -10
+KPX m uacute -10
+KPX m ucircumflex -10
+KPX m udieresis -10
+KPX m ugrave -10
+KPX m uhungarumlaut -10
+KPX m umacron -10
+KPX m uogonek -10
+KPX m uring -10
+KPX m y -15
+KPX m yacute -15
+KPX m ydieresis -15
+KPX n u -10
+KPX n uacute -10
+KPX n ucircumflex -10
+KPX n udieresis -10
+KPX n ugrave -10
+KPX n uhungarumlaut -10
+KPX n umacron -10
+KPX n uogonek -10
+KPX n uring -10
+KPX n v -20
+KPX n y -15
+KPX n yacute -15
+KPX n ydieresis -15
+KPX nacute u -10
+KPX nacute uacute -10
+KPX nacute ucircumflex -10
+KPX nacute udieresis -10
+KPX nacute ugrave -10
+KPX nacute uhungarumlaut -10
+KPX nacute umacron -10
+KPX nacute uogonek -10
+KPX nacute uring -10
+KPX nacute v -20
+KPX nacute y -15
+KPX nacute yacute -15
+KPX nacute ydieresis -15
+KPX ncaron u -10
+KPX ncaron uacute -10
+KPX ncaron ucircumflex -10
+KPX ncaron udieresis -10
+KPX ncaron ugrave -10
+KPX ncaron uhungarumlaut -10
+KPX ncaron umacron -10
+KPX ncaron uogonek -10
+KPX ncaron uring -10
+KPX ncaron v -20
+KPX ncaron y -15
+KPX ncaron yacute -15
+KPX ncaron ydieresis -15
+KPX ncommaaccent u -10
+KPX ncommaaccent uacute -10
+KPX ncommaaccent ucircumflex -10
+KPX ncommaaccent udieresis -10
+KPX ncommaaccent ugrave -10
+KPX ncommaaccent uhungarumlaut -10
+KPX ncommaaccent umacron -10
+KPX ncommaaccent uogonek -10
+KPX ncommaaccent uring -10
+KPX ncommaaccent v -20
+KPX ncommaaccent y -15
+KPX ncommaaccent yacute -15
+KPX ncommaaccent ydieresis -15
+KPX ntilde u -10
+KPX ntilde uacute -10
+KPX ntilde ucircumflex -10
+KPX ntilde udieresis -10
+KPX ntilde ugrave -10
+KPX ntilde uhungarumlaut -10
+KPX ntilde umacron -10
+KPX ntilde uogonek -10
+KPX ntilde uring -10
+KPX ntilde v -20
+KPX ntilde y -15
+KPX ntilde yacute -15
+KPX ntilde ydieresis -15
+KPX o comma -40
+KPX o period -40
+KPX o v -15
+KPX o w -15
+KPX o x -30
+KPX o y -30
+KPX o yacute -30
+KPX o ydieresis -30
+KPX oacute comma -40
+KPX oacute period -40
+KPX oacute v -15
+KPX oacute w -15
+KPX oacute x -30
+KPX oacute y -30
+KPX oacute yacute -30
+KPX oacute ydieresis -30
+KPX ocircumflex comma -40
+KPX ocircumflex period -40
+KPX ocircumflex v -15
+KPX ocircumflex w -15
+KPX ocircumflex x -30
+KPX ocircumflex y -30
+KPX ocircumflex yacute -30
+KPX ocircumflex ydieresis -30
+KPX odieresis comma -40
+KPX odieresis period -40
+KPX odieresis v -15
+KPX odieresis w -15
+KPX odieresis x -30
+KPX odieresis y -30
+KPX odieresis yacute -30
+KPX odieresis ydieresis -30
+KPX ograve comma -40
+KPX ograve period -40
+KPX ograve v -15
+KPX ograve w -15
+KPX ograve x -30
+KPX ograve y -30
+KPX ograve yacute -30
+KPX ograve ydieresis -30
+KPX ohungarumlaut comma -40
+KPX ohungarumlaut period -40
+KPX ohungarumlaut v -15
+KPX ohungarumlaut w -15
+KPX ohungarumlaut x -30
+KPX ohungarumlaut y -30
+KPX ohungarumlaut yacute -30
+KPX ohungarumlaut ydieresis -30
+KPX omacron comma -40
+KPX omacron period -40
+KPX omacron v -15
+KPX omacron w -15
+KPX omacron x -30
+KPX omacron y -30
+KPX omacron yacute -30
+KPX omacron ydieresis -30
+KPX oslash a -55
+KPX oslash aacute -55
+KPX oslash abreve -55
+KPX oslash acircumflex -55
+KPX oslash adieresis -55
+KPX oslash agrave -55
+KPX oslash amacron -55
+KPX oslash aogonek -55
+KPX oslash aring -55
+KPX oslash atilde -55
+KPX oslash b -55
+KPX oslash c -55
+KPX oslash cacute -55
+KPX oslash ccaron -55
+KPX oslash ccedilla -55
+KPX oslash comma -95
+KPX oslash d -55
+KPX oslash dcroat -55
+KPX oslash e -55
+KPX oslash eacute -55
+KPX oslash ecaron -55
+KPX oslash ecircumflex -55
+KPX oslash edieresis -55
+KPX oslash edotaccent -55
+KPX oslash egrave -55
+KPX oslash emacron -55
+KPX oslash eogonek -55
+KPX oslash f -55
+KPX oslash g -55
+KPX oslash gbreve -55
+KPX oslash gcommaaccent -55
+KPX oslash h -55
+KPX oslash i -55
+KPX oslash iacute -55
+KPX oslash icircumflex -55
+KPX oslash idieresis -55
+KPX oslash igrave -55
+KPX oslash imacron -55
+KPX oslash iogonek -55
+KPX oslash j -55
+KPX oslash k -55
+KPX oslash kcommaaccent -55
+KPX oslash l -55
+KPX oslash lacute -55
+KPX oslash lcommaaccent -55
+KPX oslash lslash -55
+KPX oslash m -55
+KPX oslash n -55
+KPX oslash nacute -55
+KPX oslash ncaron -55
+KPX oslash ncommaaccent -55
+KPX oslash ntilde -55
+KPX oslash o -55
+KPX oslash oacute -55
+KPX oslash ocircumflex -55
+KPX oslash odieresis -55
+KPX oslash ograve -55
+KPX oslash ohungarumlaut -55
+KPX oslash omacron -55
+KPX oslash oslash -55
+KPX oslash otilde -55
+KPX oslash p -55
+KPX oslash period -95
+KPX oslash q -55
+KPX oslash r -55
+KPX oslash racute -55
+KPX oslash rcaron -55
+KPX oslash rcommaaccent -55
+KPX oslash s -55
+KPX oslash sacute -55
+KPX oslash scaron -55
+KPX oslash scedilla -55
+KPX oslash scommaaccent -55
+KPX oslash t -55
+KPX oslash tcommaaccent -55
+KPX oslash u -55
+KPX oslash uacute -55
+KPX oslash ucircumflex -55
+KPX oslash udieresis -55
+KPX oslash ugrave -55
+KPX oslash uhungarumlaut -55
+KPX oslash umacron -55
+KPX oslash uogonek -55
+KPX oslash uring -55
+KPX oslash v -70
+KPX oslash w -70
+KPX oslash x -85
+KPX oslash y -70
+KPX oslash yacute -70
+KPX oslash ydieresis -70
+KPX oslash z -55
+KPX oslash zacute -55
+KPX oslash zcaron -55
+KPX oslash zdotaccent -55
+KPX otilde comma -40
+KPX otilde period -40
+KPX otilde v -15
+KPX otilde w -15
+KPX otilde x -30
+KPX otilde y -30
+KPX otilde yacute -30
+KPX otilde ydieresis -30
+KPX p comma -35
+KPX p period -35
+KPX p y -30
+KPX p yacute -30
+KPX p ydieresis -30
+KPX period quotedblright -100
+KPX period quoteright -100
+KPX period space -60
+KPX quotedblright space -40
+KPX quoteleft quoteleft -57
+KPX quoteright d -50
+KPX quoteright dcroat -50
+KPX quoteright quoteright -57
+KPX quoteright r -50
+KPX quoteright racute -50
+KPX quoteright rcaron -50
+KPX quoteright rcommaaccent -50
+KPX quoteright s -50
+KPX quoteright sacute -50
+KPX quoteright scaron -50
+KPX quoteright scedilla -50
+KPX quoteright scommaaccent -50
+KPX quoteright space -70
+KPX r a -10
+KPX r aacute -10
+KPX r abreve -10
+KPX r acircumflex -10
+KPX r adieresis -10
+KPX r agrave -10
+KPX r amacron -10
+KPX r aogonek -10
+KPX r aring -10
+KPX r atilde -10
+KPX r colon 30
+KPX r comma -50
+KPX r i 15
+KPX r iacute 15
+KPX r icircumflex 15
+KPX r idieresis 15
+KPX r igrave 15
+KPX r imacron 15
+KPX r iogonek 15
+KPX r k 15
+KPX r kcommaaccent 15
+KPX r l 15
+KPX r lacute 15
+KPX r lcommaaccent 15
+KPX r lslash 15
+KPX r m 25
+KPX r n 25
+KPX r nacute 25
+KPX r ncaron 25
+KPX r ncommaaccent 25
+KPX r ntilde 25
+KPX r p 30
+KPX r period -50
+KPX r semicolon 30
+KPX r t 40
+KPX r tcommaaccent 40
+KPX r u 15
+KPX r uacute 15
+KPX r ucircumflex 15
+KPX r udieresis 15
+KPX r ugrave 15
+KPX r uhungarumlaut 15
+KPX r umacron 15
+KPX r uogonek 15
+KPX r uring 15
+KPX r v 30
+KPX r y 30
+KPX r yacute 30
+KPX r ydieresis 30
+KPX racute a -10
+KPX racute aacute -10
+KPX racute abreve -10
+KPX racute acircumflex -10
+KPX racute adieresis -10
+KPX racute agrave -10
+KPX racute amacron -10
+KPX racute aogonek -10
+KPX racute aring -10
+KPX racute atilde -10
+KPX racute colon 30
+KPX racute comma -50
+KPX racute i 15
+KPX racute iacute 15
+KPX racute icircumflex 15
+KPX racute idieresis 15
+KPX racute igrave 15
+KPX racute imacron 15
+KPX racute iogonek 15
+KPX racute k 15
+KPX racute kcommaaccent 15
+KPX racute l 15
+KPX racute lacute 15
+KPX racute lcommaaccent 15
+KPX racute lslash 15
+KPX racute m 25
+KPX racute n 25
+KPX racute nacute 25
+KPX racute ncaron 25
+KPX racute ncommaaccent 25
+KPX racute ntilde 25
+KPX racute p 30
+KPX racute period -50
+KPX racute semicolon 30
+KPX racute t 40
+KPX racute tcommaaccent 40
+KPX racute u 15
+KPX racute uacute 15
+KPX racute ucircumflex 15
+KPX racute udieresis 15
+KPX racute ugrave 15
+KPX racute uhungarumlaut 15
+KPX racute umacron 15
+KPX racute uogonek 15
+KPX racute uring 15
+KPX racute v 30
+KPX racute y 30
+KPX racute yacute 30
+KPX racute ydieresis 30
+KPX rcaron a -10
+KPX rcaron aacute -10
+KPX rcaron abreve -10
+KPX rcaron acircumflex -10
+KPX rcaron adieresis -10
+KPX rcaron agrave -10
+KPX rcaron amacron -10
+KPX rcaron aogonek -10
+KPX rcaron aring -10
+KPX rcaron atilde -10
+KPX rcaron colon 30
+KPX rcaron comma -50
+KPX rcaron i 15
+KPX rcaron iacute 15
+KPX rcaron icircumflex 15
+KPX rcaron idieresis 15
+KPX rcaron igrave 15
+KPX rcaron imacron 15
+KPX rcaron iogonek 15
+KPX rcaron k 15
+KPX rcaron kcommaaccent 15
+KPX rcaron l 15
+KPX rcaron lacute 15
+KPX rcaron lcommaaccent 15
+KPX rcaron lslash 15
+KPX rcaron m 25
+KPX rcaron n 25
+KPX rcaron nacute 25
+KPX rcaron ncaron 25
+KPX rcaron ncommaaccent 25
+KPX rcaron ntilde 25
+KPX rcaron p 30
+KPX rcaron period -50
+KPX rcaron semicolon 30
+KPX rcaron t 40
+KPX rcaron tcommaaccent 40
+KPX rcaron u 15
+KPX rcaron uacute 15
+KPX rcaron ucircumflex 15
+KPX rcaron udieresis 15
+KPX rcaron ugrave 15
+KPX rcaron uhungarumlaut 15
+KPX rcaron umacron 15
+KPX rcaron uogonek 15
+KPX rcaron uring 15
+KPX rcaron v 30
+KPX rcaron y 30
+KPX rcaron yacute 30
+KPX rcaron ydieresis 30
+KPX rcommaaccent a -10
+KPX rcommaaccent aacute -10
+KPX rcommaaccent abreve -10
+KPX rcommaaccent acircumflex -10
+KPX rcommaaccent adieresis -10
+KPX rcommaaccent agrave -10
+KPX rcommaaccent amacron -10
+KPX rcommaaccent aogonek -10
+KPX rcommaaccent aring -10
+KPX rcommaaccent atilde -10
+KPX rcommaaccent colon 30
+KPX rcommaaccent comma -50
+KPX rcommaaccent i 15
+KPX rcommaaccent iacute 15
+KPX rcommaaccent icircumflex 15
+KPX rcommaaccent idieresis 15
+KPX rcommaaccent igrave 15
+KPX rcommaaccent imacron 15
+KPX rcommaaccent iogonek 15
+KPX rcommaaccent k 15
+KPX rcommaaccent kcommaaccent 15
+KPX rcommaaccent l 15
+KPX rcommaaccent lacute 15
+KPX rcommaaccent lcommaaccent 15
+KPX rcommaaccent lslash 15
+KPX rcommaaccent m 25
+KPX rcommaaccent n 25
+KPX rcommaaccent nacute 25
+KPX rcommaaccent ncaron 25
+KPX rcommaaccent ncommaaccent 25
+KPX rcommaaccent ntilde 25
+KPX rcommaaccent p 30
+KPX rcommaaccent period -50
+KPX rcommaaccent semicolon 30
+KPX rcommaaccent t 40
+KPX rcommaaccent tcommaaccent 40
+KPX rcommaaccent u 15
+KPX rcommaaccent uacute 15
+KPX rcommaaccent ucircumflex 15
+KPX rcommaaccent udieresis 15
+KPX rcommaaccent ugrave 15
+KPX rcommaaccent uhungarumlaut 15
+KPX rcommaaccent umacron 15
+KPX rcommaaccent uogonek 15
+KPX rcommaaccent uring 15
+KPX rcommaaccent v 30
+KPX rcommaaccent y 30
+KPX rcommaaccent yacute 30
+KPX rcommaaccent ydieresis 30
+KPX s comma -15
+KPX s period -15
+KPX s w -30
+KPX sacute comma -15
+KPX sacute period -15
+KPX sacute w -30
+KPX scaron comma -15
+KPX scaron period -15
+KPX scaron w -30
+KPX scedilla comma -15
+KPX scedilla period -15
+KPX scedilla w -30
+KPX scommaaccent comma -15
+KPX scommaaccent period -15
+KPX scommaaccent w -30
+KPX semicolon space -50
+KPX space T -50
+KPX space Tcaron -50
+KPX space Tcommaaccent -50
+KPX space V -50
+KPX space W -40
+KPX space Y -90
+KPX space Yacute -90
+KPX space Ydieresis -90
+KPX space quotedblleft -30
+KPX space quoteleft -60
+KPX v a -25
+KPX v aacute -25
+KPX v abreve -25
+KPX v acircumflex -25
+KPX v adieresis -25
+KPX v agrave -25
+KPX v amacron -25
+KPX v aogonek -25
+KPX v aring -25
+KPX v atilde -25
+KPX v comma -80
+KPX v e -25
+KPX v eacute -25
+KPX v ecaron -25
+KPX v ecircumflex -25
+KPX v edieresis -25
+KPX v edotaccent -25
+KPX v egrave -25
+KPX v emacron -25
+KPX v eogonek -25
+KPX v o -25
+KPX v oacute -25
+KPX v ocircumflex -25
+KPX v odieresis -25
+KPX v ograve -25
+KPX v ohungarumlaut -25
+KPX v omacron -25
+KPX v oslash -25
+KPX v otilde -25
+KPX v period -80
+KPX w a -15
+KPX w aacute -15
+KPX w abreve -15
+KPX w acircumflex -15
+KPX w adieresis -15
+KPX w agrave -15
+KPX w amacron -15
+KPX w aogonek -15
+KPX w aring -15
+KPX w atilde -15
+KPX w comma -60
+KPX w e -10
+KPX w eacute -10
+KPX w ecaron -10
+KPX w ecircumflex -10
+KPX w edieresis -10
+KPX w edotaccent -10
+KPX w egrave -10
+KPX w emacron -10
+KPX w eogonek -10
+KPX w o -10
+KPX w oacute -10
+KPX w ocircumflex -10
+KPX w odieresis -10
+KPX w ograve -10
+KPX w ohungarumlaut -10
+KPX w omacron -10
+KPX w oslash -10
+KPX w otilde -10
+KPX w period -60
+KPX x e -30
+KPX x eacute -30
+KPX x ecaron -30
+KPX x ecircumflex -30
+KPX x edieresis -30
+KPX x edotaccent -30
+KPX x egrave -30
+KPX x emacron -30
+KPX x eogonek -30
+KPX y a -20
+KPX y aacute -20
+KPX y abreve -20
+KPX y acircumflex -20
+KPX y adieresis -20
+KPX y agrave -20
+KPX y amacron -20
+KPX y aogonek -20
+KPX y aring -20
+KPX y atilde -20
+KPX y comma -100
+KPX y e -20
+KPX y eacute -20
+KPX y ecaron -20
+KPX y ecircumflex -20
+KPX y edieresis -20
+KPX y edotaccent -20
+KPX y egrave -20
+KPX y emacron -20
+KPX y eogonek -20
+KPX y o -20
+KPX y oacute -20
+KPX y ocircumflex -20
+KPX y odieresis -20
+KPX y ograve -20
+KPX y ohungarumlaut -20
+KPX y omacron -20
+KPX y oslash -20
+KPX y otilde -20
+KPX y period -100
+KPX yacute a -20
+KPX yacute aacute -20
+KPX yacute abreve -20
+KPX yacute acircumflex -20
+KPX yacute adieresis -20
+KPX yacute agrave -20
+KPX yacute amacron -20
+KPX yacute aogonek -20
+KPX yacute aring -20
+KPX yacute atilde -20
+KPX yacute comma -100
+KPX yacute e -20
+KPX yacute eacute -20
+KPX yacute ecaron -20
+KPX yacute ecircumflex -20
+KPX yacute edieresis -20
+KPX yacute edotaccent -20
+KPX yacute egrave -20
+KPX yacute emacron -20
+KPX yacute eogonek -20
+KPX yacute o -20
+KPX yacute oacute -20
+KPX yacute ocircumflex -20
+KPX yacute odieresis -20
+KPX yacute ograve -20
+KPX yacute ohungarumlaut -20
+KPX yacute omacron -20
+KPX yacute oslash -20
+KPX yacute otilde -20
+KPX yacute period -100
+KPX ydieresis a -20
+KPX ydieresis aacute -20
+KPX ydieresis abreve -20
+KPX ydieresis acircumflex -20
+KPX ydieresis adieresis -20
+KPX ydieresis agrave -20
+KPX ydieresis amacron -20
+KPX ydieresis aogonek -20
+KPX ydieresis aring -20
+KPX ydieresis atilde -20
+KPX ydieresis comma -100
+KPX ydieresis e -20
+KPX ydieresis eacute -20
+KPX ydieresis ecaron -20
+KPX ydieresis ecircumflex -20
+KPX ydieresis edieresis -20
+KPX ydieresis edotaccent -20
+KPX ydieresis egrave -20
+KPX ydieresis emacron -20
+KPX ydieresis eogonek -20
+KPX ydieresis o -20
+KPX ydieresis oacute -20
+KPX ydieresis ocircumflex -20
+KPX ydieresis odieresis -20
+KPX ydieresis ograve -20
+KPX ydieresis ohungarumlaut -20
+KPX ydieresis omacron -20
+KPX ydieresis oslash -20
+KPX ydieresis otilde -20
+KPX ydieresis period -100
+KPX z e -15
+KPX z eacute -15
+KPX z ecaron -15
+KPX z ecircumflex -15
+KPX z edieresis -15
+KPX z edotaccent -15
+KPX z egrave -15
+KPX z emacron -15
+KPX z eogonek -15
+KPX z o -15
+KPX z oacute -15
+KPX z ocircumflex -15
+KPX z odieresis -15
+KPX z ograve -15
+KPX z ohungarumlaut -15
+KPX z omacron -15
+KPX z oslash -15
+KPX z otilde -15
+KPX zacute e -15
+KPX zacute eacute -15
+KPX zacute ecaron -15
+KPX zacute ecircumflex -15
+KPX zacute edieresis -15
+KPX zacute edotaccent -15
+KPX zacute egrave -15
+KPX zacute emacron -15
+KPX zacute eogonek -15
+KPX zacute o -15
+KPX zacute oacute -15
+KPX zacute ocircumflex -15
+KPX zacute odieresis -15
+KPX zacute ograve -15
+KPX zacute ohungarumlaut -15
+KPX zacute omacron -15
+KPX zacute oslash -15
+KPX zacute otilde -15
+KPX zcaron e -15
+KPX zcaron eacute -15
+KPX zcaron ecaron -15
+KPX zcaron ecircumflex -15
+KPX zcaron edieresis -15
+KPX zcaron edotaccent -15
+KPX zcaron egrave -15
+KPX zcaron emacron -15
+KPX zcaron eogonek -15
+KPX zcaron o -15
+KPX zcaron oacute -15
+KPX zcaron ocircumflex -15
+KPX zcaron odieresis -15
+KPX zcaron ograve -15
+KPX zcaron ohungarumlaut -15
+KPX zcaron omacron -15
+KPX zcaron oslash -15
+KPX zcaron otilde -15
+KPX zdotaccent e -15
+KPX zdotaccent eacute -15
+KPX zdotaccent ecaron -15
+KPX zdotaccent ecircumflex -15
+KPX zdotaccent edieresis -15
+KPX zdotaccent edotaccent -15
+KPX zdotaccent egrave -15
+KPX zdotaccent emacron -15
+KPX zdotaccent eogonek -15
+KPX zdotaccent o -15
+KPX zdotaccent oacute -15
+KPX zdotaccent ocircumflex -15
+KPX zdotaccent odieresis -15
+KPX zdotaccent ograve -15
+KPX zdotaccent ohungarumlaut -15
+KPX zdotaccent omacron -15
+KPX zdotaccent oslash -15
+KPX zdotaccent otilde -15
+EndKernPairs
+EndKernData
+EndFontMetrics
diff --git a/program/libraries/afm/Makefile.am b/program/libraries/afm/Makefile.am
--- /dev/null
@@ -0,0 +1,6 @@
+EXTRA_DIST= COPYRIGHT.txt Courier-Bold.afm Courier-BoldOblique.afm \
+ Courier-Oblique.afm Courier.afm Helvetica-Bold.afm \
+ Helvetica-BoldOblique.afm Helvetica-Oblique.afm \
+ Helvetica.afm Symbol.afm Times-Bold.afm Times-BoldItalic.afm \
+ Times-Italic.afm Times-Roman.afm ZapfDingbats.afm \
+ compile_afm.pl glyphlist.txt test-afm.c
diff --git a/program/libraries/afm/Symbol.afm b/program/libraries/afm/Symbol.afm
--- /dev/null
@@ -0,0 +1,213 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All rights reserved.
+Comment Creation Date: Thu May 1 15:12:25 1997
+Comment UniqueID 43064
+Comment VMusage 30820 39997
+FontName Symbol
+FullName Symbol
+FamilyName Symbol
+Weight Medium
+ItalicAngle 0
+IsFixedPitch false
+CharacterSet Special
+FontBBox -180 -293 1090 1010
+UnderlinePosition -100
+UnderlineThickness 50
+Version 001.008
+Notice Copyright (c) 1985, 1987, 1989, 1990, 1997 Adobe Systems Incorporated. All rights reserved.
+EncodingScheme FontSpecific
+StdHW 92
+StdVW 85
+StartCharMetrics 190
+C 32 ; WX 250 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 333 ; N exclam ; B 128 -17 240 672 ;
+C 34 ; WX 713 ; N universal ; B 31 0 681 705 ;
+C 35 ; WX 500 ; N numbersign ; B 20 -16 481 673 ;
+C 36 ; WX 549 ; N existential ; B 25 0 478 707 ;
+C 37 ; WX 833 ; N percent ; B 63 -36 771 655 ;
+C 38 ; WX 778 ; N ampersand ; B 41 -18 750 661 ;
+C 39 ; WX 439 ; N suchthat ; B 48 -17 414 500 ;
+C 40 ; WX 333 ; N parenleft ; B 53 -191 300 673 ;
+C 41 ; WX 333 ; N parenright ; B 30 -191 277 673 ;
+C 42 ; WX 500 ; N asteriskmath ; B 65 134 427 551 ;
+C 43 ; WX 549 ; N plus ; B 10 0 539 533 ;
+C 44 ; WX 250 ; N comma ; B 56 -152 194 104 ;
+C 45 ; WX 549 ; N minus ; B 11 233 535 288 ;
+C 46 ; WX 250 ; N period ; B 69 -17 181 95 ;
+C 47 ; WX 278 ; N slash ; B 0 -18 254 646 ;
+C 48 ; WX 500 ; N zero ; B 24 -14 476 685 ;
+C 49 ; WX 500 ; N one ; B 117 0 390 673 ;
+C 50 ; WX 500 ; N two ; B 25 0 475 685 ;
+C 51 ; WX 500 ; N three ; B 43 -14 435 685 ;
+C 52 ; WX 500 ; N four ; B 15 0 469 685 ;
+C 53 ; WX 500 ; N five ; B 32 -14 445 690 ;
+C 54 ; WX 500 ; N six ; B 34 -14 468 685 ;
+C 55 ; WX 500 ; N seven ; B 24 -16 448 673 ;
+C 56 ; WX 500 ; N eight ; B 56 -14 445 685 ;
+C 57 ; WX 500 ; N nine ; B 30 -18 459 685 ;
+C 58 ; WX 278 ; N colon ; B 81 -17 193 460 ;
+C 59 ; WX 278 ; N semicolon ; B 83 -152 221 460 ;
+C 60 ; WX 549 ; N less ; B 26 0 523 522 ;
+C 61 ; WX 549 ; N equal ; B 11 141 537 390 ;
+C 62 ; WX 549 ; N greater ; B 26 0 523 522 ;
+C 63 ; WX 444 ; N question ; B 70 -17 412 686 ;
+C 64 ; WX 549 ; N congruent ; B 11 0 537 475 ;
+C 65 ; WX 722 ; N Alpha ; B 4 0 684 673 ;
+C 66 ; WX 667 ; N Beta ; B 29 0 592 673 ;
+C 67 ; WX 722 ; N Chi ; B -9 0 704 673 ;
+C 68 ; WX 612 ; N Delta ; B 6 0 608 688 ;
+C 69 ; WX 611 ; N Epsilon ; B 32 0 617 673 ;
+C 70 ; WX 763 ; N Phi ; B 26 0 741 673 ;
+C 71 ; WX 603 ; N Gamma ; B 24 0 609 673 ;
+C 72 ; WX 722 ; N Eta ; B 39 0 729 673 ;
+C 73 ; WX 333 ; N Iota ; B 32 0 316 673 ;
+C 74 ; WX 631 ; N theta1 ; B 18 -18 623 689 ;
+C 75 ; WX 722 ; N Kappa ; B 35 0 722 673 ;
+C 76 ; WX 686 ; N Lambda ; B 6 0 680 688 ;
+C 77 ; WX 889 ; N Mu ; B 28 0 887 673 ;
+C 78 ; WX 722 ; N Nu ; B 29 -8 720 673 ;
+C 79 ; WX 722 ; N Omicron ; B 41 -17 715 685 ;
+C 80 ; WX 768 ; N Pi ; B 25 0 745 673 ;
+C 81 ; WX 741 ; N Theta ; B 41 -17 715 685 ;
+C 82 ; WX 556 ; N Rho ; B 28 0 563 673 ;
+C 83 ; WX 592 ; N Sigma ; B 5 0 589 673 ;
+C 84 ; WX 611 ; N Tau ; B 33 0 607 673 ;
+C 85 ; WX 690 ; N Upsilon ; B -8 0 694 673 ;
+C 86 ; WX 439 ; N sigma1 ; B 40 -233 436 500 ;
+C 87 ; WX 768 ; N Omega ; B 34 0 736 688 ;
+C 88 ; WX 645 ; N Xi ; B 40 0 599 673 ;
+C 89 ; WX 795 ; N Psi ; B 15 0 781 684 ;
+C 90 ; WX 611 ; N Zeta ; B 44 0 636 673 ;
+C 91 ; WX 333 ; N bracketleft ; B 86 -155 299 674 ;
+C 92 ; WX 863 ; N therefore ; B 163 0 701 487 ;
+C 93 ; WX 333 ; N bracketright ; B 33 -155 246 674 ;
+C 94 ; WX 658 ; N perpendicular ; B 15 0 652 674 ;
+C 95 ; WX 500 ; N underscore ; B -2 -125 502 -75 ;
+C 96 ; WX 500 ; N radicalex ; B 480 881 1090 917 ;
+C 97 ; WX 631 ; N alpha ; B 41 -18 622 500 ;
+C 98 ; WX 549 ; N beta ; B 61 -223 515 741 ;
+C 99 ; WX 549 ; N chi ; B 12 -231 522 499 ;
+C 100 ; WX 494 ; N delta ; B 40 -19 481 740 ;
+C 101 ; WX 439 ; N epsilon ; B 22 -19 427 502 ;
+C 102 ; WX 521 ; N phi ; B 28 -224 492 673 ;
+C 103 ; WX 411 ; N gamma ; B 5 -225 484 499 ;
+C 104 ; WX 603 ; N eta ; B 0 -202 527 514 ;
+C 105 ; WX 329 ; N iota ; B 0 -17 301 503 ;
+C 106 ; WX 603 ; N phi1 ; B 36 -224 587 499 ;
+C 107 ; WX 549 ; N kappa ; B 33 0 558 501 ;
+C 108 ; WX 549 ; N lambda ; B 24 -17 548 739 ;
+C 109 ; WX 576 ; N mu ; B 33 -223 567 500 ;
+C 110 ; WX 521 ; N nu ; B -9 -16 475 507 ;
+C 111 ; WX 549 ; N omicron ; B 35 -19 501 499 ;
+C 112 ; WX 549 ; N pi ; B 10 -19 530 487 ;
+C 113 ; WX 521 ; N theta ; B 43 -17 485 690 ;
+C 114 ; WX 549 ; N rho ; B 50 -230 490 499 ;
+C 115 ; WX 603 ; N sigma ; B 30 -21 588 500 ;
+C 116 ; WX 439 ; N tau ; B 10 -19 418 500 ;
+C 117 ; WX 576 ; N upsilon ; B 7 -18 535 507 ;
+C 118 ; WX 713 ; N omega1 ; B 12 -18 671 583 ;
+C 119 ; WX 686 ; N omega ; B 42 -17 684 500 ;
+C 120 ; WX 493 ; N xi ; B 27 -224 469 766 ;
+C 121 ; WX 686 ; N psi ; B 12 -228 701 500 ;
+C 122 ; WX 494 ; N zeta ; B 60 -225 467 756 ;
+C 123 ; WX 480 ; N braceleft ; B 58 -183 397 673 ;
+C 124 ; WX 200 ; N bar ; B 65 -293 135 707 ;
+C 125 ; WX 480 ; N braceright ; B 79 -183 418 673 ;
+C 126 ; WX 549 ; N similar ; B 17 203 529 307 ;
+C 160 ; WX 750 ; N Euro ; B 20 -12 714 685 ;
+C 161 ; WX 620 ; N Upsilon1 ; B -2 0 610 685 ;
+C 162 ; WX 247 ; N minute ; B 27 459 228 735 ;
+C 163 ; WX 549 ; N lessequal ; B 29 0 526 639 ;
+C 164 ; WX 167 ; N fraction ; B -180 -12 340 677 ;
+C 165 ; WX 713 ; N infinity ; B 26 124 688 404 ;
+C 166 ; WX 500 ; N florin ; B 2 -193 494 686 ;
+C 167 ; WX 753 ; N club ; B 86 -26 660 533 ;
+C 168 ; WX 753 ; N diamond ; B 142 -36 600 550 ;
+C 169 ; WX 753 ; N heart ; B 117 -33 631 532 ;
+C 170 ; WX 753 ; N spade ; B 113 -36 629 548 ;
+C 171 ; WX 1042 ; N arrowboth ; B 24 -15 1024 511 ;
+C 172 ; WX 987 ; N arrowleft ; B 32 -15 942 511 ;
+C 173 ; WX 603 ; N arrowup ; B 45 0 571 910 ;
+C 174 ; WX 987 ; N arrowright ; B 49 -15 959 511 ;
+C 175 ; WX 603 ; N arrowdown ; B 45 -22 571 888 ;
+C 176 ; WX 400 ; N degree ; B 50 385 350 685 ;
+C 177 ; WX 549 ; N plusminus ; B 10 0 539 645 ;
+C 178 ; WX 411 ; N second ; B 20 459 413 737 ;
+C 179 ; WX 549 ; N greaterequal ; B 29 0 526 639 ;
+C 180 ; WX 549 ; N multiply ; B 17 8 533 524 ;
+C 181 ; WX 713 ; N proportional ; B 27 123 639 404 ;
+C 182 ; WX 494 ; N partialdiff ; B 26 -20 462 746 ;
+C 183 ; WX 460 ; N bullet ; B 50 113 410 473 ;
+C 184 ; WX 549 ; N divide ; B 10 71 536 456 ;
+C 185 ; WX 549 ; N notequal ; B 15 -25 540 549 ;
+C 186 ; WX 549 ; N equivalence ; B 14 82 538 443 ;
+C 187 ; WX 549 ; N approxequal ; B 14 135 527 394 ;
+C 188 ; WX 1000 ; N ellipsis ; B 111 -17 889 95 ;
+C 189 ; WX 603 ; N arrowvertex ; B 280 -120 336 1010 ;
+C 190 ; WX 1000 ; N arrowhorizex ; B -60 220 1050 276 ;
+C 191 ; WX 658 ; N carriagereturn ; B 15 -16 602 629 ;
+C 192 ; WX 823 ; N aleph ; B 175 -18 661 658 ;
+C 193 ; WX 686 ; N Ifraktur ; B 10 -53 578 740 ;
+C 194 ; WX 795 ; N Rfraktur ; B 26 -15 759 734 ;
+C 195 ; WX 987 ; N weierstrass ; B 159 -211 870 573 ;
+C 196 ; WX 768 ; N circlemultiply ; B 43 -17 733 673 ;
+C 197 ; WX 768 ; N circleplus ; B 43 -15 733 675 ;
+C 198 ; WX 823 ; N emptyset ; B 39 -24 781 719 ;
+C 199 ; WX 768 ; N intersection ; B 40 0 732 509 ;
+C 200 ; WX 768 ; N union ; B 40 -17 732 492 ;
+C 201 ; WX 713 ; N propersuperset ; B 20 0 673 470 ;
+C 202 ; WX 713 ; N reflexsuperset ; B 20 -125 673 470 ;
+C 203 ; WX 713 ; N notsubset ; B 36 -70 690 540 ;
+C 204 ; WX 713 ; N propersubset ; B 37 0 690 470 ;
+C 205 ; WX 713 ; N reflexsubset ; B 37 -125 690 470 ;
+C 206 ; WX 713 ; N element ; B 45 0 505 468 ;
+C 207 ; WX 713 ; N notelement ; B 45 -58 505 555 ;
+C 208 ; WX 768 ; N angle ; B 26 0 738 673 ;
+C 209 ; WX 713 ; N gradient ; B 36 -19 681 718 ;
+C 210 ; WX 790 ; N registerserif ; B 50 -17 740 673 ;
+C 211 ; WX 790 ; N copyrightserif ; B 51 -15 741 675 ;
+C 212 ; WX 890 ; N trademarkserif ; B 18 293 855 673 ;
+C 213 ; WX 823 ; N product ; B 25 -101 803 751 ;
+C 214 ; WX 549 ; N radical ; B 10 -38 515 917 ;
+C 215 ; WX 250 ; N dotmath ; B 69 210 169 310 ;
+C 216 ; WX 713 ; N logicalnot ; B 15 0 680 288 ;
+C 217 ; WX 603 ; N logicaland ; B 23 0 583 454 ;
+C 218 ; WX 603 ; N logicalor ; B 30 0 578 477 ;
+C 219 ; WX 1042 ; N arrowdblboth ; B 27 -20 1023 510 ;
+C 220 ; WX 987 ; N arrowdblleft ; B 30 -15 939 513 ;
+C 221 ; WX 603 ; N arrowdblup ; B 39 2 567 911 ;
+C 222 ; WX 987 ; N arrowdblright ; B 45 -20 954 508 ;
+C 223 ; WX 603 ; N arrowdbldown ; B 44 -19 572 890 ;
+C 224 ; WX 494 ; N lozenge ; B 18 0 466 745 ;
+C 225 ; WX 329 ; N angleleft ; B 25 -198 306 746 ;
+C 226 ; WX 790 ; N registersans ; B 50 -20 740 670 ;
+C 227 ; WX 790 ; N copyrightsans ; B 49 -15 739 675 ;
+C 228 ; WX 786 ; N trademarksans ; B 5 293 725 673 ;
+C 229 ; WX 713 ; N summation ; B 14 -108 695 752 ;
+C 230 ; WX 384 ; N parenlefttp ; B 24 -293 436 926 ;
+C 231 ; WX 384 ; N parenleftex ; B 24 -85 108 925 ;
+C 232 ; WX 384 ; N parenleftbt ; B 24 -293 436 926 ;
+C 233 ; WX 384 ; N bracketlefttp ; B 0 -80 349 926 ;
+C 234 ; WX 384 ; N bracketleftex ; B 0 -79 77 925 ;
+C 235 ; WX 384 ; N bracketleftbt ; B 0 -80 349 926 ;
+C 236 ; WX 494 ; N bracelefttp ; B 209 -85 445 925 ;
+C 237 ; WX 494 ; N braceleftmid ; B 20 -85 284 935 ;
+C 238 ; WX 494 ; N braceleftbt ; B 209 -75 445 935 ;
+C 239 ; WX 494 ; N braceex ; B 209 -85 284 935 ;
+C 241 ; WX 329 ; N angleright ; B 21 -198 302 746 ;
+C 242 ; WX 274 ; N integral ; B 2 -107 291 916 ;
+C 243 ; WX 686 ; N integraltp ; B 308 -88 675 920 ;
+C 244 ; WX 686 ; N integralex ; B 308 -88 378 975 ;
+C 245 ; WX 686 ; N integralbt ; B 11 -87 378 921 ;
+C 246 ; WX 384 ; N parenrighttp ; B 54 -293 466 926 ;
+C 247 ; WX 384 ; N parenrightex ; B 382 -85 466 925 ;
+C 248 ; WX 384 ; N parenrightbt ; B 54 -293 466 926 ;
+C 249 ; WX 384 ; N bracketrighttp ; B 22 -80 371 926 ;
+C 250 ; WX 384 ; N bracketrightex ; B 294 -79 371 925 ;
+C 251 ; WX 384 ; N bracketrightbt ; B 22 -80 371 926 ;
+C 252 ; WX 494 ; N bracerighttp ; B 48 -85 284 925 ;
+C 253 ; WX 494 ; N bracerightmid ; B 209 -85 473 935 ;
+C 254 ; WX 494 ; N bracerightbt ; B 48 -75 284 935 ;
+C -1 ; WX 790 ; N apple ; B 56 -3 733 808 ;
+EndCharMetrics
+EndFontMetrics
diff --git a/program/libraries/afm/Times-Bold.afm b/program/libraries/afm/Times-Bold.afm
--- /dev/null
@@ -0,0 +1,2588 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
+Comment Creation Date: Thu May 1 12:52:56 1997
+Comment UniqueID 43065
+Comment VMusage 41636 52661
+FontName Times-Bold
+FullName Times Bold
+FamilyName Times
+Weight Bold
+ItalicAngle 0
+IsFixedPitch false
+CharacterSet ExtendedRoman
+FontBBox -168 -218 1000 935
+UnderlinePosition -100
+UnderlineThickness 50
+Version 002.000
+Notice Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.Times is a trademark of Linotype-Hell AG and/or its subsidiaries.
+EncodingScheme AdobeStandardEncoding
+CapHeight 676
+XHeight 461
+Ascender 683
+Descender -217
+StdHW 44
+StdVW 139
+StartCharMetrics 315
+C 32 ; WX 250 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 333 ; N exclam ; B 81 -13 251 691 ;
+C 34 ; WX 555 ; N quotedbl ; B 83 404 472 691 ;
+C 35 ; WX 500 ; N numbersign ; B 4 0 496 700 ;
+C 36 ; WX 500 ; N dollar ; B 29 -99 472 750 ;
+C 37 ; WX 1000 ; N percent ; B 124 -14 877 692 ;
+C 38 ; WX 833 ; N ampersand ; B 62 -16 787 691 ;
+C 39 ; WX 333 ; N quoteright ; B 79 356 263 691 ;
+C 40 ; WX 333 ; N parenleft ; B 46 -168 306 694 ;
+C 41 ; WX 333 ; N parenright ; B 27 -168 287 694 ;
+C 42 ; WX 500 ; N asterisk ; B 56 255 447 691 ;
+C 43 ; WX 570 ; N plus ; B 33 0 537 506 ;
+C 44 ; WX 250 ; N comma ; B 39 -180 223 155 ;
+C 45 ; WX 333 ; N hyphen ; B 44 171 287 287 ;
+C 46 ; WX 250 ; N period ; B 41 -13 210 156 ;
+C 47 ; WX 278 ; N slash ; B -24 -19 302 691 ;
+C 48 ; WX 500 ; N zero ; B 24 -13 476 688 ;
+C 49 ; WX 500 ; N one ; B 65 0 442 688 ;
+C 50 ; WX 500 ; N two ; B 17 0 478 688 ;
+C 51 ; WX 500 ; N three ; B 16 -14 468 688 ;
+C 52 ; WX 500 ; N four ; B 19 0 475 688 ;
+C 53 ; WX 500 ; N five ; B 22 -8 470 676 ;
+C 54 ; WX 500 ; N six ; B 28 -13 475 688 ;
+C 55 ; WX 500 ; N seven ; B 17 0 477 676 ;
+C 56 ; WX 500 ; N eight ; B 28 -13 472 688 ;
+C 57 ; WX 500 ; N nine ; B 26 -13 473 688 ;
+C 58 ; WX 333 ; N colon ; B 82 -13 251 472 ;
+C 59 ; WX 333 ; N semicolon ; B 82 -180 266 472 ;
+C 60 ; WX 570 ; N less ; B 31 -8 539 514 ;
+C 61 ; WX 570 ; N equal ; B 33 107 537 399 ;
+C 62 ; WX 570 ; N greater ; B 31 -8 539 514 ;
+C 63 ; WX 500 ; N question ; B 57 -13 445 689 ;
+C 64 ; WX 930 ; N at ; B 108 -19 822 691 ;
+C 65 ; WX 722 ; N A ; B 9 0 689 690 ;
+C 66 ; WX 667 ; N B ; B 16 0 619 676 ;
+C 67 ; WX 722 ; N C ; B 49 -19 687 691 ;
+C 68 ; WX 722 ; N D ; B 14 0 690 676 ;
+C 69 ; WX 667 ; N E ; B 16 0 641 676 ;
+C 70 ; WX 611 ; N F ; B 16 0 583 676 ;
+C 71 ; WX 778 ; N G ; B 37 -19 755 691 ;
+C 72 ; WX 778 ; N H ; B 21 0 759 676 ;
+C 73 ; WX 389 ; N I ; B 20 0 370 676 ;
+C 74 ; WX 500 ; N J ; B 3 -96 479 676 ;
+C 75 ; WX 778 ; N K ; B 30 0 769 676 ;
+C 76 ; WX 667 ; N L ; B 19 0 638 676 ;
+C 77 ; WX 944 ; N M ; B 14 0 921 676 ;
+C 78 ; WX 722 ; N N ; B 16 -18 701 676 ;
+C 79 ; WX 778 ; N O ; B 35 -19 743 691 ;
+C 80 ; WX 611 ; N P ; B 16 0 600 676 ;
+C 81 ; WX 778 ; N Q ; B 35 -176 743 691 ;
+C 82 ; WX 722 ; N R ; B 26 0 715 676 ;
+C 83 ; WX 556 ; N S ; B 35 -19 513 692 ;
+C 84 ; WX 667 ; N T ; B 31 0 636 676 ;
+C 85 ; WX 722 ; N U ; B 16 -19 701 676 ;
+C 86 ; WX 722 ; N V ; B 16 -18 701 676 ;
+C 87 ; WX 1000 ; N W ; B 19 -15 981 676 ;
+C 88 ; WX 722 ; N X ; B 16 0 699 676 ;
+C 89 ; WX 722 ; N Y ; B 15 0 699 676 ;
+C 90 ; WX 667 ; N Z ; B 28 0 634 676 ;
+C 91 ; WX 333 ; N bracketleft ; B 67 -149 301 678 ;
+C 92 ; WX 278 ; N backslash ; B -25 -19 303 691 ;
+C 93 ; WX 333 ; N bracketright ; B 32 -149 266 678 ;
+C 94 ; WX 581 ; N asciicircum ; B 73 311 509 676 ;
+C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ;
+C 96 ; WX 333 ; N quoteleft ; B 70 356 254 691 ;
+C 97 ; WX 500 ; N a ; B 25 -14 488 473 ;
+C 98 ; WX 556 ; N b ; B 17 -14 521 676 ;
+C 99 ; WX 444 ; N c ; B 25 -14 430 473 ;
+C 100 ; WX 556 ; N d ; B 25 -14 534 676 ;
+C 101 ; WX 444 ; N e ; B 25 -14 426 473 ;
+C 102 ; WX 333 ; N f ; B 14 0 389 691 ; L i fi ; L l fl ;
+C 103 ; WX 500 ; N g ; B 28 -206 483 473 ;
+C 104 ; WX 556 ; N h ; B 16 0 534 676 ;
+C 105 ; WX 278 ; N i ; B 16 0 255 691 ;
+C 106 ; WX 333 ; N j ; B -57 -203 263 691 ;
+C 107 ; WX 556 ; N k ; B 22 0 543 676 ;
+C 108 ; WX 278 ; N l ; B 16 0 255 676 ;
+C 109 ; WX 833 ; N m ; B 16 0 814 473 ;
+C 110 ; WX 556 ; N n ; B 21 0 539 473 ;
+C 111 ; WX 500 ; N o ; B 25 -14 476 473 ;
+C 112 ; WX 556 ; N p ; B 19 -205 524 473 ;
+C 113 ; WX 556 ; N q ; B 34 -205 536 473 ;
+C 114 ; WX 444 ; N r ; B 29 0 434 473 ;
+C 115 ; WX 389 ; N s ; B 25 -14 361 473 ;
+C 116 ; WX 333 ; N t ; B 20 -12 332 630 ;
+C 117 ; WX 556 ; N u ; B 16 -14 537 461 ;
+C 118 ; WX 500 ; N v ; B 21 -14 485 461 ;
+C 119 ; WX 722 ; N w ; B 23 -14 707 461 ;
+C 120 ; WX 500 ; N x ; B 12 0 484 461 ;
+C 121 ; WX 500 ; N y ; B 16 -205 480 461 ;
+C 122 ; WX 444 ; N z ; B 21 0 420 461 ;
+C 123 ; WX 394 ; N braceleft ; B 22 -175 340 698 ;
+C 124 ; WX 220 ; N bar ; B 66 -218 154 782 ;
+C 125 ; WX 394 ; N braceright ; B 54 -175 372 698 ;
+C 126 ; WX 520 ; N asciitilde ; B 29 173 491 333 ;
+C 161 ; WX 333 ; N exclamdown ; B 82 -203 252 501 ;
+C 162 ; WX 500 ; N cent ; B 53 -140 458 588 ;
+C 163 ; WX 500 ; N sterling ; B 21 -14 477 684 ;
+C 164 ; WX 167 ; N fraction ; B -168 -12 329 688 ;
+C 165 ; WX 500 ; N yen ; B -64 0 547 676 ;
+C 166 ; WX 500 ; N florin ; B 0 -155 498 706 ;
+C 167 ; WX 500 ; N section ; B 57 -132 443 691 ;
+C 168 ; WX 500 ; N currency ; B -26 61 526 613 ;
+C 169 ; WX 278 ; N quotesingle ; B 75 404 204 691 ;
+C 170 ; WX 500 ; N quotedblleft ; B 32 356 486 691 ;
+C 171 ; WX 500 ; N guillemotleft ; B 23 36 473 415 ;
+C 172 ; WX 333 ; N guilsinglleft ; B 51 36 305 415 ;
+C 173 ; WX 333 ; N guilsinglright ; B 28 36 282 415 ;
+C 174 ; WX 556 ; N fi ; B 14 0 536 691 ;
+C 175 ; WX 556 ; N fl ; B 14 0 536 691 ;
+C 177 ; WX 500 ; N endash ; B 0 181 500 271 ;
+C 178 ; WX 500 ; N dagger ; B 47 -134 453 691 ;
+C 179 ; WX 500 ; N daggerdbl ; B 45 -132 456 691 ;
+C 180 ; WX 250 ; N periodcentered ; B 41 248 210 417 ;
+C 182 ; WX 540 ; N paragraph ; B 0 -186 519 676 ;
+C 183 ; WX 350 ; N bullet ; B 35 198 315 478 ;
+C 184 ; WX 333 ; N quotesinglbase ; B 79 -180 263 155 ;
+C 185 ; WX 500 ; N quotedblbase ; B 14 -180 468 155 ;
+C 186 ; WX 500 ; N quotedblright ; B 14 356 468 691 ;
+C 187 ; WX 500 ; N guillemotright ; B 27 36 477 415 ;
+C 188 ; WX 1000 ; N ellipsis ; B 82 -13 917 156 ;
+C 189 ; WX 1000 ; N perthousand ; B 7 -29 995 706 ;
+C 191 ; WX 500 ; N questiondown ; B 55 -201 443 501 ;
+C 193 ; WX 333 ; N grave ; B 8 528 246 713 ;
+C 194 ; WX 333 ; N acute ; B 86 528 324 713 ;
+C 195 ; WX 333 ; N circumflex ; B -2 528 335 704 ;
+C 196 ; WX 333 ; N tilde ; B -16 547 349 674 ;
+C 197 ; WX 333 ; N macron ; B 1 565 331 637 ;
+C 198 ; WX 333 ; N breve ; B 15 528 318 691 ;
+C 199 ; WX 333 ; N dotaccent ; B 103 536 258 691 ;
+C 200 ; WX 333 ; N dieresis ; B -2 537 335 667 ;
+C 202 ; WX 333 ; N ring ; B 60 527 273 740 ;
+C 203 ; WX 333 ; N cedilla ; B 68 -218 294 0 ;
+C 205 ; WX 333 ; N hungarumlaut ; B -13 528 425 713 ;
+C 206 ; WX 333 ; N ogonek ; B 90 -193 319 24 ;
+C 207 ; WX 333 ; N caron ; B -2 528 335 704 ;
+C 208 ; WX 1000 ; N emdash ; B 0 181 1000 271 ;
+C 225 ; WX 1000 ; N AE ; B 4 0 951 676 ;
+C 227 ; WX 300 ; N ordfeminine ; B -1 397 301 688 ;
+C 232 ; WX 667 ; N Lslash ; B 19 0 638 676 ;
+C 233 ; WX 778 ; N Oslash ; B 35 -74 743 737 ;
+C 234 ; WX 1000 ; N OE ; B 22 -5 981 684 ;
+C 235 ; WX 330 ; N ordmasculine ; B 18 397 312 688 ;
+C 241 ; WX 722 ; N ae ; B 33 -14 693 473 ;
+C 245 ; WX 278 ; N dotlessi ; B 16 0 255 461 ;
+C 248 ; WX 278 ; N lslash ; B -22 0 303 676 ;
+C 249 ; WX 500 ; N oslash ; B 25 -92 476 549 ;
+C 250 ; WX 722 ; N oe ; B 22 -14 696 473 ;
+C 251 ; WX 556 ; N germandbls ; B 19 -12 517 691 ;
+C -1 ; WX 389 ; N Idieresis ; B 20 0 370 877 ;
+C -1 ; WX 444 ; N eacute ; B 25 -14 426 713 ;
+C -1 ; WX 500 ; N abreve ; B 25 -14 488 691 ;
+C -1 ; WX 556 ; N uhungarumlaut ; B 16 -14 557 713 ;
+C -1 ; WX 444 ; N ecaron ; B 25 -14 426 704 ;
+C -1 ; WX 722 ; N Ydieresis ; B 15 0 699 877 ;
+C -1 ; WX 570 ; N divide ; B 33 -31 537 537 ;
+C -1 ; WX 722 ; N Yacute ; B 15 0 699 923 ;
+C -1 ; WX 722 ; N Acircumflex ; B 9 0 689 914 ;
+C -1 ; WX 500 ; N aacute ; B 25 -14 488 713 ;
+C -1 ; WX 722 ; N Ucircumflex ; B 16 -19 701 914 ;
+C -1 ; WX 500 ; N yacute ; B 16 -205 480 713 ;
+C -1 ; WX 389 ; N scommaaccent ; B 25 -218 361 473 ;
+C -1 ; WX 444 ; N ecircumflex ; B 25 -14 426 704 ;
+C -1 ; WX 722 ; N Uring ; B 16 -19 701 935 ;
+C -1 ; WX 722 ; N Udieresis ; B 16 -19 701 877 ;
+C -1 ; WX 500 ; N aogonek ; B 25 -193 504 473 ;
+C -1 ; WX 722 ; N Uacute ; B 16 -19 701 923 ;
+C -1 ; WX 556 ; N uogonek ; B 16 -193 539 461 ;
+C -1 ; WX 667 ; N Edieresis ; B 16 0 641 877 ;
+C -1 ; WX 722 ; N Dcroat ; B 6 0 690 676 ;
+C -1 ; WX 250 ; N commaaccent ; B 47 -218 203 -50 ;
+C -1 ; WX 747 ; N copyright ; B 26 -19 721 691 ;
+C -1 ; WX 667 ; N Emacron ; B 16 0 641 847 ;
+C -1 ; WX 444 ; N ccaron ; B 25 -14 430 704 ;
+C -1 ; WX 500 ; N aring ; B 25 -14 488 740 ;
+C -1 ; WX 722 ; N Ncommaaccent ; B 16 -188 701 676 ;
+C -1 ; WX 278 ; N lacute ; B 16 0 297 923 ;
+C -1 ; WX 500 ; N agrave ; B 25 -14 488 713 ;
+C -1 ; WX 667 ; N Tcommaaccent ; B 31 -218 636 676 ;
+C -1 ; WX 722 ; N Cacute ; B 49 -19 687 923 ;
+C -1 ; WX 500 ; N atilde ; B 25 -14 488 674 ;
+C -1 ; WX 667 ; N Edotaccent ; B 16 0 641 901 ;
+C -1 ; WX 389 ; N scaron ; B 25 -14 363 704 ;
+C -1 ; WX 389 ; N scedilla ; B 25 -218 361 473 ;
+C -1 ; WX 278 ; N iacute ; B 16 0 289 713 ;
+C -1 ; WX 494 ; N lozenge ; B 10 0 484 745 ;
+C -1 ; WX 722 ; N Rcaron ; B 26 0 715 914 ;
+C -1 ; WX 778 ; N Gcommaaccent ; B 37 -218 755 691 ;
+C -1 ; WX 556 ; N ucircumflex ; B 16 -14 537 704 ;
+C -1 ; WX 500 ; N acircumflex ; B 25 -14 488 704 ;
+C -1 ; WX 722 ; N Amacron ; B 9 0 689 847 ;
+C -1 ; WX 444 ; N rcaron ; B 29 0 434 704 ;
+C -1 ; WX 444 ; N ccedilla ; B 25 -218 430 473 ;
+C -1 ; WX 667 ; N Zdotaccent ; B 28 0 634 901 ;
+C -1 ; WX 611 ; N Thorn ; B 16 0 600 676 ;
+C -1 ; WX 778 ; N Omacron ; B 35 -19 743 847 ;
+C -1 ; WX 722 ; N Racute ; B 26 0 715 923 ;
+C -1 ; WX 556 ; N Sacute ; B 35 -19 513 923 ;
+C -1 ; WX 672 ; N dcaron ; B 25 -14 681 682 ;
+C -1 ; WX 722 ; N Umacron ; B 16 -19 701 847 ;
+C -1 ; WX 556 ; N uring ; B 16 -14 537 740 ;
+C -1 ; WX 300 ; N threesuperior ; B 3 268 297 688 ;
+C -1 ; WX 778 ; N Ograve ; B 35 -19 743 923 ;
+C -1 ; WX 722 ; N Agrave ; B 9 0 689 923 ;
+C -1 ; WX 722 ; N Abreve ; B 9 0 689 901 ;
+C -1 ; WX 570 ; N multiply ; B 48 16 522 490 ;
+C -1 ; WX 556 ; N uacute ; B 16 -14 537 713 ;
+C -1 ; WX 667 ; N Tcaron ; B 31 0 636 914 ;
+C -1 ; WX 494 ; N partialdiff ; B 11 -21 494 750 ;
+C -1 ; WX 500 ; N ydieresis ; B 16 -205 480 667 ;
+C -1 ; WX 722 ; N Nacute ; B 16 -18 701 923 ;
+C -1 ; WX 278 ; N icircumflex ; B -37 0 300 704 ;
+C -1 ; WX 667 ; N Ecircumflex ; B 16 0 641 914 ;
+C -1 ; WX 500 ; N adieresis ; B 25 -14 488 667 ;
+C -1 ; WX 444 ; N edieresis ; B 25 -14 426 667 ;
+C -1 ; WX 444 ; N cacute ; B 25 -14 430 713 ;
+C -1 ; WX 556 ; N nacute ; B 21 0 539 713 ;
+C -1 ; WX 556 ; N umacron ; B 16 -14 537 637 ;
+C -1 ; WX 722 ; N Ncaron ; B 16 -18 701 914 ;
+C -1 ; WX 389 ; N Iacute ; B 20 0 370 923 ;
+C -1 ; WX 570 ; N plusminus ; B 33 0 537 506 ;
+C -1 ; WX 220 ; N brokenbar ; B 66 -143 154 707 ;
+C -1 ; WX 747 ; N registered ; B 26 -19 721 691 ;
+C -1 ; WX 778 ; N Gbreve ; B 37 -19 755 901 ;
+C -1 ; WX 389 ; N Idotaccent ; B 20 0 370 901 ;
+C -1 ; WX 600 ; N summation ; B 14 -10 585 706 ;
+C -1 ; WX 667 ; N Egrave ; B 16 0 641 923 ;
+C -1 ; WX 444 ; N racute ; B 29 0 434 713 ;
+C -1 ; WX 500 ; N omacron ; B 25 -14 476 637 ;
+C -1 ; WX 667 ; N Zacute ; B 28 0 634 923 ;
+C -1 ; WX 667 ; N Zcaron ; B 28 0 634 914 ;
+C -1 ; WX 549 ; N greaterequal ; B 26 0 523 704 ;
+C -1 ; WX 722 ; N Eth ; B 6 0 690 676 ;
+C -1 ; WX 722 ; N Ccedilla ; B 49 -218 687 691 ;
+C -1 ; WX 278 ; N lcommaaccent ; B 16 -218 255 676 ;
+C -1 ; WX 416 ; N tcaron ; B 20 -12 425 815 ;
+C -1 ; WX 444 ; N eogonek ; B 25 -193 426 473 ;
+C -1 ; WX 722 ; N Uogonek ; B 16 -193 701 676 ;
+C -1 ; WX 722 ; N Aacute ; B 9 0 689 923 ;
+C -1 ; WX 722 ; N Adieresis ; B 9 0 689 877 ;
+C -1 ; WX 444 ; N egrave ; B 25 -14 426 713 ;
+C -1 ; WX 444 ; N zacute ; B 21 0 420 713 ;
+C -1 ; WX 278 ; N iogonek ; B 16 -193 274 691 ;
+C -1 ; WX 778 ; N Oacute ; B 35 -19 743 923 ;
+C -1 ; WX 500 ; N oacute ; B 25 -14 476 713 ;
+C -1 ; WX 500 ; N amacron ; B 25 -14 488 637 ;
+C -1 ; WX 389 ; N sacute ; B 25 -14 361 713 ;
+C -1 ; WX 278 ; N idieresis ; B -37 0 300 667 ;
+C -1 ; WX 778 ; N Ocircumflex ; B 35 -19 743 914 ;
+C -1 ; WX 722 ; N Ugrave ; B 16 -19 701 923 ;
+C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ;
+C -1 ; WX 556 ; N thorn ; B 19 -205 524 676 ;
+C -1 ; WX 300 ; N twosuperior ; B 0 275 300 688 ;
+C -1 ; WX 778 ; N Odieresis ; B 35 -19 743 877 ;
+C -1 ; WX 556 ; N mu ; B 33 -206 536 461 ;
+C -1 ; WX 278 ; N igrave ; B -27 0 255 713 ;
+C -1 ; WX 500 ; N ohungarumlaut ; B 25 -14 529 713 ;
+C -1 ; WX 667 ; N Eogonek ; B 16 -193 644 676 ;
+C -1 ; WX 556 ; N dcroat ; B 25 -14 534 676 ;
+C -1 ; WX 750 ; N threequarters ; B 23 -12 733 688 ;
+C -1 ; WX 556 ; N Scedilla ; B 35 -218 513 692 ;
+C -1 ; WX 394 ; N lcaron ; B 16 0 412 682 ;
+C -1 ; WX 778 ; N Kcommaaccent ; B 30 -218 769 676 ;
+C -1 ; WX 667 ; N Lacute ; B 19 0 638 923 ;
+C -1 ; WX 1000 ; N trademark ; B 24 271 977 676 ;
+C -1 ; WX 444 ; N edotaccent ; B 25 -14 426 691 ;
+C -1 ; WX 389 ; N Igrave ; B 20 0 370 923 ;
+C -1 ; WX 389 ; N Imacron ; B 20 0 370 847 ;
+C -1 ; WX 667 ; N Lcaron ; B 19 0 652 682 ;
+C -1 ; WX 750 ; N onehalf ; B -7 -12 775 688 ;
+C -1 ; WX 549 ; N lessequal ; B 29 0 526 704 ;
+C -1 ; WX 500 ; N ocircumflex ; B 25 -14 476 704 ;
+C -1 ; WX 556 ; N ntilde ; B 21 0 539 674 ;
+C -1 ; WX 722 ; N Uhungarumlaut ; B 16 -19 701 923 ;
+C -1 ; WX 667 ; N Eacute ; B 16 0 641 923 ;
+C -1 ; WX 444 ; N emacron ; B 25 -14 426 637 ;
+C -1 ; WX 500 ; N gbreve ; B 28 -206 483 691 ;
+C -1 ; WX 750 ; N onequarter ; B 28 -12 743 688 ;
+C -1 ; WX 556 ; N Scaron ; B 35 -19 513 914 ;
+C -1 ; WX 556 ; N Scommaaccent ; B 35 -218 513 692 ;
+C -1 ; WX 778 ; N Ohungarumlaut ; B 35 -19 743 923 ;
+C -1 ; WX 400 ; N degree ; B 57 402 343 688 ;
+C -1 ; WX 500 ; N ograve ; B 25 -14 476 713 ;
+C -1 ; WX 722 ; N Ccaron ; B 49 -19 687 914 ;
+C -1 ; WX 556 ; N ugrave ; B 16 -14 537 713 ;
+C -1 ; WX 549 ; N radical ; B 10 -46 512 850 ;
+C -1 ; WX 722 ; N Dcaron ; B 14 0 690 914 ;
+C -1 ; WX 444 ; N rcommaaccent ; B 29 -218 434 473 ;
+C -1 ; WX 722 ; N Ntilde ; B 16 -18 701 884 ;
+C -1 ; WX 500 ; N otilde ; B 25 -14 476 674 ;
+C -1 ; WX 722 ; N Rcommaaccent ; B 26 -218 715 676 ;
+C -1 ; WX 667 ; N Lcommaaccent ; B 19 -218 638 676 ;
+C -1 ; WX 722 ; N Atilde ; B 9 0 689 884 ;
+C -1 ; WX 722 ; N Aogonek ; B 9 -193 699 690 ;
+C -1 ; WX 722 ; N Aring ; B 9 0 689 935 ;
+C -1 ; WX 778 ; N Otilde ; B 35 -19 743 884 ;
+C -1 ; WX 444 ; N zdotaccent ; B 21 0 420 691 ;
+C -1 ; WX 667 ; N Ecaron ; B 16 0 641 914 ;
+C -1 ; WX 389 ; N Iogonek ; B 20 -193 370 676 ;
+C -1 ; WX 556 ; N kcommaaccent ; B 22 -218 543 676 ;
+C -1 ; WX 570 ; N minus ; B 33 209 537 297 ;
+C -1 ; WX 389 ; N Icircumflex ; B 20 0 370 914 ;
+C -1 ; WX 556 ; N ncaron ; B 21 0 539 704 ;
+C -1 ; WX 333 ; N tcommaaccent ; B 20 -218 332 630 ;
+C -1 ; WX 570 ; N logicalnot ; B 33 108 537 399 ;
+C -1 ; WX 500 ; N odieresis ; B 25 -14 476 667 ;
+C -1 ; WX 556 ; N udieresis ; B 16 -14 537 667 ;
+C -1 ; WX 549 ; N notequal ; B 15 -49 540 570 ;
+C -1 ; WX 500 ; N gcommaaccent ; B 28 -206 483 829 ;
+C -1 ; WX 500 ; N eth ; B 25 -14 476 691 ;
+C -1 ; WX 444 ; N zcaron ; B 21 0 420 704 ;
+C -1 ; WX 556 ; N ncommaaccent ; B 21 -218 539 473 ;
+C -1 ; WX 300 ; N onesuperior ; B 28 275 273 688 ;
+C -1 ; WX 278 ; N imacron ; B -8 0 272 637 ;
+C -1 ; WX 500 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+StartKernData
+StartKernPairs 2242
+KPX A C -55
+KPX A Cacute -55
+KPX A Ccaron -55
+KPX A Ccedilla -55
+KPX A G -55
+KPX A Gbreve -55
+KPX A Gcommaaccent -55
+KPX A O -45
+KPX A Oacute -45
+KPX A Ocircumflex -45
+KPX A Odieresis -45
+KPX A Ograve -45
+KPX A Ohungarumlaut -45
+KPX A Omacron -45
+KPX A Oslash -45
+KPX A Otilde -45
+KPX A Q -45
+KPX A T -95
+KPX A Tcaron -95
+KPX A Tcommaaccent -95
+KPX A U -50
+KPX A Uacute -50
+KPX A Ucircumflex -50
+KPX A Udieresis -50
+KPX A Ugrave -50
+KPX A Uhungarumlaut -50
+KPX A Umacron -50
+KPX A Uogonek -50
+KPX A Uring -50
+KPX A V -145
+KPX A W -130
+KPX A Y -100
+KPX A Yacute -100
+KPX A Ydieresis -100
+KPX A p -25
+KPX A quoteright -74
+KPX A u -50
+KPX A uacute -50
+KPX A ucircumflex -50
+KPX A udieresis -50
+KPX A ugrave -50
+KPX A uhungarumlaut -50
+KPX A umacron -50
+KPX A uogonek -50
+KPX A uring -50
+KPX A v -100
+KPX A w -90
+KPX A y -74
+KPX A yacute -74
+KPX A ydieresis -74
+KPX Aacute C -55
+KPX Aacute Cacute -55
+KPX Aacute Ccaron -55
+KPX Aacute Ccedilla -55
+KPX Aacute G -55
+KPX Aacute Gbreve -55
+KPX Aacute Gcommaaccent -55
+KPX Aacute O -45
+KPX Aacute Oacute -45
+KPX Aacute Ocircumflex -45
+KPX Aacute Odieresis -45
+KPX Aacute Ograve -45
+KPX Aacute Ohungarumlaut -45
+KPX Aacute Omacron -45
+KPX Aacute Oslash -45
+KPX Aacute Otilde -45
+KPX Aacute Q -45
+KPX Aacute T -95
+KPX Aacute Tcaron -95
+KPX Aacute Tcommaaccent -95
+KPX Aacute U -50
+KPX Aacute Uacute -50
+KPX Aacute Ucircumflex -50
+KPX Aacute Udieresis -50
+KPX Aacute Ugrave -50
+KPX Aacute Uhungarumlaut -50
+KPX Aacute Umacron -50
+KPX Aacute Uogonek -50
+KPX Aacute Uring -50
+KPX Aacute V -145
+KPX Aacute W -130
+KPX Aacute Y -100
+KPX Aacute Yacute -100
+KPX Aacute Ydieresis -100
+KPX Aacute p -25
+KPX Aacute quoteright -74
+KPX Aacute u -50
+KPX Aacute uacute -50
+KPX Aacute ucircumflex -50
+KPX Aacute udieresis -50
+KPX Aacute ugrave -50
+KPX Aacute uhungarumlaut -50
+KPX Aacute umacron -50
+KPX Aacute uogonek -50
+KPX Aacute uring -50
+KPX Aacute v -100
+KPX Aacute w -90
+KPX Aacute y -74
+KPX Aacute yacute -74
+KPX Aacute ydieresis -74
+KPX Abreve C -55
+KPX Abreve Cacute -55
+KPX Abreve Ccaron -55
+KPX Abreve Ccedilla -55
+KPX Abreve G -55
+KPX Abreve Gbreve -55
+KPX Abreve Gcommaaccent -55
+KPX Abreve O -45
+KPX Abreve Oacute -45
+KPX Abreve Ocircumflex -45
+KPX Abreve Odieresis -45
+KPX Abreve Ograve -45
+KPX Abreve Ohungarumlaut -45
+KPX Abreve Omacron -45
+KPX Abreve Oslash -45
+KPX Abreve Otilde -45
+KPX Abreve Q -45
+KPX Abreve T -95
+KPX Abreve Tcaron -95
+KPX Abreve Tcommaaccent -95
+KPX Abreve U -50
+KPX Abreve Uacute -50
+KPX Abreve Ucircumflex -50
+KPX Abreve Udieresis -50
+KPX Abreve Ugrave -50
+KPX Abreve Uhungarumlaut -50
+KPX Abreve Umacron -50
+KPX Abreve Uogonek -50
+KPX Abreve Uring -50
+KPX Abreve V -145
+KPX Abreve W -130
+KPX Abreve Y -100
+KPX Abreve Yacute -100
+KPX Abreve Ydieresis -100
+KPX Abreve p -25
+KPX Abreve quoteright -74
+KPX Abreve u -50
+KPX Abreve uacute -50
+KPX Abreve ucircumflex -50
+KPX Abreve udieresis -50
+KPX Abreve ugrave -50
+KPX Abreve uhungarumlaut -50
+KPX Abreve umacron -50
+KPX Abreve uogonek -50
+KPX Abreve uring -50
+KPX Abreve v -100
+KPX Abreve w -90
+KPX Abreve y -74
+KPX Abreve yacute -74
+KPX Abreve ydieresis -74
+KPX Acircumflex C -55
+KPX Acircumflex Cacute -55
+KPX Acircumflex Ccaron -55
+KPX Acircumflex Ccedilla -55
+KPX Acircumflex G -55
+KPX Acircumflex Gbreve -55
+KPX Acircumflex Gcommaaccent -55
+KPX Acircumflex O -45
+KPX Acircumflex Oacute -45
+KPX Acircumflex Ocircumflex -45
+KPX Acircumflex Odieresis -45
+KPX Acircumflex Ograve -45
+KPX Acircumflex Ohungarumlaut -45
+KPX Acircumflex Omacron -45
+KPX Acircumflex Oslash -45
+KPX Acircumflex Otilde -45
+KPX Acircumflex Q -45
+KPX Acircumflex T -95
+KPX Acircumflex Tcaron -95
+KPX Acircumflex Tcommaaccent -95
+KPX Acircumflex U -50
+KPX Acircumflex Uacute -50
+KPX Acircumflex Ucircumflex -50
+KPX Acircumflex Udieresis -50
+KPX Acircumflex Ugrave -50
+KPX Acircumflex Uhungarumlaut -50
+KPX Acircumflex Umacron -50
+KPX Acircumflex Uogonek -50
+KPX Acircumflex Uring -50
+KPX Acircumflex V -145
+KPX Acircumflex W -130
+KPX Acircumflex Y -100
+KPX Acircumflex Yacute -100
+KPX Acircumflex Ydieresis -100
+KPX Acircumflex p -25
+KPX Acircumflex quoteright -74
+KPX Acircumflex u -50
+KPX Acircumflex uacute -50
+KPX Acircumflex ucircumflex -50
+KPX Acircumflex udieresis -50
+KPX Acircumflex ugrave -50
+KPX Acircumflex uhungarumlaut -50
+KPX Acircumflex umacron -50
+KPX Acircumflex uogonek -50
+KPX Acircumflex uring -50
+KPX Acircumflex v -100
+KPX Acircumflex w -90
+KPX Acircumflex y -74
+KPX Acircumflex yacute -74
+KPX Acircumflex ydieresis -74
+KPX Adieresis C -55
+KPX Adieresis Cacute -55
+KPX Adieresis Ccaron -55
+KPX Adieresis Ccedilla -55
+KPX Adieresis G -55
+KPX Adieresis Gbreve -55
+KPX Adieresis Gcommaaccent -55
+KPX Adieresis O -45
+KPX Adieresis Oacute -45
+KPX Adieresis Ocircumflex -45
+KPX Adieresis Odieresis -45
+KPX Adieresis Ograve -45
+KPX Adieresis Ohungarumlaut -45
+KPX Adieresis Omacron -45
+KPX Adieresis Oslash -45
+KPX Adieresis Otilde -45
+KPX Adieresis Q -45
+KPX Adieresis T -95
+KPX Adieresis Tcaron -95
+KPX Adieresis Tcommaaccent -95
+KPX Adieresis U -50
+KPX Adieresis Uacute -50
+KPX Adieresis Ucircumflex -50
+KPX Adieresis Udieresis -50
+KPX Adieresis Ugrave -50
+KPX Adieresis Uhungarumlaut -50
+KPX Adieresis Umacron -50
+KPX Adieresis Uogonek -50
+KPX Adieresis Uring -50
+KPX Adieresis V -145
+KPX Adieresis W -130
+KPX Adieresis Y -100
+KPX Adieresis Yacute -100
+KPX Adieresis Ydieresis -100
+KPX Adieresis p -25
+KPX Adieresis quoteright -74
+KPX Adieresis u -50
+KPX Adieresis uacute -50
+KPX Adieresis ucircumflex -50
+KPX Adieresis udieresis -50
+KPX Adieresis ugrave -50
+KPX Adieresis uhungarumlaut -50
+KPX Adieresis umacron -50
+KPX Adieresis uogonek -50
+KPX Adieresis uring -50
+KPX Adieresis v -100
+KPX Adieresis w -90
+KPX Adieresis y -74
+KPX Adieresis yacute -74
+KPX Adieresis ydieresis -74
+KPX Agrave C -55
+KPX Agrave Cacute -55
+KPX Agrave Ccaron -55
+KPX Agrave Ccedilla -55
+KPX Agrave G -55
+KPX Agrave Gbreve -55
+KPX Agrave Gcommaaccent -55
+KPX Agrave O -45
+KPX Agrave Oacute -45
+KPX Agrave Ocircumflex -45
+KPX Agrave Odieresis -45
+KPX Agrave Ograve -45
+KPX Agrave Ohungarumlaut -45
+KPX Agrave Omacron -45
+KPX Agrave Oslash -45
+KPX Agrave Otilde -45
+KPX Agrave Q -45
+KPX Agrave T -95
+KPX Agrave Tcaron -95
+KPX Agrave Tcommaaccent -95
+KPX Agrave U -50
+KPX Agrave Uacute -50
+KPX Agrave Ucircumflex -50
+KPX Agrave Udieresis -50
+KPX Agrave Ugrave -50
+KPX Agrave Uhungarumlaut -50
+KPX Agrave Umacron -50
+KPX Agrave Uogonek -50
+KPX Agrave Uring -50
+KPX Agrave V -145
+KPX Agrave W -130
+KPX Agrave Y -100
+KPX Agrave Yacute -100
+KPX Agrave Ydieresis -100
+KPX Agrave p -25
+KPX Agrave quoteright -74
+KPX Agrave u -50
+KPX Agrave uacute -50
+KPX Agrave ucircumflex -50
+KPX Agrave udieresis -50
+KPX Agrave ugrave -50
+KPX Agrave uhungarumlaut -50
+KPX Agrave umacron -50
+KPX Agrave uogonek -50
+KPX Agrave uring -50
+KPX Agrave v -100
+KPX Agrave w -90
+KPX Agrave y -74
+KPX Agrave yacute -74
+KPX Agrave ydieresis -74
+KPX Amacron C -55
+KPX Amacron Cacute -55
+KPX Amacron Ccaron -55
+KPX Amacron Ccedilla -55
+KPX Amacron G -55
+KPX Amacron Gbreve -55
+KPX Amacron Gcommaaccent -55
+KPX Amacron O -45
+KPX Amacron Oacute -45
+KPX Amacron Ocircumflex -45
+KPX Amacron Odieresis -45
+KPX Amacron Ograve -45
+KPX Amacron Ohungarumlaut -45
+KPX Amacron Omacron -45
+KPX Amacron Oslash -45
+KPX Amacron Otilde -45
+KPX Amacron Q -45
+KPX Amacron T -95
+KPX Amacron Tcaron -95
+KPX Amacron Tcommaaccent -95
+KPX Amacron U -50
+KPX Amacron Uacute -50
+KPX Amacron Ucircumflex -50
+KPX Amacron Udieresis -50
+KPX Amacron Ugrave -50
+KPX Amacron Uhungarumlaut -50
+KPX Amacron Umacron -50
+KPX Amacron Uogonek -50
+KPX Amacron Uring -50
+KPX Amacron V -145
+KPX Amacron W -130
+KPX Amacron Y -100
+KPX Amacron Yacute -100
+KPX Amacron Ydieresis -100
+KPX Amacron p -25
+KPX Amacron quoteright -74
+KPX Amacron u -50
+KPX Amacron uacute -50
+KPX Amacron ucircumflex -50
+KPX Amacron udieresis -50
+KPX Amacron ugrave -50
+KPX Amacron uhungarumlaut -50
+KPX Amacron umacron -50
+KPX Amacron uogonek -50
+KPX Amacron uring -50
+KPX Amacron v -100
+KPX Amacron w -90
+KPX Amacron y -74
+KPX Amacron yacute -74
+KPX Amacron ydieresis -74
+KPX Aogonek C -55
+KPX Aogonek Cacute -55
+KPX Aogonek Ccaron -55
+KPX Aogonek Ccedilla -55
+KPX Aogonek G -55
+KPX Aogonek Gbreve -55
+KPX Aogonek Gcommaaccent -55
+KPX Aogonek O -45
+KPX Aogonek Oacute -45
+KPX Aogonek Ocircumflex -45
+KPX Aogonek Odieresis -45
+KPX Aogonek Ograve -45
+KPX Aogonek Ohungarumlaut -45
+KPX Aogonek Omacron -45
+KPX Aogonek Oslash -45
+KPX Aogonek Otilde -45
+KPX Aogonek Q -45
+KPX Aogonek T -95
+KPX Aogonek Tcaron -95
+KPX Aogonek Tcommaaccent -95
+KPX Aogonek U -50
+KPX Aogonek Uacute -50
+KPX Aogonek Ucircumflex -50
+KPX Aogonek Udieresis -50
+KPX Aogonek Ugrave -50
+KPX Aogonek Uhungarumlaut -50
+KPX Aogonek Umacron -50
+KPX Aogonek Uogonek -50
+KPX Aogonek Uring -50
+KPX Aogonek V -145
+KPX Aogonek W -130
+KPX Aogonek Y -100
+KPX Aogonek Yacute -100
+KPX Aogonek Ydieresis -100
+KPX Aogonek p -25
+KPX Aogonek quoteright -74
+KPX Aogonek u -50
+KPX Aogonek uacute -50
+KPX Aogonek ucircumflex -50
+KPX Aogonek udieresis -50
+KPX Aogonek ugrave -50
+KPX Aogonek uhungarumlaut -50
+KPX Aogonek umacron -50
+KPX Aogonek uogonek -50
+KPX Aogonek uring -50
+KPX Aogonek v -100
+KPX Aogonek w -90
+KPX Aogonek y -34
+KPX Aogonek yacute -34
+KPX Aogonek ydieresis -34
+KPX Aring C -55
+KPX Aring Cacute -55
+KPX Aring Ccaron -55
+KPX Aring Ccedilla -55
+KPX Aring G -55
+KPX Aring Gbreve -55
+KPX Aring Gcommaaccent -55
+KPX Aring O -45
+KPX Aring Oacute -45
+KPX Aring Ocircumflex -45
+KPX Aring Odieresis -45
+KPX Aring Ograve -45
+KPX Aring Ohungarumlaut -45
+KPX Aring Omacron -45
+KPX Aring Oslash -45
+KPX Aring Otilde -45
+KPX Aring Q -45
+KPX Aring T -95
+KPX Aring Tcaron -95
+KPX Aring Tcommaaccent -95
+KPX Aring U -50
+KPX Aring Uacute -50
+KPX Aring Ucircumflex -50
+KPX Aring Udieresis -50
+KPX Aring Ugrave -50
+KPX Aring Uhungarumlaut -50
+KPX Aring Umacron -50
+KPX Aring Uogonek -50
+KPX Aring Uring -50
+KPX Aring V -145
+KPX Aring W -130
+KPX Aring Y -100
+KPX Aring Yacute -100
+KPX Aring Ydieresis -100
+KPX Aring p -25
+KPX Aring quoteright -74
+KPX Aring u -50
+KPX Aring uacute -50
+KPX Aring ucircumflex -50
+KPX Aring udieresis -50
+KPX Aring ugrave -50
+KPX Aring uhungarumlaut -50
+KPX Aring umacron -50
+KPX Aring uogonek -50
+KPX Aring uring -50
+KPX Aring v -100
+KPX Aring w -90
+KPX Aring y -74
+KPX Aring yacute -74
+KPX Aring ydieresis -74
+KPX Atilde C -55
+KPX Atilde Cacute -55
+KPX Atilde Ccaron -55
+KPX Atilde Ccedilla -55
+KPX Atilde G -55
+KPX Atilde Gbreve -55
+KPX Atilde Gcommaaccent -55
+KPX Atilde O -45
+KPX Atilde Oacute -45
+KPX Atilde Ocircumflex -45
+KPX Atilde Odieresis -45
+KPX Atilde Ograve -45
+KPX Atilde Ohungarumlaut -45
+KPX Atilde Omacron -45
+KPX Atilde Oslash -45
+KPX Atilde Otilde -45
+KPX Atilde Q -45
+KPX Atilde T -95
+KPX Atilde Tcaron -95
+KPX Atilde Tcommaaccent -95
+KPX Atilde U -50
+KPX Atilde Uacute -50
+KPX Atilde Ucircumflex -50
+KPX Atilde Udieresis -50
+KPX Atilde Ugrave -50
+KPX Atilde Uhungarumlaut -50
+KPX Atilde Umacron -50
+KPX Atilde Uogonek -50
+KPX Atilde Uring -50
+KPX Atilde V -145
+KPX Atilde W -130
+KPX Atilde Y -100
+KPX Atilde Yacute -100
+KPX Atilde Ydieresis -100
+KPX Atilde p -25
+KPX Atilde quoteright -74
+KPX Atilde u -50
+KPX Atilde uacute -50
+KPX Atilde ucircumflex -50
+KPX Atilde udieresis -50
+KPX Atilde ugrave -50
+KPX Atilde uhungarumlaut -50
+KPX Atilde umacron -50
+KPX Atilde uogonek -50
+KPX Atilde uring -50
+KPX Atilde v -100
+KPX Atilde w -90
+KPX Atilde y -74
+KPX Atilde yacute -74
+KPX Atilde ydieresis -74
+KPX B A -30
+KPX B Aacute -30
+KPX B Abreve -30
+KPX B Acircumflex -30
+KPX B Adieresis -30
+KPX B Agrave -30
+KPX B Amacron -30
+KPX B Aogonek -30
+KPX B Aring -30
+KPX B Atilde -30
+KPX B U -10
+KPX B Uacute -10
+KPX B Ucircumflex -10
+KPX B Udieresis -10
+KPX B Ugrave -10
+KPX B Uhungarumlaut -10
+KPX B Umacron -10
+KPX B Uogonek -10
+KPX B Uring -10
+KPX D A -35
+KPX D Aacute -35
+KPX D Abreve -35
+KPX D Acircumflex -35
+KPX D Adieresis -35
+KPX D Agrave -35
+KPX D Amacron -35
+KPX D Aogonek -35
+KPX D Aring -35
+KPX D Atilde -35
+KPX D V -40
+KPX D W -40
+KPX D Y -40
+KPX D Yacute -40
+KPX D Ydieresis -40
+KPX D period -20
+KPX Dcaron A -35
+KPX Dcaron Aacute -35
+KPX Dcaron Abreve -35
+KPX Dcaron Acircumflex -35
+KPX Dcaron Adieresis -35
+KPX Dcaron Agrave -35
+KPX Dcaron Amacron -35
+KPX Dcaron Aogonek -35
+KPX Dcaron Aring -35
+KPX Dcaron Atilde -35
+KPX Dcaron V -40
+KPX Dcaron W -40
+KPX Dcaron Y -40
+KPX Dcaron Yacute -40
+KPX Dcaron Ydieresis -40
+KPX Dcaron period -20
+KPX Dcroat A -35
+KPX Dcroat Aacute -35
+KPX Dcroat Abreve -35
+KPX Dcroat Acircumflex -35
+KPX Dcroat Adieresis -35
+KPX Dcroat Agrave -35
+KPX Dcroat Amacron -35
+KPX Dcroat Aogonek -35
+KPX Dcroat Aring -35
+KPX Dcroat Atilde -35
+KPX Dcroat V -40
+KPX Dcroat W -40
+KPX Dcroat Y -40
+KPX Dcroat Yacute -40
+KPX Dcroat Ydieresis -40
+KPX Dcroat period -20
+KPX F A -90
+KPX F Aacute -90
+KPX F Abreve -90
+KPX F Acircumflex -90
+KPX F Adieresis -90
+KPX F Agrave -90
+KPX F Amacron -90
+KPX F Aogonek -90
+KPX F Aring -90
+KPX F Atilde -90
+KPX F a -25
+KPX F aacute -25
+KPX F abreve -25
+KPX F acircumflex -25
+KPX F adieresis -25
+KPX F agrave -25
+KPX F amacron -25
+KPX F aogonek -25
+KPX F aring -25
+KPX F atilde -25
+KPX F comma -92
+KPX F e -25
+KPX F eacute -25
+KPX F ecaron -25
+KPX F ecircumflex -25
+KPX F edieresis -25
+KPX F edotaccent -25
+KPX F egrave -25
+KPX F emacron -25
+KPX F eogonek -25
+KPX F o -25
+KPX F oacute -25
+KPX F ocircumflex -25
+KPX F odieresis -25
+KPX F ograve -25
+KPX F ohungarumlaut -25
+KPX F omacron -25
+KPX F oslash -25
+KPX F otilde -25
+KPX F period -110
+KPX J A -30
+KPX J Aacute -30
+KPX J Abreve -30
+KPX J Acircumflex -30
+KPX J Adieresis -30
+KPX J Agrave -30
+KPX J Amacron -30
+KPX J Aogonek -30
+KPX J Aring -30
+KPX J Atilde -30
+KPX J a -15
+KPX J aacute -15
+KPX J abreve -15
+KPX J acircumflex -15
+KPX J adieresis -15
+KPX J agrave -15
+KPX J amacron -15
+KPX J aogonek -15
+KPX J aring -15
+KPX J atilde -15
+KPX J e -15
+KPX J eacute -15
+KPX J ecaron -15
+KPX J ecircumflex -15
+KPX J edieresis -15
+KPX J edotaccent -15
+KPX J egrave -15
+KPX J emacron -15
+KPX J eogonek -15
+KPX J o -15
+KPX J oacute -15
+KPX J ocircumflex -15
+KPX J odieresis -15
+KPX J ograve -15
+KPX J ohungarumlaut -15
+KPX J omacron -15
+KPX J oslash -15
+KPX J otilde -15
+KPX J period -20
+KPX J u -15
+KPX J uacute -15
+KPX J ucircumflex -15
+KPX J udieresis -15
+KPX J ugrave -15
+KPX J uhungarumlaut -15
+KPX J umacron -15
+KPX J uogonek -15
+KPX J uring -15
+KPX K O -30
+KPX K Oacute -30
+KPX K Ocircumflex -30
+KPX K Odieresis -30
+KPX K Ograve -30
+KPX K Ohungarumlaut -30
+KPX K Omacron -30
+KPX K Oslash -30
+KPX K Otilde -30
+KPX K e -25
+KPX K eacute -25
+KPX K ecaron -25
+KPX K ecircumflex -25
+KPX K edieresis -25
+KPX K edotaccent -25
+KPX K egrave -25
+KPX K emacron -25
+KPX K eogonek -25
+KPX K o -25
+KPX K oacute -25
+KPX K ocircumflex -25
+KPX K odieresis -25
+KPX K ograve -25
+KPX K ohungarumlaut -25
+KPX K omacron -25
+KPX K oslash -25
+KPX K otilde -25
+KPX K u -15
+KPX K uacute -15
+KPX K ucircumflex -15
+KPX K udieresis -15
+KPX K ugrave -15
+KPX K uhungarumlaut -15
+KPX K umacron -15
+KPX K uogonek -15
+KPX K uring -15
+KPX K y -45
+KPX K yacute -45
+KPX K ydieresis -45
+KPX Kcommaaccent O -30
+KPX Kcommaaccent Oacute -30
+KPX Kcommaaccent Ocircumflex -30
+KPX Kcommaaccent Odieresis -30
+KPX Kcommaaccent Ograve -30
+KPX Kcommaaccent Ohungarumlaut -30
+KPX Kcommaaccent Omacron -30
+KPX Kcommaaccent Oslash -30
+KPX Kcommaaccent Otilde -30
+KPX Kcommaaccent e -25
+KPX Kcommaaccent eacute -25
+KPX Kcommaaccent ecaron -25
+KPX Kcommaaccent ecircumflex -25
+KPX Kcommaaccent edieresis -25
+KPX Kcommaaccent edotaccent -25
+KPX Kcommaaccent egrave -25
+KPX Kcommaaccent emacron -25
+KPX Kcommaaccent eogonek -25
+KPX Kcommaaccent o -25
+KPX Kcommaaccent oacute -25
+KPX Kcommaaccent ocircumflex -25
+KPX Kcommaaccent odieresis -25
+KPX Kcommaaccent ograve -25
+KPX Kcommaaccent ohungarumlaut -25
+KPX Kcommaaccent omacron -25
+KPX Kcommaaccent oslash -25
+KPX Kcommaaccent otilde -25
+KPX Kcommaaccent u -15
+KPX Kcommaaccent uacute -15
+KPX Kcommaaccent ucircumflex -15
+KPX Kcommaaccent udieresis -15
+KPX Kcommaaccent ugrave -15
+KPX Kcommaaccent uhungarumlaut -15
+KPX Kcommaaccent umacron -15
+KPX Kcommaaccent uogonek -15
+KPX Kcommaaccent uring -15
+KPX Kcommaaccent y -45
+KPX Kcommaaccent yacute -45
+KPX Kcommaaccent ydieresis -45
+KPX L T -92
+KPX L Tcaron -92
+KPX L Tcommaaccent -92
+KPX L V -92
+KPX L W -92
+KPX L Y -92
+KPX L Yacute -92
+KPX L Ydieresis -92
+KPX L quotedblright -20
+KPX L quoteright -110
+KPX L y -55
+KPX L yacute -55
+KPX L ydieresis -55
+KPX Lacute T -92
+KPX Lacute Tcaron -92
+KPX Lacute Tcommaaccent -92
+KPX Lacute V -92
+KPX Lacute W -92
+KPX Lacute Y -92
+KPX Lacute Yacute -92
+KPX Lacute Ydieresis -92
+KPX Lacute quotedblright -20
+KPX Lacute quoteright -110
+KPX Lacute y -55
+KPX Lacute yacute -55
+KPX Lacute ydieresis -55
+KPX Lcommaaccent T -92
+KPX Lcommaaccent Tcaron -92
+KPX Lcommaaccent Tcommaaccent -92
+KPX Lcommaaccent V -92
+KPX Lcommaaccent W -92
+KPX Lcommaaccent Y -92
+KPX Lcommaaccent Yacute -92
+KPX Lcommaaccent Ydieresis -92
+KPX Lcommaaccent quotedblright -20
+KPX Lcommaaccent quoteright -110
+KPX Lcommaaccent y -55
+KPX Lcommaaccent yacute -55
+KPX Lcommaaccent ydieresis -55
+KPX Lslash T -92
+KPX Lslash Tcaron -92
+KPX Lslash Tcommaaccent -92
+KPX Lslash V -92
+KPX Lslash W -92
+KPX Lslash Y -92
+KPX Lslash Yacute -92
+KPX Lslash Ydieresis -92
+KPX Lslash quotedblright -20
+KPX Lslash quoteright -110
+KPX Lslash y -55
+KPX Lslash yacute -55
+KPX Lslash ydieresis -55
+KPX N A -20
+KPX N Aacute -20
+KPX N Abreve -20
+KPX N Acircumflex -20
+KPX N Adieresis -20
+KPX N Agrave -20
+KPX N Amacron -20
+KPX N Aogonek -20
+KPX N Aring -20
+KPX N Atilde -20
+KPX Nacute A -20
+KPX Nacute Aacute -20
+KPX Nacute Abreve -20
+KPX Nacute Acircumflex -20
+KPX Nacute Adieresis -20
+KPX Nacute Agrave -20
+KPX Nacute Amacron -20
+KPX Nacute Aogonek -20
+KPX Nacute Aring -20
+KPX Nacute Atilde -20
+KPX Ncaron A -20
+KPX Ncaron Aacute -20
+KPX Ncaron Abreve -20
+KPX Ncaron Acircumflex -20
+KPX Ncaron Adieresis -20
+KPX Ncaron Agrave -20
+KPX Ncaron Amacron -20
+KPX Ncaron Aogonek -20
+KPX Ncaron Aring -20
+KPX Ncaron Atilde -20
+KPX Ncommaaccent A -20
+KPX Ncommaaccent Aacute -20
+KPX Ncommaaccent Abreve -20
+KPX Ncommaaccent Acircumflex -20
+KPX Ncommaaccent Adieresis -20
+KPX Ncommaaccent Agrave -20
+KPX Ncommaaccent Amacron -20
+KPX Ncommaaccent Aogonek -20
+KPX Ncommaaccent Aring -20
+KPX Ncommaaccent Atilde -20
+KPX Ntilde A -20
+KPX Ntilde Aacute -20
+KPX Ntilde Abreve -20
+KPX Ntilde Acircumflex -20
+KPX Ntilde Adieresis -20
+KPX Ntilde Agrave -20
+KPX Ntilde Amacron -20
+KPX Ntilde Aogonek -20
+KPX Ntilde Aring -20
+KPX Ntilde Atilde -20
+KPX O A -40
+KPX O Aacute -40
+KPX O Abreve -40
+KPX O Acircumflex -40
+KPX O Adieresis -40
+KPX O Agrave -40
+KPX O Amacron -40
+KPX O Aogonek -40
+KPX O Aring -40
+KPX O Atilde -40
+KPX O T -40
+KPX O Tcaron -40
+KPX O Tcommaaccent -40
+KPX O V -50
+KPX O W -50
+KPX O X -40
+KPX O Y -50
+KPX O Yacute -50
+KPX O Ydieresis -50
+KPX Oacute A -40
+KPX Oacute Aacute -40
+KPX Oacute Abreve -40
+KPX Oacute Acircumflex -40
+KPX Oacute Adieresis -40
+KPX Oacute Agrave -40
+KPX Oacute Amacron -40
+KPX Oacute Aogonek -40
+KPX Oacute Aring -40
+KPX Oacute Atilde -40
+KPX Oacute T -40
+KPX Oacute Tcaron -40
+KPX Oacute Tcommaaccent -40
+KPX Oacute V -50
+KPX Oacute W -50
+KPX Oacute X -40
+KPX Oacute Y -50
+KPX Oacute Yacute -50
+KPX Oacute Ydieresis -50
+KPX Ocircumflex A -40
+KPX Ocircumflex Aacute -40
+KPX Ocircumflex Abreve -40
+KPX Ocircumflex Acircumflex -40
+KPX Ocircumflex Adieresis -40
+KPX Ocircumflex Agrave -40
+KPX Ocircumflex Amacron -40
+KPX Ocircumflex Aogonek -40
+KPX Ocircumflex Aring -40
+KPX Ocircumflex Atilde -40
+KPX Ocircumflex T -40
+KPX Ocircumflex Tcaron -40
+KPX Ocircumflex Tcommaaccent -40
+KPX Ocircumflex V -50
+KPX Ocircumflex W -50
+KPX Ocircumflex X -40
+KPX Ocircumflex Y -50
+KPX Ocircumflex Yacute -50
+KPX Ocircumflex Ydieresis -50
+KPX Odieresis A -40
+KPX Odieresis Aacute -40
+KPX Odieresis Abreve -40
+KPX Odieresis Acircumflex -40
+KPX Odieresis Adieresis -40
+KPX Odieresis Agrave -40
+KPX Odieresis Amacron -40
+KPX Odieresis Aogonek -40
+KPX Odieresis Aring -40
+KPX Odieresis Atilde -40
+KPX Odieresis T -40
+KPX Odieresis Tcaron -40
+KPX Odieresis Tcommaaccent -40
+KPX Odieresis V -50
+KPX Odieresis W -50
+KPX Odieresis X -40
+KPX Odieresis Y -50
+KPX Odieresis Yacute -50
+KPX Odieresis Ydieresis -50
+KPX Ograve A -40
+KPX Ograve Aacute -40
+KPX Ograve Abreve -40
+KPX Ograve Acircumflex -40
+KPX Ograve Adieresis -40
+KPX Ograve Agrave -40
+KPX Ograve Amacron -40
+KPX Ograve Aogonek -40
+KPX Ograve Aring -40
+KPX Ograve Atilde -40
+KPX Ograve T -40
+KPX Ograve Tcaron -40
+KPX Ograve Tcommaaccent -40
+KPX Ograve V -50
+KPX Ograve W -50
+KPX Ograve X -40
+KPX Ograve Y -50
+KPX Ograve Yacute -50
+KPX Ograve Ydieresis -50
+KPX Ohungarumlaut A -40
+KPX Ohungarumlaut Aacute -40
+KPX Ohungarumlaut Abreve -40
+KPX Ohungarumlaut Acircumflex -40
+KPX Ohungarumlaut Adieresis -40
+KPX Ohungarumlaut Agrave -40
+KPX Ohungarumlaut Amacron -40
+KPX Ohungarumlaut Aogonek -40
+KPX Ohungarumlaut Aring -40
+KPX Ohungarumlaut Atilde -40
+KPX Ohungarumlaut T -40
+KPX Ohungarumlaut Tcaron -40
+KPX Ohungarumlaut Tcommaaccent -40
+KPX Ohungarumlaut V -50
+KPX Ohungarumlaut W -50
+KPX Ohungarumlaut X -40
+KPX Ohungarumlaut Y -50
+KPX Ohungarumlaut Yacute -50
+KPX Ohungarumlaut Ydieresis -50
+KPX Omacron A -40
+KPX Omacron Aacute -40
+KPX Omacron Abreve -40
+KPX Omacron Acircumflex -40
+KPX Omacron Adieresis -40
+KPX Omacron Agrave -40
+KPX Omacron Amacron -40
+KPX Omacron Aogonek -40
+KPX Omacron Aring -40
+KPX Omacron Atilde -40
+KPX Omacron T -40
+KPX Omacron Tcaron -40
+KPX Omacron Tcommaaccent -40
+KPX Omacron V -50
+KPX Omacron W -50
+KPX Omacron X -40
+KPX Omacron Y -50
+KPX Omacron Yacute -50
+KPX Omacron Ydieresis -50
+KPX Oslash A -40
+KPX Oslash Aacute -40
+KPX Oslash Abreve -40
+KPX Oslash Acircumflex -40
+KPX Oslash Adieresis -40
+KPX Oslash Agrave -40
+KPX Oslash Amacron -40
+KPX Oslash Aogonek -40
+KPX Oslash Aring -40
+KPX Oslash Atilde -40
+KPX Oslash T -40
+KPX Oslash Tcaron -40
+KPX Oslash Tcommaaccent -40
+KPX Oslash V -50
+KPX Oslash W -50
+KPX Oslash X -40
+KPX Oslash Y -50
+KPX Oslash Yacute -50
+KPX Oslash Ydieresis -50
+KPX Otilde A -40
+KPX Otilde Aacute -40
+KPX Otilde Abreve -40
+KPX Otilde Acircumflex -40
+KPX Otilde Adieresis -40
+KPX Otilde Agrave -40
+KPX Otilde Amacron -40
+KPX Otilde Aogonek -40
+KPX Otilde Aring -40
+KPX Otilde Atilde -40
+KPX Otilde T -40
+KPX Otilde Tcaron -40
+KPX Otilde Tcommaaccent -40
+KPX Otilde V -50
+KPX Otilde W -50
+KPX Otilde X -40
+KPX Otilde Y -50
+KPX Otilde Yacute -50
+KPX Otilde Ydieresis -50
+KPX P A -74
+KPX P Aacute -74
+KPX P Abreve -74
+KPX P Acircumflex -74
+KPX P Adieresis -74
+KPX P Agrave -74
+KPX P Amacron -74
+KPX P Aogonek -74
+KPX P Aring -74
+KPX P Atilde -74
+KPX P a -10
+KPX P aacute -10
+KPX P abreve -10
+KPX P acircumflex -10
+KPX P adieresis -10
+KPX P agrave -10
+KPX P amacron -10
+KPX P aogonek -10
+KPX P aring -10
+KPX P atilde -10
+KPX P comma -92
+KPX P e -20
+KPX P eacute -20
+KPX P ecaron -20
+KPX P ecircumflex -20
+KPX P edieresis -20
+KPX P edotaccent -20
+KPX P egrave -20
+KPX P emacron -20
+KPX P eogonek -20
+KPX P o -20
+KPX P oacute -20
+KPX P ocircumflex -20
+KPX P odieresis -20
+KPX P ograve -20
+KPX P ohungarumlaut -20
+KPX P omacron -20
+KPX P oslash -20
+KPX P otilde -20
+KPX P period -110
+KPX Q U -10
+KPX Q Uacute -10
+KPX Q Ucircumflex -10
+KPX Q Udieresis -10
+KPX Q Ugrave -10
+KPX Q Uhungarumlaut -10
+KPX Q Umacron -10
+KPX Q Uogonek -10
+KPX Q Uring -10
+KPX Q period -20
+KPX R O -30
+KPX R Oacute -30
+KPX R Ocircumflex -30
+KPX R Odieresis -30
+KPX R Ograve -30
+KPX R Ohungarumlaut -30
+KPX R Omacron -30
+KPX R Oslash -30
+KPX R Otilde -30
+KPX R T -40
+KPX R Tcaron -40
+KPX R Tcommaaccent -40
+KPX R U -30
+KPX R Uacute -30
+KPX R Ucircumflex -30
+KPX R Udieresis -30
+KPX R Ugrave -30
+KPX R Uhungarumlaut -30
+KPX R Umacron -30
+KPX R Uogonek -30
+KPX R Uring -30
+KPX R V -55
+KPX R W -35
+KPX R Y -35
+KPX R Yacute -35
+KPX R Ydieresis -35
+KPX Racute O -30
+KPX Racute Oacute -30
+KPX Racute Ocircumflex -30
+KPX Racute Odieresis -30
+KPX Racute Ograve -30
+KPX Racute Ohungarumlaut -30
+KPX Racute Omacron -30
+KPX Racute Oslash -30
+KPX Racute Otilde -30
+KPX Racute T -40
+KPX Racute Tcaron -40
+KPX Racute Tcommaaccent -40
+KPX Racute U -30
+KPX Racute Uacute -30
+KPX Racute Ucircumflex -30
+KPX Racute Udieresis -30
+KPX Racute Ugrave -30
+KPX Racute Uhungarumlaut -30
+KPX Racute Umacron -30
+KPX Racute Uogonek -30
+KPX Racute Uring -30
+KPX Racute V -55
+KPX Racute W -35
+KPX Racute Y -35
+KPX Racute Yacute -35
+KPX Racute Ydieresis -35
+KPX Rcaron O -30
+KPX Rcaron Oacute -30
+KPX Rcaron Ocircumflex -30
+KPX Rcaron Odieresis -30
+KPX Rcaron Ograve -30
+KPX Rcaron Ohungarumlaut -30
+KPX Rcaron Omacron -30
+KPX Rcaron Oslash -30
+KPX Rcaron Otilde -30
+KPX Rcaron T -40
+KPX Rcaron Tcaron -40
+KPX Rcaron Tcommaaccent -40
+KPX Rcaron U -30
+KPX Rcaron Uacute -30
+KPX Rcaron Ucircumflex -30
+KPX Rcaron Udieresis -30
+KPX Rcaron Ugrave -30
+KPX Rcaron Uhungarumlaut -30
+KPX Rcaron Umacron -30
+KPX Rcaron Uogonek -30
+KPX Rcaron Uring -30
+KPX Rcaron V -55
+KPX Rcaron W -35
+KPX Rcaron Y -35
+KPX Rcaron Yacute -35
+KPX Rcaron Ydieresis -35
+KPX Rcommaaccent O -30
+KPX Rcommaaccent Oacute -30
+KPX Rcommaaccent Ocircumflex -30
+KPX Rcommaaccent Odieresis -30
+KPX Rcommaaccent Ograve -30
+KPX Rcommaaccent Ohungarumlaut -30
+KPX Rcommaaccent Omacron -30
+KPX Rcommaaccent Oslash -30
+KPX Rcommaaccent Otilde -30
+KPX Rcommaaccent T -40
+KPX Rcommaaccent Tcaron -40
+KPX Rcommaaccent Tcommaaccent -40
+KPX Rcommaaccent U -30
+KPX Rcommaaccent Uacute -30
+KPX Rcommaaccent Ucircumflex -30
+KPX Rcommaaccent Udieresis -30
+KPX Rcommaaccent Ugrave -30
+KPX Rcommaaccent Uhungarumlaut -30
+KPX Rcommaaccent Umacron -30
+KPX Rcommaaccent Uogonek -30
+KPX Rcommaaccent Uring -30
+KPX Rcommaaccent V -55
+KPX Rcommaaccent W -35
+KPX Rcommaaccent Y -35
+KPX Rcommaaccent Yacute -35
+KPX Rcommaaccent Ydieresis -35
+KPX T A -90
+KPX T Aacute -90
+KPX T Abreve -90
+KPX T Acircumflex -90
+KPX T Adieresis -90
+KPX T Agrave -90
+KPX T Amacron -90
+KPX T Aogonek -90
+KPX T Aring -90
+KPX T Atilde -90
+KPX T O -18
+KPX T Oacute -18
+KPX T Ocircumflex -18
+KPX T Odieresis -18
+KPX T Ograve -18
+KPX T Ohungarumlaut -18
+KPX T Omacron -18
+KPX T Oslash -18
+KPX T Otilde -18
+KPX T a -92
+KPX T aacute -92
+KPX T abreve -52
+KPX T acircumflex -52
+KPX T adieresis -52
+KPX T agrave -52
+KPX T amacron -52
+KPX T aogonek -92
+KPX T aring -92
+KPX T atilde -52
+KPX T colon -74
+KPX T comma -74
+KPX T e -92
+KPX T eacute -92
+KPX T ecaron -92
+KPX T ecircumflex -92
+KPX T edieresis -52
+KPX T edotaccent -92
+KPX T egrave -52
+KPX T emacron -52
+KPX T eogonek -92
+KPX T hyphen -92
+KPX T i -18
+KPX T iacute -18
+KPX T iogonek -18
+KPX T o -92
+KPX T oacute -92
+KPX T ocircumflex -92
+KPX T odieresis -92
+KPX T ograve -92
+KPX T ohungarumlaut -92
+KPX T omacron -92
+KPX T oslash -92
+KPX T otilde -92
+KPX T period -90
+KPX T r -74
+KPX T racute -74
+KPX T rcaron -74
+KPX T rcommaaccent -74
+KPX T semicolon -74
+KPX T u -92
+KPX T uacute -92
+KPX T ucircumflex -92
+KPX T udieresis -92
+KPX T ugrave -92
+KPX T uhungarumlaut -92
+KPX T umacron -92
+KPX T uogonek -92
+KPX T uring -92
+KPX T w -74
+KPX T y -34
+KPX T yacute -34
+KPX T ydieresis -34
+KPX Tcaron A -90
+KPX Tcaron Aacute -90
+KPX Tcaron Abreve -90
+KPX Tcaron Acircumflex -90
+KPX Tcaron Adieresis -90
+KPX Tcaron Agrave -90
+KPX Tcaron Amacron -90
+KPX Tcaron Aogonek -90
+KPX Tcaron Aring -90
+KPX Tcaron Atilde -90
+KPX Tcaron O -18
+KPX Tcaron Oacute -18
+KPX Tcaron Ocircumflex -18
+KPX Tcaron Odieresis -18
+KPX Tcaron Ograve -18
+KPX Tcaron Ohungarumlaut -18
+KPX Tcaron Omacron -18
+KPX Tcaron Oslash -18
+KPX Tcaron Otilde -18
+KPX Tcaron a -92
+KPX Tcaron aacute -92
+KPX Tcaron abreve -52
+KPX Tcaron acircumflex -52
+KPX Tcaron adieresis -52
+KPX Tcaron agrave -52
+KPX Tcaron amacron -52
+KPX Tcaron aogonek -92
+KPX Tcaron aring -92
+KPX Tcaron atilde -52
+KPX Tcaron colon -74
+KPX Tcaron comma -74
+KPX Tcaron e -92
+KPX Tcaron eacute -92
+KPX Tcaron ecaron -92
+KPX Tcaron ecircumflex -92
+KPX Tcaron edieresis -52
+KPX Tcaron edotaccent -92
+KPX Tcaron egrave -52
+KPX Tcaron emacron -52
+KPX Tcaron eogonek -92
+KPX Tcaron hyphen -92
+KPX Tcaron i -18
+KPX Tcaron iacute -18
+KPX Tcaron iogonek -18
+KPX Tcaron o -92
+KPX Tcaron oacute -92
+KPX Tcaron ocircumflex -92
+KPX Tcaron odieresis -92
+KPX Tcaron ograve -92
+KPX Tcaron ohungarumlaut -92
+KPX Tcaron omacron -92
+KPX Tcaron oslash -92
+KPX Tcaron otilde -92
+KPX Tcaron period -90
+KPX Tcaron r -74
+KPX Tcaron racute -74
+KPX Tcaron rcaron -74
+KPX Tcaron rcommaaccent -74
+KPX Tcaron semicolon -74
+KPX Tcaron u -92
+KPX Tcaron uacute -92
+KPX Tcaron ucircumflex -92
+KPX Tcaron udieresis -92
+KPX Tcaron ugrave -92
+KPX Tcaron uhungarumlaut -92
+KPX Tcaron umacron -92
+KPX Tcaron uogonek -92
+KPX Tcaron uring -92
+KPX Tcaron w -74
+KPX Tcaron y -34
+KPX Tcaron yacute -34
+KPX Tcaron ydieresis -34
+KPX Tcommaaccent A -90
+KPX Tcommaaccent Aacute -90
+KPX Tcommaaccent Abreve -90
+KPX Tcommaaccent Acircumflex -90
+KPX Tcommaaccent Adieresis -90
+KPX Tcommaaccent Agrave -90
+KPX Tcommaaccent Amacron -90
+KPX Tcommaaccent Aogonek -90
+KPX Tcommaaccent Aring -90
+KPX Tcommaaccent Atilde -90
+KPX Tcommaaccent O -18
+KPX Tcommaaccent Oacute -18
+KPX Tcommaaccent Ocircumflex -18
+KPX Tcommaaccent Odieresis -18
+KPX Tcommaaccent Ograve -18
+KPX Tcommaaccent Ohungarumlaut -18
+KPX Tcommaaccent Omacron -18
+KPX Tcommaaccent Oslash -18
+KPX Tcommaaccent Otilde -18
+KPX Tcommaaccent a -92
+KPX Tcommaaccent aacute -92
+KPX Tcommaaccent abreve -52
+KPX Tcommaaccent acircumflex -52
+KPX Tcommaaccent adieresis -52
+KPX Tcommaaccent agrave -52
+KPX Tcommaaccent amacron -52
+KPX Tcommaaccent aogonek -92
+KPX Tcommaaccent aring -92
+KPX Tcommaaccent atilde -52
+KPX Tcommaaccent colon -74
+KPX Tcommaaccent comma -74
+KPX Tcommaaccent e -92
+KPX Tcommaaccent eacute -92
+KPX Tcommaaccent ecaron -92
+KPX Tcommaaccent ecircumflex -92
+KPX Tcommaaccent edieresis -52
+KPX Tcommaaccent edotaccent -92
+KPX Tcommaaccent egrave -52
+KPX Tcommaaccent emacron -52
+KPX Tcommaaccent eogonek -92
+KPX Tcommaaccent hyphen -92
+KPX Tcommaaccent i -18
+KPX Tcommaaccent iacute -18
+KPX Tcommaaccent iogonek -18
+KPX Tcommaaccent o -92
+KPX Tcommaaccent oacute -92
+KPX Tcommaaccent ocircumflex -92
+KPX Tcommaaccent odieresis -92
+KPX Tcommaaccent ograve -92
+KPX Tcommaaccent ohungarumlaut -92
+KPX Tcommaaccent omacron -92
+KPX Tcommaaccent oslash -92
+KPX Tcommaaccent otilde -92
+KPX Tcommaaccent period -90
+KPX Tcommaaccent r -74
+KPX Tcommaaccent racute -74
+KPX Tcommaaccent rcaron -74
+KPX Tcommaaccent rcommaaccent -74
+KPX Tcommaaccent semicolon -74
+KPX Tcommaaccent u -92
+KPX Tcommaaccent uacute -92
+KPX Tcommaaccent ucircumflex -92
+KPX Tcommaaccent udieresis -92
+KPX Tcommaaccent ugrave -92
+KPX Tcommaaccent uhungarumlaut -92
+KPX Tcommaaccent umacron -92
+KPX Tcommaaccent uogonek -92
+KPX Tcommaaccent uring -92
+KPX Tcommaaccent w -74
+KPX Tcommaaccent y -34
+KPX Tcommaaccent yacute -34
+KPX Tcommaaccent ydieresis -34
+KPX U A -60
+KPX U Aacute -60
+KPX U Abreve -60
+KPX U Acircumflex -60
+KPX U Adieresis -60
+KPX U Agrave -60
+KPX U Amacron -60
+KPX U Aogonek -60
+KPX U Aring -60
+KPX U Atilde -60
+KPX U comma -50
+KPX U period -50
+KPX Uacute A -60
+KPX Uacute Aacute -60
+KPX Uacute Abreve -60
+KPX Uacute Acircumflex -60
+KPX Uacute Adieresis -60
+KPX Uacute Agrave -60
+KPX Uacute Amacron -60
+KPX Uacute Aogonek -60
+KPX Uacute Aring -60
+KPX Uacute Atilde -60
+KPX Uacute comma -50
+KPX Uacute period -50
+KPX Ucircumflex A -60
+KPX Ucircumflex Aacute -60
+KPX Ucircumflex Abreve -60
+KPX Ucircumflex Acircumflex -60
+KPX Ucircumflex Adieresis -60
+KPX Ucircumflex Agrave -60
+KPX Ucircumflex Amacron -60
+KPX Ucircumflex Aogonek -60
+KPX Ucircumflex Aring -60
+KPX Ucircumflex Atilde -60
+KPX Ucircumflex comma -50
+KPX Ucircumflex period -50
+KPX Udieresis A -60
+KPX Udieresis Aacute -60
+KPX Udieresis Abreve -60
+KPX Udieresis Acircumflex -60
+KPX Udieresis Adieresis -60
+KPX Udieresis Agrave -60
+KPX Udieresis Amacron -60
+KPX Udieresis Aogonek -60
+KPX Udieresis Aring -60
+KPX Udieresis Atilde -60
+KPX Udieresis comma -50
+KPX Udieresis period -50
+KPX Ugrave A -60
+KPX Ugrave Aacute -60
+KPX Ugrave Abreve -60
+KPX Ugrave Acircumflex -60
+KPX Ugrave Adieresis -60
+KPX Ugrave Agrave -60
+KPX Ugrave Amacron -60
+KPX Ugrave Aogonek -60
+KPX Ugrave Aring -60
+KPX Ugrave Atilde -60
+KPX Ugrave comma -50
+KPX Ugrave period -50
+KPX Uhungarumlaut A -60
+KPX Uhungarumlaut Aacute -60
+KPX Uhungarumlaut Abreve -60
+KPX Uhungarumlaut Acircumflex -60
+KPX Uhungarumlaut Adieresis -60
+KPX Uhungarumlaut Agrave -60
+KPX Uhungarumlaut Amacron -60
+KPX Uhungarumlaut Aogonek -60
+KPX Uhungarumlaut Aring -60
+KPX Uhungarumlaut Atilde -60
+KPX Uhungarumlaut comma -50
+KPX Uhungarumlaut period -50
+KPX Umacron A -60
+KPX Umacron Aacute -60
+KPX Umacron Abreve -60
+KPX Umacron Acircumflex -60
+KPX Umacron Adieresis -60
+KPX Umacron Agrave -60
+KPX Umacron Amacron -60
+KPX Umacron Aogonek -60
+KPX Umacron Aring -60
+KPX Umacron Atilde -60
+KPX Umacron comma -50
+KPX Umacron period -50
+KPX Uogonek A -60
+KPX Uogonek Aacute -60
+KPX Uogonek Abreve -60
+KPX Uogonek Acircumflex -60
+KPX Uogonek Adieresis -60
+KPX Uogonek Agrave -60
+KPX Uogonek Amacron -60
+KPX Uogonek Aogonek -60
+KPX Uogonek Aring -60
+KPX Uogonek Atilde -60
+KPX Uogonek comma -50
+KPX Uogonek period -50
+KPX Uring A -60
+KPX Uring Aacute -60
+KPX Uring Abreve -60
+KPX Uring Acircumflex -60
+KPX Uring Adieresis -60
+KPX Uring Agrave -60
+KPX Uring Amacron -60
+KPX Uring Aogonek -60
+KPX Uring Aring -60
+KPX Uring Atilde -60
+KPX Uring comma -50
+KPX Uring period -50
+KPX V A -135
+KPX V Aacute -135
+KPX V Abreve -135
+KPX V Acircumflex -135
+KPX V Adieresis -135
+KPX V Agrave -135
+KPX V Amacron -135
+KPX V Aogonek -135
+KPX V Aring -135
+KPX V Atilde -135
+KPX V G -30
+KPX V Gbreve -30
+KPX V Gcommaaccent -30
+KPX V O -45
+KPX V Oacute -45
+KPX V Ocircumflex -45
+KPX V Odieresis -45
+KPX V Ograve -45
+KPX V Ohungarumlaut -45
+KPX V Omacron -45
+KPX V Oslash -45
+KPX V Otilde -45
+KPX V a -92
+KPX V aacute -92
+KPX V abreve -92
+KPX V acircumflex -92
+KPX V adieresis -92
+KPX V agrave -92
+KPX V amacron -92
+KPX V aogonek -92
+KPX V aring -92
+KPX V atilde -92
+KPX V colon -92
+KPX V comma -129
+KPX V e -100
+KPX V eacute -100
+KPX V ecaron -100
+KPX V ecircumflex -100
+KPX V edieresis -100
+KPX V edotaccent -100
+KPX V egrave -100
+KPX V emacron -100
+KPX V eogonek -100
+KPX V hyphen -74
+KPX V i -37
+KPX V iacute -37
+KPX V icircumflex -37
+KPX V idieresis -37
+KPX V igrave -37
+KPX V imacron -37
+KPX V iogonek -37
+KPX V o -100
+KPX V oacute -100
+KPX V ocircumflex -100
+KPX V odieresis -100
+KPX V ograve -100
+KPX V ohungarumlaut -100
+KPX V omacron -100
+KPX V oslash -100
+KPX V otilde -100
+KPX V period -145
+KPX V semicolon -92
+KPX V u -92
+KPX V uacute -92
+KPX V ucircumflex -92
+KPX V udieresis -92
+KPX V ugrave -92
+KPX V uhungarumlaut -92
+KPX V umacron -92
+KPX V uogonek -92
+KPX V uring -92
+KPX W A -120
+KPX W Aacute -120
+KPX W Abreve -120
+KPX W Acircumflex -120
+KPX W Adieresis -120
+KPX W Agrave -120
+KPX W Amacron -120
+KPX W Aogonek -120
+KPX W Aring -120
+KPX W Atilde -120
+KPX W O -10
+KPX W Oacute -10
+KPX W Ocircumflex -10
+KPX W Odieresis -10
+KPX W Ograve -10
+KPX W Ohungarumlaut -10
+KPX W Omacron -10
+KPX W Oslash -10
+KPX W Otilde -10
+KPX W a -65
+KPX W aacute -65
+KPX W abreve -65
+KPX W acircumflex -65
+KPX W adieresis -65
+KPX W agrave -65
+KPX W amacron -65
+KPX W aogonek -65
+KPX W aring -65
+KPX W atilde -65
+KPX W colon -55
+KPX W comma -92
+KPX W e -65
+KPX W eacute -65
+KPX W ecaron -65
+KPX W ecircumflex -65
+KPX W edieresis -65
+KPX W edotaccent -65
+KPX W egrave -65
+KPX W emacron -65
+KPX W eogonek -65
+KPX W hyphen -37
+KPX W i -18
+KPX W iacute -18
+KPX W iogonek -18
+KPX W o -75
+KPX W oacute -75
+KPX W ocircumflex -75
+KPX W odieresis -75
+KPX W ograve -75
+KPX W ohungarumlaut -75
+KPX W omacron -75
+KPX W oslash -75
+KPX W otilde -75
+KPX W period -92
+KPX W semicolon -55
+KPX W u -50
+KPX W uacute -50
+KPX W ucircumflex -50
+KPX W udieresis -50
+KPX W ugrave -50
+KPX W uhungarumlaut -50
+KPX W umacron -50
+KPX W uogonek -50
+KPX W uring -50
+KPX W y -60
+KPX W yacute -60
+KPX W ydieresis -60
+KPX Y A -110
+KPX Y Aacute -110
+KPX Y Abreve -110
+KPX Y Acircumflex -110
+KPX Y Adieresis -110
+KPX Y Agrave -110
+KPX Y Amacron -110
+KPX Y Aogonek -110
+KPX Y Aring -110
+KPX Y Atilde -110
+KPX Y O -35
+KPX Y Oacute -35
+KPX Y Ocircumflex -35
+KPX Y Odieresis -35
+KPX Y Ograve -35
+KPX Y Ohungarumlaut -35
+KPX Y Omacron -35
+KPX Y Oslash -35
+KPX Y Otilde -35
+KPX Y a -85
+KPX Y aacute -85
+KPX Y abreve -85
+KPX Y acircumflex -85
+KPX Y adieresis -85
+KPX Y agrave -85
+KPX Y amacron -85
+KPX Y aogonek -85
+KPX Y aring -85
+KPX Y atilde -85
+KPX Y colon -92
+KPX Y comma -92
+KPX Y e -111
+KPX Y eacute -111
+KPX Y ecaron -111
+KPX Y ecircumflex -111
+KPX Y edieresis -71
+KPX Y edotaccent -111
+KPX Y egrave -71
+KPX Y emacron -71
+KPX Y eogonek -111
+KPX Y hyphen -92
+KPX Y i -37
+KPX Y iacute -37
+KPX Y iogonek -37
+KPX Y o -111
+KPX Y oacute -111
+KPX Y ocircumflex -111
+KPX Y odieresis -111
+KPX Y ograve -111
+KPX Y ohungarumlaut -111
+KPX Y omacron -111
+KPX Y oslash -111
+KPX Y otilde -111
+KPX Y period -92
+KPX Y semicolon -92
+KPX Y u -92
+KPX Y uacute -92
+KPX Y ucircumflex -92
+KPX Y udieresis -92
+KPX Y ugrave -92
+KPX Y uhungarumlaut -92
+KPX Y umacron -92
+KPX Y uogonek -92
+KPX Y uring -92
+KPX Yacute A -110
+KPX Yacute Aacute -110
+KPX Yacute Abreve -110
+KPX Yacute Acircumflex -110
+KPX Yacute Adieresis -110
+KPX Yacute Agrave -110
+KPX Yacute Amacron -110
+KPX Yacute Aogonek -110
+KPX Yacute Aring -110
+KPX Yacute Atilde -110
+KPX Yacute O -35
+KPX Yacute Oacute -35
+KPX Yacute Ocircumflex -35
+KPX Yacute Odieresis -35
+KPX Yacute Ograve -35
+KPX Yacute Ohungarumlaut -35
+KPX Yacute Omacron -35
+KPX Yacute Oslash -35
+KPX Yacute Otilde -35
+KPX Yacute a -85
+KPX Yacute aacute -85
+KPX Yacute abreve -85
+KPX Yacute acircumflex -85
+KPX Yacute adieresis -85
+KPX Yacute agrave -85
+KPX Yacute amacron -85
+KPX Yacute aogonek -85
+KPX Yacute aring -85
+KPX Yacute atilde -85
+KPX Yacute colon -92
+KPX Yacute comma -92
+KPX Yacute e -111
+KPX Yacute eacute -111
+KPX Yacute ecaron -111
+KPX Yacute ecircumflex -111
+KPX Yacute edieresis -71
+KPX Yacute edotaccent -111
+KPX Yacute egrave -71
+KPX Yacute emacron -71
+KPX Yacute eogonek -111
+KPX Yacute hyphen -92
+KPX Yacute i -37
+KPX Yacute iacute -37
+KPX Yacute iogonek -37
+KPX Yacute o -111
+KPX Yacute oacute -111
+KPX Yacute ocircumflex -111
+KPX Yacute odieresis -111
+KPX Yacute ograve -111
+KPX Yacute ohungarumlaut -111
+KPX Yacute omacron -111
+KPX Yacute oslash -111
+KPX Yacute otilde -111
+KPX Yacute period -92
+KPX Yacute semicolon -92
+KPX Yacute u -92
+KPX Yacute uacute -92
+KPX Yacute ucircumflex -92
+KPX Yacute udieresis -92
+KPX Yacute ugrave -92
+KPX Yacute uhungarumlaut -92
+KPX Yacute umacron -92
+KPX Yacute uogonek -92
+KPX Yacute uring -92
+KPX Ydieresis A -110
+KPX Ydieresis Aacute -110
+KPX Ydieresis Abreve -110
+KPX Ydieresis Acircumflex -110
+KPX Ydieresis Adieresis -110
+KPX Ydieresis Agrave -110
+KPX Ydieresis Amacron -110
+KPX Ydieresis Aogonek -110
+KPX Ydieresis Aring -110
+KPX Ydieresis Atilde -110
+KPX Ydieresis O -35
+KPX Ydieresis Oacute -35
+KPX Ydieresis Ocircumflex -35
+KPX Ydieresis Odieresis -35
+KPX Ydieresis Ograve -35
+KPX Ydieresis Ohungarumlaut -35
+KPX Ydieresis Omacron -35
+KPX Ydieresis Oslash -35
+KPX Ydieresis Otilde -35
+KPX Ydieresis a -85
+KPX Ydieresis aacute -85
+KPX Ydieresis abreve -85
+KPX Ydieresis acircumflex -85
+KPX Ydieresis adieresis -85
+KPX Ydieresis agrave -85
+KPX Ydieresis amacron -85
+KPX Ydieresis aogonek -85
+KPX Ydieresis aring -85
+KPX Ydieresis atilde -85
+KPX Ydieresis colon -92
+KPX Ydieresis comma -92
+KPX Ydieresis e -111
+KPX Ydieresis eacute -111
+KPX Ydieresis ecaron -111
+KPX Ydieresis ecircumflex -111
+KPX Ydieresis edieresis -71
+KPX Ydieresis edotaccent -111
+KPX Ydieresis egrave -71
+KPX Ydieresis emacron -71
+KPX Ydieresis eogonek -111
+KPX Ydieresis hyphen -92
+KPX Ydieresis i -37
+KPX Ydieresis iacute -37
+KPX Ydieresis iogonek -37
+KPX Ydieresis o -111
+KPX Ydieresis oacute -111
+KPX Ydieresis ocircumflex -111
+KPX Ydieresis odieresis -111
+KPX Ydieresis ograve -111
+KPX Ydieresis ohungarumlaut -111
+KPX Ydieresis omacron -111
+KPX Ydieresis oslash -111
+KPX Ydieresis otilde -111
+KPX Ydieresis period -92
+KPX Ydieresis semicolon -92
+KPX Ydieresis u -92
+KPX Ydieresis uacute -92
+KPX Ydieresis ucircumflex -92
+KPX Ydieresis udieresis -92
+KPX Ydieresis ugrave -92
+KPX Ydieresis uhungarumlaut -92
+KPX Ydieresis umacron -92
+KPX Ydieresis uogonek -92
+KPX Ydieresis uring -92
+KPX a v -25
+KPX aacute v -25
+KPX abreve v -25
+KPX acircumflex v -25
+KPX adieresis v -25
+KPX agrave v -25
+KPX amacron v -25
+KPX aogonek v -25
+KPX aring v -25
+KPX atilde v -25
+KPX b b -10
+KPX b period -40
+KPX b u -20
+KPX b uacute -20
+KPX b ucircumflex -20
+KPX b udieresis -20
+KPX b ugrave -20
+KPX b uhungarumlaut -20
+KPX b umacron -20
+KPX b uogonek -20
+KPX b uring -20
+KPX b v -15
+KPX comma quotedblright -45
+KPX comma quoteright -55
+KPX d w -15
+KPX dcroat w -15
+KPX e v -15
+KPX eacute v -15
+KPX ecaron v -15
+KPX ecircumflex v -15
+KPX edieresis v -15
+KPX edotaccent v -15
+KPX egrave v -15
+KPX emacron v -15
+KPX eogonek v -15
+KPX f comma -15
+KPX f dotlessi -35
+KPX f i -25
+KPX f o -25
+KPX f oacute -25
+KPX f ocircumflex -25
+KPX f odieresis -25
+KPX f ograve -25
+KPX f ohungarumlaut -25
+KPX f omacron -25
+KPX f oslash -25
+KPX f otilde -25
+KPX f period -15
+KPX f quotedblright 50
+KPX f quoteright 55
+KPX g period -15
+KPX gbreve period -15
+KPX gcommaaccent period -15
+KPX h y -15
+KPX h yacute -15
+KPX h ydieresis -15
+KPX i v -10
+KPX iacute v -10
+KPX icircumflex v -10
+KPX idieresis v -10
+KPX igrave v -10
+KPX imacron v -10
+KPX iogonek v -10
+KPX k e -10
+KPX k eacute -10
+KPX k ecaron -10
+KPX k ecircumflex -10
+KPX k edieresis -10
+KPX k edotaccent -10
+KPX k egrave -10
+KPX k emacron -10
+KPX k eogonek -10
+KPX k o -15
+KPX k oacute -15
+KPX k ocircumflex -15
+KPX k odieresis -15
+KPX k ograve -15
+KPX k ohungarumlaut -15
+KPX k omacron -15
+KPX k oslash -15
+KPX k otilde -15
+KPX k y -15
+KPX k yacute -15
+KPX k ydieresis -15
+KPX kcommaaccent e -10
+KPX kcommaaccent eacute -10
+KPX kcommaaccent ecaron -10
+KPX kcommaaccent ecircumflex -10
+KPX kcommaaccent edieresis -10
+KPX kcommaaccent edotaccent -10
+KPX kcommaaccent egrave -10
+KPX kcommaaccent emacron -10
+KPX kcommaaccent eogonek -10
+KPX kcommaaccent o -15
+KPX kcommaaccent oacute -15
+KPX kcommaaccent ocircumflex -15
+KPX kcommaaccent odieresis -15
+KPX kcommaaccent ograve -15
+KPX kcommaaccent ohungarumlaut -15
+KPX kcommaaccent omacron -15
+KPX kcommaaccent oslash -15
+KPX kcommaaccent otilde -15
+KPX kcommaaccent y -15
+KPX kcommaaccent yacute -15
+KPX kcommaaccent ydieresis -15
+KPX n v -40
+KPX nacute v -40
+KPX ncaron v -40
+KPX ncommaaccent v -40
+KPX ntilde v -40
+KPX o v -10
+KPX o w -10
+KPX oacute v -10
+KPX oacute w -10
+KPX ocircumflex v -10
+KPX ocircumflex w -10
+KPX odieresis v -10
+KPX odieresis w -10
+KPX ograve v -10
+KPX ograve w -10
+KPX ohungarumlaut v -10
+KPX ohungarumlaut w -10
+KPX omacron v -10
+KPX omacron w -10
+KPX oslash v -10
+KPX oslash w -10
+KPX otilde v -10
+KPX otilde w -10
+KPX period quotedblright -55
+KPX period quoteright -55
+KPX quotedblleft A -10
+KPX quotedblleft Aacute -10
+KPX quotedblleft Abreve -10
+KPX quotedblleft Acircumflex -10
+KPX quotedblleft Adieresis -10
+KPX quotedblleft Agrave -10
+KPX quotedblleft Amacron -10
+KPX quotedblleft Aogonek -10
+KPX quotedblleft Aring -10
+KPX quotedblleft Atilde -10
+KPX quoteleft A -10
+KPX quoteleft Aacute -10
+KPX quoteleft Abreve -10
+KPX quoteleft Acircumflex -10
+KPX quoteleft Adieresis -10
+KPX quoteleft Agrave -10
+KPX quoteleft Amacron -10
+KPX quoteleft Aogonek -10
+KPX quoteleft Aring -10
+KPX quoteleft Atilde -10
+KPX quoteleft quoteleft -63
+KPX quoteright d -20
+KPX quoteright dcroat -20
+KPX quoteright quoteright -63
+KPX quoteright r -20
+KPX quoteright racute -20
+KPX quoteright rcaron -20
+KPX quoteright rcommaaccent -20
+KPX quoteright s -37
+KPX quoteright sacute -37
+KPX quoteright scaron -37
+KPX quoteright scedilla -37
+KPX quoteright scommaaccent -37
+KPX quoteright space -74
+KPX quoteright v -20
+KPX r c -18
+KPX r cacute -18
+KPX r ccaron -18
+KPX r ccedilla -18
+KPX r comma -92
+KPX r e -18
+KPX r eacute -18
+KPX r ecaron -18
+KPX r ecircumflex -18
+KPX r edieresis -18
+KPX r edotaccent -18
+KPX r egrave -18
+KPX r emacron -18
+KPX r eogonek -18
+KPX r g -10
+KPX r gbreve -10
+KPX r gcommaaccent -10
+KPX r hyphen -37
+KPX r n -15
+KPX r nacute -15
+KPX r ncaron -15
+KPX r ncommaaccent -15
+KPX r ntilde -15
+KPX r o -18
+KPX r oacute -18
+KPX r ocircumflex -18
+KPX r odieresis -18
+KPX r ograve -18
+KPX r ohungarumlaut -18
+KPX r omacron -18
+KPX r oslash -18
+KPX r otilde -18
+KPX r p -10
+KPX r period -100
+KPX r q -18
+KPX r v -10
+KPX racute c -18
+KPX racute cacute -18
+KPX racute ccaron -18
+KPX racute ccedilla -18
+KPX racute comma -92
+KPX racute e -18
+KPX racute eacute -18
+KPX racute ecaron -18
+KPX racute ecircumflex -18
+KPX racute edieresis -18
+KPX racute edotaccent -18
+KPX racute egrave -18
+KPX racute emacron -18
+KPX racute eogonek -18
+KPX racute g -10
+KPX racute gbreve -10
+KPX racute gcommaaccent -10
+KPX racute hyphen -37
+KPX racute n -15
+KPX racute nacute -15
+KPX racute ncaron -15
+KPX racute ncommaaccent -15
+KPX racute ntilde -15
+KPX racute o -18
+KPX racute oacute -18
+KPX racute ocircumflex -18
+KPX racute odieresis -18
+KPX racute ograve -18
+KPX racute ohungarumlaut -18
+KPX racute omacron -18
+KPX racute oslash -18
+KPX racute otilde -18
+KPX racute p -10
+KPX racute period -100
+KPX racute q -18
+KPX racute v -10
+KPX rcaron c -18
+KPX rcaron cacute -18
+KPX rcaron ccaron -18
+KPX rcaron ccedilla -18
+KPX rcaron comma -92
+KPX rcaron e -18
+KPX rcaron eacute -18
+KPX rcaron ecaron -18
+KPX rcaron ecircumflex -18
+KPX rcaron edieresis -18
+KPX rcaron edotaccent -18
+KPX rcaron egrave -18
+KPX rcaron emacron -18
+KPX rcaron eogonek -18
+KPX rcaron g -10
+KPX rcaron gbreve -10
+KPX rcaron gcommaaccent -10
+KPX rcaron hyphen -37
+KPX rcaron n -15
+KPX rcaron nacute -15
+KPX rcaron ncaron -15
+KPX rcaron ncommaaccent -15
+KPX rcaron ntilde -15
+KPX rcaron o -18
+KPX rcaron oacute -18
+KPX rcaron ocircumflex -18
+KPX rcaron odieresis -18
+KPX rcaron ograve -18
+KPX rcaron ohungarumlaut -18
+KPX rcaron omacron -18
+KPX rcaron oslash -18
+KPX rcaron otilde -18
+KPX rcaron p -10
+KPX rcaron period -100
+KPX rcaron q -18
+KPX rcaron v -10
+KPX rcommaaccent c -18
+KPX rcommaaccent cacute -18
+KPX rcommaaccent ccaron -18
+KPX rcommaaccent ccedilla -18
+KPX rcommaaccent comma -92
+KPX rcommaaccent e -18
+KPX rcommaaccent eacute -18
+KPX rcommaaccent ecaron -18
+KPX rcommaaccent ecircumflex -18
+KPX rcommaaccent edieresis -18
+KPX rcommaaccent edotaccent -18
+KPX rcommaaccent egrave -18
+KPX rcommaaccent emacron -18
+KPX rcommaaccent eogonek -18
+KPX rcommaaccent g -10
+KPX rcommaaccent gbreve -10
+KPX rcommaaccent gcommaaccent -10
+KPX rcommaaccent hyphen -37
+KPX rcommaaccent n -15
+KPX rcommaaccent nacute -15
+KPX rcommaaccent ncaron -15
+KPX rcommaaccent ncommaaccent -15
+KPX rcommaaccent ntilde -15
+KPX rcommaaccent o -18
+KPX rcommaaccent oacute -18
+KPX rcommaaccent ocircumflex -18
+KPX rcommaaccent odieresis -18
+KPX rcommaaccent ograve -18
+KPX rcommaaccent ohungarumlaut -18
+KPX rcommaaccent omacron -18
+KPX rcommaaccent oslash -18
+KPX rcommaaccent otilde -18
+KPX rcommaaccent p -10
+KPX rcommaaccent period -100
+KPX rcommaaccent q -18
+KPX rcommaaccent v -10
+KPX space A -55
+KPX space Aacute -55
+KPX space Abreve -55
+KPX space Acircumflex -55
+KPX space Adieresis -55
+KPX space Agrave -55
+KPX space Amacron -55
+KPX space Aogonek -55
+KPX space Aring -55
+KPX space Atilde -55
+KPX space T -30
+KPX space Tcaron -30
+KPX space Tcommaaccent -30
+KPX space V -45
+KPX space W -30
+KPX space Y -55
+KPX space Yacute -55
+KPX space Ydieresis -55
+KPX v a -10
+KPX v aacute -10
+KPX v abreve -10
+KPX v acircumflex -10
+KPX v adieresis -10
+KPX v agrave -10
+KPX v amacron -10
+KPX v aogonek -10
+KPX v aring -10
+KPX v atilde -10
+KPX v comma -55
+KPX v e -10
+KPX v eacute -10
+KPX v ecaron -10
+KPX v ecircumflex -10
+KPX v edieresis -10
+KPX v edotaccent -10
+KPX v egrave -10
+KPX v emacron -10
+KPX v eogonek -10
+KPX v o -10
+KPX v oacute -10
+KPX v ocircumflex -10
+KPX v odieresis -10
+KPX v ograve -10
+KPX v ohungarumlaut -10
+KPX v omacron -10
+KPX v oslash -10
+KPX v otilde -10
+KPX v period -70
+KPX w comma -55
+KPX w o -10
+KPX w oacute -10
+KPX w ocircumflex -10
+KPX w odieresis -10
+KPX w ograve -10
+KPX w ohungarumlaut -10
+KPX w omacron -10
+KPX w oslash -10
+KPX w otilde -10
+KPX w period -70
+KPX y comma -55
+KPX y e -10
+KPX y eacute -10
+KPX y ecaron -10
+KPX y ecircumflex -10
+KPX y edieresis -10
+KPX y edotaccent -10
+KPX y egrave -10
+KPX y emacron -10
+KPX y eogonek -10
+KPX y o -25
+KPX y oacute -25
+KPX y ocircumflex -25
+KPX y odieresis -25
+KPX y ograve -25
+KPX y ohungarumlaut -25
+KPX y omacron -25
+KPX y oslash -25
+KPX y otilde -25
+KPX y period -70
+KPX yacute comma -55
+KPX yacute e -10
+KPX yacute eacute -10
+KPX yacute ecaron -10
+KPX yacute ecircumflex -10
+KPX yacute edieresis -10
+KPX yacute edotaccent -10
+KPX yacute egrave -10
+KPX yacute emacron -10
+KPX yacute eogonek -10
+KPX yacute o -25
+KPX yacute oacute -25
+KPX yacute ocircumflex -25
+KPX yacute odieresis -25
+KPX yacute ograve -25
+KPX yacute ohungarumlaut -25
+KPX yacute omacron -25
+KPX yacute oslash -25
+KPX yacute otilde -25
+KPX yacute period -70
+KPX ydieresis comma -55
+KPX ydieresis e -10
+KPX ydieresis eacute -10
+KPX ydieresis ecaron -10
+KPX ydieresis ecircumflex -10
+KPX ydieresis edieresis -10
+KPX ydieresis edotaccent -10
+KPX ydieresis egrave -10
+KPX ydieresis emacron -10
+KPX ydieresis eogonek -10
+KPX ydieresis o -25
+KPX ydieresis oacute -25
+KPX ydieresis ocircumflex -25
+KPX ydieresis odieresis -25
+KPX ydieresis ograve -25
+KPX ydieresis ohungarumlaut -25
+KPX ydieresis omacron -25
+KPX ydieresis oslash -25
+KPX ydieresis otilde -25
+KPX ydieresis period -70
+EndKernPairs
+EndKernData
+EndFontMetrics
diff --git a/program/libraries/afm/Times-BoldItalic.afm b/program/libraries/afm/Times-BoldItalic.afm
--- /dev/null
@@ -0,0 +1,2384 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
+Comment Creation Date: Thu May 1 13:04:06 1997
+Comment UniqueID 43066
+Comment VMusage 45874 56899
+FontName Times-BoldItalic
+FullName Times Bold Italic
+FamilyName Times
+Weight Bold
+ItalicAngle -15
+IsFixedPitch false
+CharacterSet ExtendedRoman
+FontBBox -200 -218 996 921
+UnderlinePosition -100
+UnderlineThickness 50
+Version 002.000
+Notice Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.Times is a trademark of Linotype-Hell AG and/or its subsidiaries.
+EncodingScheme AdobeStandardEncoding
+CapHeight 669
+XHeight 462
+Ascender 683
+Descender -217
+StdHW 42
+StdVW 121
+StartCharMetrics 315
+C 32 ; WX 250 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 389 ; N exclam ; B 67 -13 370 684 ;
+C 34 ; WX 555 ; N quotedbl ; B 136 398 536 685 ;
+C 35 ; WX 500 ; N numbersign ; B -33 0 533 700 ;
+C 36 ; WX 500 ; N dollar ; B -20 -100 497 733 ;
+C 37 ; WX 833 ; N percent ; B 39 -10 793 692 ;
+C 38 ; WX 778 ; N ampersand ; B 5 -19 699 682 ;
+C 39 ; WX 333 ; N quoteright ; B 98 369 302 685 ;
+C 40 ; WX 333 ; N parenleft ; B 28 -179 344 685 ;
+C 41 ; WX 333 ; N parenright ; B -44 -179 271 685 ;
+C 42 ; WX 500 ; N asterisk ; B 65 249 456 685 ;
+C 43 ; WX 570 ; N plus ; B 33 0 537 506 ;
+C 44 ; WX 250 ; N comma ; B -60 -182 144 134 ;
+C 45 ; WX 333 ; N hyphen ; B 2 166 271 282 ;
+C 46 ; WX 250 ; N period ; B -9 -13 139 135 ;
+C 47 ; WX 278 ; N slash ; B -64 -18 342 685 ;
+C 48 ; WX 500 ; N zero ; B 17 -14 477 683 ;
+C 49 ; WX 500 ; N one ; B 5 0 419 683 ;
+C 50 ; WX 500 ; N two ; B -27 0 446 683 ;
+C 51 ; WX 500 ; N three ; B -15 -13 450 683 ;
+C 52 ; WX 500 ; N four ; B -15 0 503 683 ;
+C 53 ; WX 500 ; N five ; B -11 -13 487 669 ;
+C 54 ; WX 500 ; N six ; B 23 -15 509 679 ;
+C 55 ; WX 500 ; N seven ; B 52 0 525 669 ;
+C 56 ; WX 500 ; N eight ; B 3 -13 476 683 ;
+C 57 ; WX 500 ; N nine ; B -12 -10 475 683 ;
+C 58 ; WX 333 ; N colon ; B 23 -13 264 459 ;
+C 59 ; WX 333 ; N semicolon ; B -25 -183 264 459 ;
+C 60 ; WX 570 ; N less ; B 31 -8 539 514 ;
+C 61 ; WX 570 ; N equal ; B 33 107 537 399 ;
+C 62 ; WX 570 ; N greater ; B 31 -8 539 514 ;
+C 63 ; WX 500 ; N question ; B 79 -13 470 684 ;
+C 64 ; WX 832 ; N at ; B 63 -18 770 685 ;
+C 65 ; WX 667 ; N A ; B -67 0 593 683 ;
+C 66 ; WX 667 ; N B ; B -24 0 624 669 ;
+C 67 ; WX 667 ; N C ; B 32 -18 677 685 ;
+C 68 ; WX 722 ; N D ; B -46 0 685 669 ;
+C 69 ; WX 667 ; N E ; B -27 0 653 669 ;
+C 70 ; WX 667 ; N F ; B -13 0 660 669 ;
+C 71 ; WX 722 ; N G ; B 21 -18 706 685 ;
+C 72 ; WX 778 ; N H ; B -24 0 799 669 ;
+C 73 ; WX 389 ; N I ; B -32 0 406 669 ;
+C 74 ; WX 500 ; N J ; B -46 -99 524 669 ;
+C 75 ; WX 667 ; N K ; B -21 0 702 669 ;
+C 76 ; WX 611 ; N L ; B -22 0 590 669 ;
+C 77 ; WX 889 ; N M ; B -29 -12 917 669 ;
+C 78 ; WX 722 ; N N ; B -27 -15 748 669 ;
+C 79 ; WX 722 ; N O ; B 27 -18 691 685 ;
+C 80 ; WX 611 ; N P ; B -27 0 613 669 ;
+C 81 ; WX 722 ; N Q ; B 27 -208 691 685 ;
+C 82 ; WX 667 ; N R ; B -29 0 623 669 ;
+C 83 ; WX 556 ; N S ; B 2 -18 526 685 ;
+C 84 ; WX 611 ; N T ; B 50 0 650 669 ;
+C 85 ; WX 722 ; N U ; B 67 -18 744 669 ;
+C 86 ; WX 667 ; N V ; B 65 -18 715 669 ;
+C 87 ; WX 889 ; N W ; B 65 -18 940 669 ;
+C 88 ; WX 667 ; N X ; B -24 0 694 669 ;
+C 89 ; WX 611 ; N Y ; B 73 0 659 669 ;
+C 90 ; WX 611 ; N Z ; B -11 0 590 669 ;
+C 91 ; WX 333 ; N bracketleft ; B -37 -159 362 674 ;
+C 92 ; WX 278 ; N backslash ; B -1 -18 279 685 ;
+C 93 ; WX 333 ; N bracketright ; B -56 -157 343 674 ;
+C 94 ; WX 570 ; N asciicircum ; B 67 304 503 669 ;
+C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ;
+C 96 ; WX 333 ; N quoteleft ; B 128 369 332 685 ;
+C 97 ; WX 500 ; N a ; B -21 -14 455 462 ;
+C 98 ; WX 500 ; N b ; B -14 -13 444 699 ;
+C 99 ; WX 444 ; N c ; B -5 -13 392 462 ;
+C 100 ; WX 500 ; N d ; B -21 -13 517 699 ;
+C 101 ; WX 444 ; N e ; B 5 -13 398 462 ;
+C 102 ; WX 333 ; N f ; B -169 -205 446 698 ; L i fi ; L l fl ;
+C 103 ; WX 500 ; N g ; B -52 -203 478 462 ;
+C 104 ; WX 556 ; N h ; B -13 -9 498 699 ;
+C 105 ; WX 278 ; N i ; B 2 -9 263 684 ;
+C 106 ; WX 278 ; N j ; B -189 -207 279 684 ;
+C 107 ; WX 500 ; N k ; B -23 -8 483 699 ;
+C 108 ; WX 278 ; N l ; B 2 -9 290 699 ;
+C 109 ; WX 778 ; N m ; B -14 -9 722 462 ;
+C 110 ; WX 556 ; N n ; B -6 -9 493 462 ;
+C 111 ; WX 500 ; N o ; B -3 -13 441 462 ;
+C 112 ; WX 500 ; N p ; B -120 -205 446 462 ;
+C 113 ; WX 500 ; N q ; B 1 -205 471 462 ;
+C 114 ; WX 389 ; N r ; B -21 0 389 462 ;
+C 115 ; WX 389 ; N s ; B -19 -13 333 462 ;
+C 116 ; WX 278 ; N t ; B -11 -9 281 594 ;
+C 117 ; WX 556 ; N u ; B 15 -9 492 462 ;
+C 118 ; WX 444 ; N v ; B 16 -13 401 462 ;
+C 119 ; WX 667 ; N w ; B 16 -13 614 462 ;
+C 120 ; WX 500 ; N x ; B -46 -13 469 462 ;
+C 121 ; WX 444 ; N y ; B -94 -205 392 462 ;
+C 122 ; WX 389 ; N z ; B -43 -78 368 449 ;
+C 123 ; WX 348 ; N braceleft ; B 5 -187 436 686 ;
+C 124 ; WX 220 ; N bar ; B 66 -218 154 782 ;
+C 125 ; WX 348 ; N braceright ; B -129 -187 302 686 ;
+C 126 ; WX 570 ; N asciitilde ; B 54 173 516 333 ;
+C 161 ; WX 389 ; N exclamdown ; B 19 -205 322 492 ;
+C 162 ; WX 500 ; N cent ; B 42 -143 439 576 ;
+C 163 ; WX 500 ; N sterling ; B -32 -12 510 683 ;
+C 164 ; WX 167 ; N fraction ; B -169 -14 324 683 ;
+C 165 ; WX 500 ; N yen ; B 33 0 628 669 ;
+C 166 ; WX 500 ; N florin ; B -87 -156 537 707 ;
+C 167 ; WX 500 ; N section ; B 36 -143 459 685 ;
+C 168 ; WX 500 ; N currency ; B -26 34 526 586 ;
+C 169 ; WX 278 ; N quotesingle ; B 128 398 268 685 ;
+C 170 ; WX 500 ; N quotedblleft ; B 53 369 513 685 ;
+C 171 ; WX 500 ; N guillemotleft ; B 12 32 468 415 ;
+C 172 ; WX 333 ; N guilsinglleft ; B 32 32 303 415 ;
+C 173 ; WX 333 ; N guilsinglright ; B 10 32 281 415 ;
+C 174 ; WX 556 ; N fi ; B -188 -205 514 703 ;
+C 175 ; WX 556 ; N fl ; B -186 -205 553 704 ;
+C 177 ; WX 500 ; N endash ; B -40 178 477 269 ;
+C 178 ; WX 500 ; N dagger ; B 91 -145 494 685 ;
+C 179 ; WX 500 ; N daggerdbl ; B 10 -139 493 685 ;
+C 180 ; WX 250 ; N periodcentered ; B 51 257 199 405 ;
+C 182 ; WX 500 ; N paragraph ; B -57 -193 562 669 ;
+C 183 ; WX 350 ; N bullet ; B 0 175 350 525 ;
+C 184 ; WX 333 ; N quotesinglbase ; B -5 -182 199 134 ;
+C 185 ; WX 500 ; N quotedblbase ; B -57 -182 403 134 ;
+C 186 ; WX 500 ; N quotedblright ; B 53 369 513 685 ;
+C 187 ; WX 500 ; N guillemotright ; B 12 32 468 415 ;
+C 188 ; WX 1000 ; N ellipsis ; B 40 -13 852 135 ;
+C 189 ; WX 1000 ; N perthousand ; B 7 -29 996 706 ;
+C 191 ; WX 500 ; N questiondown ; B 30 -205 421 492 ;
+C 193 ; WX 333 ; N grave ; B 85 516 297 697 ;
+C 194 ; WX 333 ; N acute ; B 139 516 379 697 ;
+C 195 ; WX 333 ; N circumflex ; B 40 516 367 690 ;
+C 196 ; WX 333 ; N tilde ; B 48 536 407 655 ;
+C 197 ; WX 333 ; N macron ; B 51 553 393 623 ;
+C 198 ; WX 333 ; N breve ; B 71 516 387 678 ;
+C 199 ; WX 333 ; N dotaccent ; B 163 550 298 684 ;
+C 200 ; WX 333 ; N dieresis ; B 55 550 402 684 ;
+C 202 ; WX 333 ; N ring ; B 127 516 340 729 ;
+C 203 ; WX 333 ; N cedilla ; B -80 -218 156 5 ;
+C 205 ; WX 333 ; N hungarumlaut ; B 69 516 498 697 ;
+C 206 ; WX 333 ; N ogonek ; B 15 -183 244 34 ;
+C 207 ; WX 333 ; N caron ; B 79 516 411 690 ;
+C 208 ; WX 1000 ; N emdash ; B -40 178 977 269 ;
+C 225 ; WX 944 ; N AE ; B -64 0 918 669 ;
+C 227 ; WX 266 ; N ordfeminine ; B 16 399 330 685 ;
+C 232 ; WX 611 ; N Lslash ; B -22 0 590 669 ;
+C 233 ; WX 722 ; N Oslash ; B 27 -125 691 764 ;
+C 234 ; WX 944 ; N OE ; B 23 -8 946 677 ;
+C 235 ; WX 300 ; N ordmasculine ; B 56 400 347 685 ;
+C 241 ; WX 722 ; N ae ; B -5 -13 673 462 ;
+C 245 ; WX 278 ; N dotlessi ; B 2 -9 238 462 ;
+C 248 ; WX 278 ; N lslash ; B -7 -9 307 699 ;
+C 249 ; WX 500 ; N oslash ; B -3 -119 441 560 ;
+C 250 ; WX 722 ; N oe ; B 6 -13 674 462 ;
+C 251 ; WX 500 ; N germandbls ; B -200 -200 473 705 ;
+C -1 ; WX 389 ; N Idieresis ; B -32 0 450 862 ;
+C -1 ; WX 444 ; N eacute ; B 5 -13 435 697 ;
+C -1 ; WX 500 ; N abreve ; B -21 -14 471 678 ;
+C -1 ; WX 556 ; N uhungarumlaut ; B 15 -9 610 697 ;
+C -1 ; WX 444 ; N ecaron ; B 5 -13 467 690 ;
+C -1 ; WX 611 ; N Ydieresis ; B 73 0 659 862 ;
+C -1 ; WX 570 ; N divide ; B 33 -29 537 535 ;
+C -1 ; WX 611 ; N Yacute ; B 73 0 659 904 ;
+C -1 ; WX 667 ; N Acircumflex ; B -67 0 593 897 ;
+C -1 ; WX 500 ; N aacute ; B -21 -14 463 697 ;
+C -1 ; WX 722 ; N Ucircumflex ; B 67 -18 744 897 ;
+C -1 ; WX 444 ; N yacute ; B -94 -205 435 697 ;
+C -1 ; WX 389 ; N scommaaccent ; B -19 -218 333 462 ;
+C -1 ; WX 444 ; N ecircumflex ; B 5 -13 423 690 ;
+C -1 ; WX 722 ; N Uring ; B 67 -18 744 921 ;
+C -1 ; WX 722 ; N Udieresis ; B 67 -18 744 862 ;
+C -1 ; WX 500 ; N aogonek ; B -21 -183 455 462 ;
+C -1 ; WX 722 ; N Uacute ; B 67 -18 744 904 ;
+C -1 ; WX 556 ; N uogonek ; B 15 -183 492 462 ;
+C -1 ; WX 667 ; N Edieresis ; B -27 0 653 862 ;
+C -1 ; WX 722 ; N Dcroat ; B -31 0 700 669 ;
+C -1 ; WX 250 ; N commaaccent ; B -36 -218 131 -50 ;
+C -1 ; WX 747 ; N copyright ; B 30 -18 718 685 ;
+C -1 ; WX 667 ; N Emacron ; B -27 0 653 830 ;
+C -1 ; WX 444 ; N ccaron ; B -5 -13 467 690 ;
+C -1 ; WX 500 ; N aring ; B -21 -14 455 729 ;
+C -1 ; WX 722 ; N Ncommaaccent ; B -27 -218 748 669 ;
+C -1 ; WX 278 ; N lacute ; B 2 -9 392 904 ;
+C -1 ; WX 500 ; N agrave ; B -21 -14 455 697 ;
+C -1 ; WX 611 ; N Tcommaaccent ; B 50 -218 650 669 ;
+C -1 ; WX 667 ; N Cacute ; B 32 -18 677 904 ;
+C -1 ; WX 500 ; N atilde ; B -21 -14 491 655 ;
+C -1 ; WX 667 ; N Edotaccent ; B -27 0 653 862 ;
+C -1 ; WX 389 ; N scaron ; B -19 -13 424 690 ;
+C -1 ; WX 389 ; N scedilla ; B -19 -218 333 462 ;
+C -1 ; WX 278 ; N iacute ; B 2 -9 352 697 ;
+C -1 ; WX 494 ; N lozenge ; B 10 0 484 745 ;
+C -1 ; WX 667 ; N Rcaron ; B -29 0 623 897 ;
+C -1 ; WX 722 ; N Gcommaaccent ; B 21 -218 706 685 ;
+C -1 ; WX 556 ; N ucircumflex ; B 15 -9 492 690 ;
+C -1 ; WX 500 ; N acircumflex ; B -21 -14 455 690 ;
+C -1 ; WX 667 ; N Amacron ; B -67 0 593 830 ;
+C -1 ; WX 389 ; N rcaron ; B -21 0 424 690 ;
+C -1 ; WX 444 ; N ccedilla ; B -5 -218 392 462 ;
+C -1 ; WX 611 ; N Zdotaccent ; B -11 0 590 862 ;
+C -1 ; WX 611 ; N Thorn ; B -27 0 573 669 ;
+C -1 ; WX 722 ; N Omacron ; B 27 -18 691 830 ;
+C -1 ; WX 667 ; N Racute ; B -29 0 623 904 ;
+C -1 ; WX 556 ; N Sacute ; B 2 -18 531 904 ;
+C -1 ; WX 608 ; N dcaron ; B -21 -13 675 708 ;
+C -1 ; WX 722 ; N Umacron ; B 67 -18 744 830 ;
+C -1 ; WX 556 ; N uring ; B 15 -9 492 729 ;
+C -1 ; WX 300 ; N threesuperior ; B 17 265 321 683 ;
+C -1 ; WX 722 ; N Ograve ; B 27 -18 691 904 ;
+C -1 ; WX 667 ; N Agrave ; B -67 0 593 904 ;
+C -1 ; WX 667 ; N Abreve ; B -67 0 593 885 ;
+C -1 ; WX 570 ; N multiply ; B 48 16 522 490 ;
+C -1 ; WX 556 ; N uacute ; B 15 -9 492 697 ;
+C -1 ; WX 611 ; N Tcaron ; B 50 0 650 897 ;
+C -1 ; WX 494 ; N partialdiff ; B 11 -21 494 750 ;
+C -1 ; WX 444 ; N ydieresis ; B -94 -205 443 655 ;
+C -1 ; WX 722 ; N Nacute ; B -27 -15 748 904 ;
+C -1 ; WX 278 ; N icircumflex ; B -3 -9 324 690 ;
+C -1 ; WX 667 ; N Ecircumflex ; B -27 0 653 897 ;
+C -1 ; WX 500 ; N adieresis ; B -21 -14 476 655 ;
+C -1 ; WX 444 ; N edieresis ; B 5 -13 448 655 ;
+C -1 ; WX 444 ; N cacute ; B -5 -13 435 697 ;
+C -1 ; WX 556 ; N nacute ; B -6 -9 493 697 ;
+C -1 ; WX 556 ; N umacron ; B 15 -9 492 623 ;
+C -1 ; WX 722 ; N Ncaron ; B -27 -15 748 897 ;
+C -1 ; WX 389 ; N Iacute ; B -32 0 432 904 ;
+C -1 ; WX 570 ; N plusminus ; B 33 0 537 506 ;
+C -1 ; WX 220 ; N brokenbar ; B 66 -143 154 707 ;
+C -1 ; WX 747 ; N registered ; B 30 -18 718 685 ;
+C -1 ; WX 722 ; N Gbreve ; B 21 -18 706 885 ;
+C -1 ; WX 389 ; N Idotaccent ; B -32 0 406 862 ;
+C -1 ; WX 600 ; N summation ; B 14 -10 585 706 ;
+C -1 ; WX 667 ; N Egrave ; B -27 0 653 904 ;
+C -1 ; WX 389 ; N racute ; B -21 0 407 697 ;
+C -1 ; WX 500 ; N omacron ; B -3 -13 462 623 ;
+C -1 ; WX 611 ; N Zacute ; B -11 0 590 904 ;
+C -1 ; WX 611 ; N Zcaron ; B -11 0 590 897 ;
+C -1 ; WX 549 ; N greaterequal ; B 26 0 523 704 ;
+C -1 ; WX 722 ; N Eth ; B -31 0 700 669 ;
+C -1 ; WX 667 ; N Ccedilla ; B 32 -218 677 685 ;
+C -1 ; WX 278 ; N lcommaaccent ; B -42 -218 290 699 ;
+C -1 ; WX 366 ; N tcaron ; B -11 -9 434 754 ;
+C -1 ; WX 444 ; N eogonek ; B 5 -183 398 462 ;
+C -1 ; WX 722 ; N Uogonek ; B 67 -183 744 669 ;
+C -1 ; WX 667 ; N Aacute ; B -67 0 593 904 ;
+C -1 ; WX 667 ; N Adieresis ; B -67 0 593 862 ;
+C -1 ; WX 444 ; N egrave ; B 5 -13 398 697 ;
+C -1 ; WX 389 ; N zacute ; B -43 -78 407 697 ;
+C -1 ; WX 278 ; N iogonek ; B -20 -183 263 684 ;
+C -1 ; WX 722 ; N Oacute ; B 27 -18 691 904 ;
+C -1 ; WX 500 ; N oacute ; B -3 -13 463 697 ;
+C -1 ; WX 500 ; N amacron ; B -21 -14 467 623 ;
+C -1 ; WX 389 ; N sacute ; B -19 -13 407 697 ;
+C -1 ; WX 278 ; N idieresis ; B 2 -9 364 655 ;
+C -1 ; WX 722 ; N Ocircumflex ; B 27 -18 691 897 ;
+C -1 ; WX 722 ; N Ugrave ; B 67 -18 744 904 ;
+C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ;
+C -1 ; WX 500 ; N thorn ; B -120 -205 446 699 ;
+C -1 ; WX 300 ; N twosuperior ; B 2 274 313 683 ;
+C -1 ; WX 722 ; N Odieresis ; B 27 -18 691 862 ;
+C -1 ; WX 576 ; N mu ; B -60 -207 516 449 ;
+C -1 ; WX 278 ; N igrave ; B 2 -9 259 697 ;
+C -1 ; WX 500 ; N ohungarumlaut ; B -3 -13 582 697 ;
+C -1 ; WX 667 ; N Eogonek ; B -27 -183 653 669 ;
+C -1 ; WX 500 ; N dcroat ; B -21 -13 552 699 ;
+C -1 ; WX 750 ; N threequarters ; B 7 -14 726 683 ;
+C -1 ; WX 556 ; N Scedilla ; B 2 -218 526 685 ;
+C -1 ; WX 382 ; N lcaron ; B 2 -9 448 708 ;
+C -1 ; WX 667 ; N Kcommaaccent ; B -21 -218 702 669 ;
+C -1 ; WX 611 ; N Lacute ; B -22 0 590 904 ;
+C -1 ; WX 1000 ; N trademark ; B 32 263 968 669 ;
+C -1 ; WX 444 ; N edotaccent ; B 5 -13 398 655 ;
+C -1 ; WX 389 ; N Igrave ; B -32 0 406 904 ;
+C -1 ; WX 389 ; N Imacron ; B -32 0 461 830 ;
+C -1 ; WX 611 ; N Lcaron ; B -22 0 671 718 ;
+C -1 ; WX 750 ; N onehalf ; B -9 -14 723 683 ;
+C -1 ; WX 549 ; N lessequal ; B 29 0 526 704 ;
+C -1 ; WX 500 ; N ocircumflex ; B -3 -13 451 690 ;
+C -1 ; WX 556 ; N ntilde ; B -6 -9 504 655 ;
+C -1 ; WX 722 ; N Uhungarumlaut ; B 67 -18 744 904 ;
+C -1 ; WX 667 ; N Eacute ; B -27 0 653 904 ;
+C -1 ; WX 444 ; N emacron ; B 5 -13 439 623 ;
+C -1 ; WX 500 ; N gbreve ; B -52 -203 478 678 ;
+C -1 ; WX 750 ; N onequarter ; B 7 -14 721 683 ;
+C -1 ; WX 556 ; N Scaron ; B 2 -18 553 897 ;
+C -1 ; WX 556 ; N Scommaaccent ; B 2 -218 526 685 ;
+C -1 ; WX 722 ; N Ohungarumlaut ; B 27 -18 723 904 ;
+C -1 ; WX 400 ; N degree ; B 83 397 369 683 ;
+C -1 ; WX 500 ; N ograve ; B -3 -13 441 697 ;
+C -1 ; WX 667 ; N Ccaron ; B 32 -18 677 897 ;
+C -1 ; WX 556 ; N ugrave ; B 15 -9 492 697 ;
+C -1 ; WX 549 ; N radical ; B 10 -46 512 850 ;
+C -1 ; WX 722 ; N Dcaron ; B -46 0 685 897 ;
+C -1 ; WX 389 ; N rcommaaccent ; B -67 -218 389 462 ;
+C -1 ; WX 722 ; N Ntilde ; B -27 -15 748 862 ;
+C -1 ; WX 500 ; N otilde ; B -3 -13 491 655 ;
+C -1 ; WX 667 ; N Rcommaaccent ; B -29 -218 623 669 ;
+C -1 ; WX 611 ; N Lcommaaccent ; B -22 -218 590 669 ;
+C -1 ; WX 667 ; N Atilde ; B -67 0 593 862 ;
+C -1 ; WX 667 ; N Aogonek ; B -67 -183 604 683 ;
+C -1 ; WX 667 ; N Aring ; B -67 0 593 921 ;
+C -1 ; WX 722 ; N Otilde ; B 27 -18 691 862 ;
+C -1 ; WX 389 ; N zdotaccent ; B -43 -78 368 655 ;
+C -1 ; WX 667 ; N Ecaron ; B -27 0 653 897 ;
+C -1 ; WX 389 ; N Iogonek ; B -32 -183 406 669 ;
+C -1 ; WX 500 ; N kcommaaccent ; B -23 -218 483 699 ;
+C -1 ; WX 606 ; N minus ; B 51 209 555 297 ;
+C -1 ; WX 389 ; N Icircumflex ; B -32 0 450 897 ;
+C -1 ; WX 556 ; N ncaron ; B -6 -9 523 690 ;
+C -1 ; WX 278 ; N tcommaaccent ; B -62 -218 281 594 ;
+C -1 ; WX 606 ; N logicalnot ; B 51 108 555 399 ;
+C -1 ; WX 500 ; N odieresis ; B -3 -13 471 655 ;
+C -1 ; WX 556 ; N udieresis ; B 15 -9 499 655 ;
+C -1 ; WX 549 ; N notequal ; B 15 -49 540 570 ;
+C -1 ; WX 500 ; N gcommaaccent ; B -52 -203 478 767 ;
+C -1 ; WX 500 ; N eth ; B -3 -13 454 699 ;
+C -1 ; WX 389 ; N zcaron ; B -43 -78 424 690 ;
+C -1 ; WX 556 ; N ncommaaccent ; B -6 -218 493 462 ;
+C -1 ; WX 300 ; N onesuperior ; B 30 274 301 683 ;
+C -1 ; WX 278 ; N imacron ; B 2 -9 294 623 ;
+C -1 ; WX 500 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+StartKernData
+StartKernPairs 2038
+KPX A C -65
+KPX A Cacute -65
+KPX A Ccaron -65
+KPX A Ccedilla -65
+KPX A G -60
+KPX A Gbreve -60
+KPX A Gcommaaccent -60
+KPX A O -50
+KPX A Oacute -50
+KPX A Ocircumflex -50
+KPX A Odieresis -50
+KPX A Ograve -50
+KPX A Ohungarumlaut -50
+KPX A Omacron -50
+KPX A Oslash -50
+KPX A Otilde -50
+KPX A Q -55
+KPX A T -55
+KPX A Tcaron -55
+KPX A Tcommaaccent -55
+KPX A U -50
+KPX A Uacute -50
+KPX A Ucircumflex -50
+KPX A Udieresis -50
+KPX A Ugrave -50
+KPX A Uhungarumlaut -50
+KPX A Umacron -50
+KPX A Uogonek -50
+KPX A Uring -50
+KPX A V -95
+KPX A W -100
+KPX A Y -70
+KPX A Yacute -70
+KPX A Ydieresis -70
+KPX A quoteright -74
+KPX A u -30
+KPX A uacute -30
+KPX A ucircumflex -30
+KPX A udieresis -30
+KPX A ugrave -30
+KPX A uhungarumlaut -30
+KPX A umacron -30
+KPX A uogonek -30
+KPX A uring -30
+KPX A v -74
+KPX A w -74
+KPX A y -74
+KPX A yacute -74
+KPX A ydieresis -74
+KPX Aacute C -65
+KPX Aacute Cacute -65
+KPX Aacute Ccaron -65
+KPX Aacute Ccedilla -65
+KPX Aacute G -60
+KPX Aacute Gbreve -60
+KPX Aacute Gcommaaccent -60
+KPX Aacute O -50
+KPX Aacute Oacute -50
+KPX Aacute Ocircumflex -50
+KPX Aacute Odieresis -50
+KPX Aacute Ograve -50
+KPX Aacute Ohungarumlaut -50
+KPX Aacute Omacron -50
+KPX Aacute Oslash -50
+KPX Aacute Otilde -50
+KPX Aacute Q -55
+KPX Aacute T -55
+KPX Aacute Tcaron -55
+KPX Aacute Tcommaaccent -55
+KPX Aacute U -50
+KPX Aacute Uacute -50
+KPX Aacute Ucircumflex -50
+KPX Aacute Udieresis -50
+KPX Aacute Ugrave -50
+KPX Aacute Uhungarumlaut -50
+KPX Aacute Umacron -50
+KPX Aacute Uogonek -50
+KPX Aacute Uring -50
+KPX Aacute V -95
+KPX Aacute W -100
+KPX Aacute Y -70
+KPX Aacute Yacute -70
+KPX Aacute Ydieresis -70
+KPX Aacute quoteright -74
+KPX Aacute u -30
+KPX Aacute uacute -30
+KPX Aacute ucircumflex -30
+KPX Aacute udieresis -30
+KPX Aacute ugrave -30
+KPX Aacute uhungarumlaut -30
+KPX Aacute umacron -30
+KPX Aacute uogonek -30
+KPX Aacute uring -30
+KPX Aacute v -74
+KPX Aacute w -74
+KPX Aacute y -74
+KPX Aacute yacute -74
+KPX Aacute ydieresis -74
+KPX Abreve C -65
+KPX Abreve Cacute -65
+KPX Abreve Ccaron -65
+KPX Abreve Ccedilla -65
+KPX Abreve G -60
+KPX Abreve Gbreve -60
+KPX Abreve Gcommaaccent -60
+KPX Abreve O -50
+KPX Abreve Oacute -50
+KPX Abreve Ocircumflex -50
+KPX Abreve Odieresis -50
+KPX Abreve Ograve -50
+KPX Abreve Ohungarumlaut -50
+KPX Abreve Omacron -50
+KPX Abreve Oslash -50
+KPX Abreve Otilde -50
+KPX Abreve Q -55
+KPX Abreve T -55
+KPX Abreve Tcaron -55
+KPX Abreve Tcommaaccent -55
+KPX Abreve U -50
+KPX Abreve Uacute -50
+KPX Abreve Ucircumflex -50
+KPX Abreve Udieresis -50
+KPX Abreve Ugrave -50
+KPX Abreve Uhungarumlaut -50
+KPX Abreve Umacron -50
+KPX Abreve Uogonek -50
+KPX Abreve Uring -50
+KPX Abreve V -95
+KPX Abreve W -100
+KPX Abreve Y -70
+KPX Abreve Yacute -70
+KPX Abreve Ydieresis -70
+KPX Abreve quoteright -74
+KPX Abreve u -30
+KPX Abreve uacute -30
+KPX Abreve ucircumflex -30
+KPX Abreve udieresis -30
+KPX Abreve ugrave -30
+KPX Abreve uhungarumlaut -30
+KPX Abreve umacron -30
+KPX Abreve uogonek -30
+KPX Abreve uring -30
+KPX Abreve v -74
+KPX Abreve w -74
+KPX Abreve y -74
+KPX Abreve yacute -74
+KPX Abreve ydieresis -74
+KPX Acircumflex C -65
+KPX Acircumflex Cacute -65
+KPX Acircumflex Ccaron -65
+KPX Acircumflex Ccedilla -65
+KPX Acircumflex G -60
+KPX Acircumflex Gbreve -60
+KPX Acircumflex Gcommaaccent -60
+KPX Acircumflex O -50
+KPX Acircumflex Oacute -50
+KPX Acircumflex Ocircumflex -50
+KPX Acircumflex Odieresis -50
+KPX Acircumflex Ograve -50
+KPX Acircumflex Ohungarumlaut -50
+KPX Acircumflex Omacron -50
+KPX Acircumflex Oslash -50
+KPX Acircumflex Otilde -50
+KPX Acircumflex Q -55
+KPX Acircumflex T -55
+KPX Acircumflex Tcaron -55
+KPX Acircumflex Tcommaaccent -55
+KPX Acircumflex U -50
+KPX Acircumflex Uacute -50
+KPX Acircumflex Ucircumflex -50
+KPX Acircumflex Udieresis -50
+KPX Acircumflex Ugrave -50
+KPX Acircumflex Uhungarumlaut -50
+KPX Acircumflex Umacron -50
+KPX Acircumflex Uogonek -50
+KPX Acircumflex Uring -50
+KPX Acircumflex V -95
+KPX Acircumflex W -100
+KPX Acircumflex Y -70
+KPX Acircumflex Yacute -70
+KPX Acircumflex Ydieresis -70
+KPX Acircumflex quoteright -74
+KPX Acircumflex u -30
+KPX Acircumflex uacute -30
+KPX Acircumflex ucircumflex -30
+KPX Acircumflex udieresis -30
+KPX Acircumflex ugrave -30
+KPX Acircumflex uhungarumlaut -30
+KPX Acircumflex umacron -30
+KPX Acircumflex uogonek -30
+KPX Acircumflex uring -30
+KPX Acircumflex v -74
+KPX Acircumflex w -74
+KPX Acircumflex y -74
+KPX Acircumflex yacute -74
+KPX Acircumflex ydieresis -74
+KPX Adieresis C -65
+KPX Adieresis Cacute -65
+KPX Adieresis Ccaron -65
+KPX Adieresis Ccedilla -65
+KPX Adieresis G -60
+KPX Adieresis Gbreve -60
+KPX Adieresis Gcommaaccent -60
+KPX Adieresis O -50
+KPX Adieresis Oacute -50
+KPX Adieresis Ocircumflex -50
+KPX Adieresis Odieresis -50
+KPX Adieresis Ograve -50
+KPX Adieresis Ohungarumlaut -50
+KPX Adieresis Omacron -50
+KPX Adieresis Oslash -50
+KPX Adieresis Otilde -50
+KPX Adieresis Q -55
+KPX Adieresis T -55
+KPX Adieresis Tcaron -55
+KPX Adieresis Tcommaaccent -55
+KPX Adieresis U -50
+KPX Adieresis Uacute -50
+KPX Adieresis Ucircumflex -50
+KPX Adieresis Udieresis -50
+KPX Adieresis Ugrave -50
+KPX Adieresis Uhungarumlaut -50
+KPX Adieresis Umacron -50
+KPX Adieresis Uogonek -50
+KPX Adieresis Uring -50
+KPX Adieresis V -95
+KPX Adieresis W -100
+KPX Adieresis Y -70
+KPX Adieresis Yacute -70
+KPX Adieresis Ydieresis -70
+KPX Adieresis quoteright -74
+KPX Adieresis u -30
+KPX Adieresis uacute -30
+KPX Adieresis ucircumflex -30
+KPX Adieresis udieresis -30
+KPX Adieresis ugrave -30
+KPX Adieresis uhungarumlaut -30
+KPX Adieresis umacron -30
+KPX Adieresis uogonek -30
+KPX Adieresis uring -30
+KPX Adieresis v -74
+KPX Adieresis w -74
+KPX Adieresis y -74
+KPX Adieresis yacute -74
+KPX Adieresis ydieresis -74
+KPX Agrave C -65
+KPX Agrave Cacute -65
+KPX Agrave Ccaron -65
+KPX Agrave Ccedilla -65
+KPX Agrave G -60
+KPX Agrave Gbreve -60
+KPX Agrave Gcommaaccent -60
+KPX Agrave O -50
+KPX Agrave Oacute -50
+KPX Agrave Ocircumflex -50
+KPX Agrave Odieresis -50
+KPX Agrave Ograve -50
+KPX Agrave Ohungarumlaut -50
+KPX Agrave Omacron -50
+KPX Agrave Oslash -50
+KPX Agrave Otilde -50
+KPX Agrave Q -55
+KPX Agrave T -55
+KPX Agrave Tcaron -55
+KPX Agrave Tcommaaccent -55
+KPX Agrave U -50
+KPX Agrave Uacute -50
+KPX Agrave Ucircumflex -50
+KPX Agrave Udieresis -50
+KPX Agrave Ugrave -50
+KPX Agrave Uhungarumlaut -50
+KPX Agrave Umacron -50
+KPX Agrave Uogonek -50
+KPX Agrave Uring -50
+KPX Agrave V -95
+KPX Agrave W -100
+KPX Agrave Y -70
+KPX Agrave Yacute -70
+KPX Agrave Ydieresis -70
+KPX Agrave quoteright -74
+KPX Agrave u -30
+KPX Agrave uacute -30
+KPX Agrave ucircumflex -30
+KPX Agrave udieresis -30
+KPX Agrave ugrave -30
+KPX Agrave uhungarumlaut -30
+KPX Agrave umacron -30
+KPX Agrave uogonek -30
+KPX Agrave uring -30
+KPX Agrave v -74
+KPX Agrave w -74
+KPX Agrave y -74
+KPX Agrave yacute -74
+KPX Agrave ydieresis -74
+KPX Amacron C -65
+KPX Amacron Cacute -65
+KPX Amacron Ccaron -65
+KPX Amacron Ccedilla -65
+KPX Amacron G -60
+KPX Amacron Gbreve -60
+KPX Amacron Gcommaaccent -60
+KPX Amacron O -50
+KPX Amacron Oacute -50
+KPX Amacron Ocircumflex -50
+KPX Amacron Odieresis -50
+KPX Amacron Ograve -50
+KPX Amacron Ohungarumlaut -50
+KPX Amacron Omacron -50
+KPX Amacron Oslash -50
+KPX Amacron Otilde -50
+KPX Amacron Q -55
+KPX Amacron T -55
+KPX Amacron Tcaron -55
+KPX Amacron Tcommaaccent -55
+KPX Amacron U -50
+KPX Amacron Uacute -50
+KPX Amacron Ucircumflex -50
+KPX Amacron Udieresis -50
+KPX Amacron Ugrave -50
+KPX Amacron Uhungarumlaut -50
+KPX Amacron Umacron -50
+KPX Amacron Uogonek -50
+KPX Amacron Uring -50
+KPX Amacron V -95
+KPX Amacron W -100
+KPX Amacron Y -70
+KPX Amacron Yacute -70
+KPX Amacron Ydieresis -70
+KPX Amacron quoteright -74
+KPX Amacron u -30
+KPX Amacron uacute -30
+KPX Amacron ucircumflex -30
+KPX Amacron udieresis -30
+KPX Amacron ugrave -30
+KPX Amacron uhungarumlaut -30
+KPX Amacron umacron -30
+KPX Amacron uogonek -30
+KPX Amacron uring -30
+KPX Amacron v -74
+KPX Amacron w -74
+KPX Amacron y -74
+KPX Amacron yacute -74
+KPX Amacron ydieresis -74
+KPX Aogonek C -65
+KPX Aogonek Cacute -65
+KPX Aogonek Ccaron -65
+KPX Aogonek Ccedilla -65
+KPX Aogonek G -60
+KPX Aogonek Gbreve -60
+KPX Aogonek Gcommaaccent -60
+KPX Aogonek O -50
+KPX Aogonek Oacute -50
+KPX Aogonek Ocircumflex -50
+KPX Aogonek Odieresis -50
+KPX Aogonek Ograve -50
+KPX Aogonek Ohungarumlaut -50
+KPX Aogonek Omacron -50
+KPX Aogonek Oslash -50
+KPX Aogonek Otilde -50
+KPX Aogonek Q -55
+KPX Aogonek T -55
+KPX Aogonek Tcaron -55
+KPX Aogonek Tcommaaccent -55
+KPX Aogonek U -50
+KPX Aogonek Uacute -50
+KPX Aogonek Ucircumflex -50
+KPX Aogonek Udieresis -50
+KPX Aogonek Ugrave -50
+KPX Aogonek Uhungarumlaut -50
+KPX Aogonek Umacron -50
+KPX Aogonek Uogonek -50
+KPX Aogonek Uring -50
+KPX Aogonek V -95
+KPX Aogonek W -100
+KPX Aogonek Y -70
+KPX Aogonek Yacute -70
+KPX Aogonek Ydieresis -70
+KPX Aogonek quoteright -74
+KPX Aogonek u -30
+KPX Aogonek uacute -30
+KPX Aogonek ucircumflex -30
+KPX Aogonek udieresis -30
+KPX Aogonek ugrave -30
+KPX Aogonek uhungarumlaut -30
+KPX Aogonek umacron -30
+KPX Aogonek uogonek -30
+KPX Aogonek uring -30
+KPX Aogonek v -74
+KPX Aogonek w -74
+KPX Aogonek y -34
+KPX Aogonek yacute -34
+KPX Aogonek ydieresis -34
+KPX Aring C -65
+KPX Aring Cacute -65
+KPX Aring Ccaron -65
+KPX Aring Ccedilla -65
+KPX Aring G -60
+KPX Aring Gbreve -60
+KPX Aring Gcommaaccent -60
+KPX Aring O -50
+KPX Aring Oacute -50
+KPX Aring Ocircumflex -50
+KPX Aring Odieresis -50
+KPX Aring Ograve -50
+KPX Aring Ohungarumlaut -50
+KPX Aring Omacron -50
+KPX Aring Oslash -50
+KPX Aring Otilde -50
+KPX Aring Q -55
+KPX Aring T -55
+KPX Aring Tcaron -55
+KPX Aring Tcommaaccent -55
+KPX Aring U -50
+KPX Aring Uacute -50
+KPX Aring Ucircumflex -50
+KPX Aring Udieresis -50
+KPX Aring Ugrave -50
+KPX Aring Uhungarumlaut -50
+KPX Aring Umacron -50
+KPX Aring Uogonek -50
+KPX Aring Uring -50
+KPX Aring V -95
+KPX Aring W -100
+KPX Aring Y -70
+KPX Aring Yacute -70
+KPX Aring Ydieresis -70
+KPX Aring quoteright -74
+KPX Aring u -30
+KPX Aring uacute -30
+KPX Aring ucircumflex -30
+KPX Aring udieresis -30
+KPX Aring ugrave -30
+KPX Aring uhungarumlaut -30
+KPX Aring umacron -30
+KPX Aring uogonek -30
+KPX Aring uring -30
+KPX Aring v -74
+KPX Aring w -74
+KPX Aring y -74
+KPX Aring yacute -74
+KPX Aring ydieresis -74
+KPX Atilde C -65
+KPX Atilde Cacute -65
+KPX Atilde Ccaron -65
+KPX Atilde Ccedilla -65
+KPX Atilde G -60
+KPX Atilde Gbreve -60
+KPX Atilde Gcommaaccent -60
+KPX Atilde O -50
+KPX Atilde Oacute -50
+KPX Atilde Ocircumflex -50
+KPX Atilde Odieresis -50
+KPX Atilde Ograve -50
+KPX Atilde Ohungarumlaut -50
+KPX Atilde Omacron -50
+KPX Atilde Oslash -50
+KPX Atilde Otilde -50
+KPX Atilde Q -55
+KPX Atilde T -55
+KPX Atilde Tcaron -55
+KPX Atilde Tcommaaccent -55
+KPX Atilde U -50
+KPX Atilde Uacute -50
+KPX Atilde Ucircumflex -50
+KPX Atilde Udieresis -50
+KPX Atilde Ugrave -50
+KPX Atilde Uhungarumlaut -50
+KPX Atilde Umacron -50
+KPX Atilde Uogonek -50
+KPX Atilde Uring -50
+KPX Atilde V -95
+KPX Atilde W -100
+KPX Atilde Y -70
+KPX Atilde Yacute -70
+KPX Atilde Ydieresis -70
+KPX Atilde quoteright -74
+KPX Atilde u -30
+KPX Atilde uacute -30
+KPX Atilde ucircumflex -30
+KPX Atilde udieresis -30
+KPX Atilde ugrave -30
+KPX Atilde uhungarumlaut -30
+KPX Atilde umacron -30
+KPX Atilde uogonek -30
+KPX Atilde uring -30
+KPX Atilde v -74
+KPX Atilde w -74
+KPX Atilde y -74
+KPX Atilde yacute -74
+KPX Atilde ydieresis -74
+KPX B A -25
+KPX B Aacute -25
+KPX B Abreve -25
+KPX B Acircumflex -25
+KPX B Adieresis -25
+KPX B Agrave -25
+KPX B Amacron -25
+KPX B Aogonek -25
+KPX B Aring -25
+KPX B Atilde -25
+KPX B U -10
+KPX B Uacute -10
+KPX B Ucircumflex -10
+KPX B Udieresis -10
+KPX B Ugrave -10
+KPX B Uhungarumlaut -10
+KPX B Umacron -10
+KPX B Uogonek -10
+KPX B Uring -10
+KPX D A -25
+KPX D Aacute -25
+KPX D Abreve -25
+KPX D Acircumflex -25
+KPX D Adieresis -25
+KPX D Agrave -25
+KPX D Amacron -25
+KPX D Aogonek -25
+KPX D Aring -25
+KPX D Atilde -25
+KPX D V -50
+KPX D W -40
+KPX D Y -50
+KPX D Yacute -50
+KPX D Ydieresis -50
+KPX Dcaron A -25
+KPX Dcaron Aacute -25
+KPX Dcaron Abreve -25
+KPX Dcaron Acircumflex -25
+KPX Dcaron Adieresis -25
+KPX Dcaron Agrave -25
+KPX Dcaron Amacron -25
+KPX Dcaron Aogonek -25
+KPX Dcaron Aring -25
+KPX Dcaron Atilde -25
+KPX Dcaron V -50
+KPX Dcaron W -40
+KPX Dcaron Y -50
+KPX Dcaron Yacute -50
+KPX Dcaron Ydieresis -50
+KPX Dcroat A -25
+KPX Dcroat Aacute -25
+KPX Dcroat Abreve -25
+KPX Dcroat Acircumflex -25
+KPX Dcroat Adieresis -25
+KPX Dcroat Agrave -25
+KPX Dcroat Amacron -25
+KPX Dcroat Aogonek -25
+KPX Dcroat Aring -25
+KPX Dcroat Atilde -25
+KPX Dcroat V -50
+KPX Dcroat W -40
+KPX Dcroat Y -50
+KPX Dcroat Yacute -50
+KPX Dcroat Ydieresis -50
+KPX F A -100
+KPX F Aacute -100
+KPX F Abreve -100
+KPX F Acircumflex -100
+KPX F Adieresis -100
+KPX F Agrave -100
+KPX F Amacron -100
+KPX F Aogonek -100
+KPX F Aring -100
+KPX F Atilde -100
+KPX F a -95
+KPX F aacute -95
+KPX F abreve -95
+KPX F acircumflex -95
+KPX F adieresis -95
+KPX F agrave -95
+KPX F amacron -95
+KPX F aogonek -95
+KPX F aring -95
+KPX F atilde -95
+KPX F comma -129
+KPX F e -100
+KPX F eacute -100
+KPX F ecaron -100
+KPX F ecircumflex -100
+KPX F edieresis -100
+KPX F edotaccent -100
+KPX F egrave -100
+KPX F emacron -100
+KPX F eogonek -100
+KPX F i -40
+KPX F iacute -40
+KPX F icircumflex -40
+KPX F idieresis -40
+KPX F igrave -40
+KPX F imacron -40
+KPX F iogonek -40
+KPX F o -70
+KPX F oacute -70
+KPX F ocircumflex -70
+KPX F odieresis -70
+KPX F ograve -70
+KPX F ohungarumlaut -70
+KPX F omacron -70
+KPX F oslash -70
+KPX F otilde -70
+KPX F period -129
+KPX F r -50
+KPX F racute -50
+KPX F rcaron -50
+KPX F rcommaaccent -50
+KPX J A -25
+KPX J Aacute -25
+KPX J Abreve -25
+KPX J Acircumflex -25
+KPX J Adieresis -25
+KPX J Agrave -25
+KPX J Amacron -25
+KPX J Aogonek -25
+KPX J Aring -25
+KPX J Atilde -25
+KPX J a -40
+KPX J aacute -40
+KPX J abreve -40
+KPX J acircumflex -40
+KPX J adieresis -40
+KPX J agrave -40
+KPX J amacron -40
+KPX J aogonek -40
+KPX J aring -40
+KPX J atilde -40
+KPX J comma -10
+KPX J e -40
+KPX J eacute -40
+KPX J ecaron -40
+KPX J ecircumflex -40
+KPX J edieresis -40
+KPX J edotaccent -40
+KPX J egrave -40
+KPX J emacron -40
+KPX J eogonek -40
+KPX J o -40
+KPX J oacute -40
+KPX J ocircumflex -40
+KPX J odieresis -40
+KPX J ograve -40
+KPX J ohungarumlaut -40
+KPX J omacron -40
+KPX J oslash -40
+KPX J otilde -40
+KPX J period -10
+KPX J u -40
+KPX J uacute -40
+KPX J ucircumflex -40
+KPX J udieresis -40
+KPX J ugrave -40
+KPX J uhungarumlaut -40
+KPX J umacron -40
+KPX J uogonek -40
+KPX J uring -40
+KPX K O -30
+KPX K Oacute -30
+KPX K Ocircumflex -30
+KPX K Odieresis -30
+KPX K Ograve -30
+KPX K Ohungarumlaut -30
+KPX K Omacron -30
+KPX K Oslash -30
+KPX K Otilde -30
+KPX K e -25
+KPX K eacute -25
+KPX K ecaron -25
+KPX K ecircumflex -25
+KPX K edieresis -25
+KPX K edotaccent -25
+KPX K egrave -25
+KPX K emacron -25
+KPX K eogonek -25
+KPX K o -25
+KPX K oacute -25
+KPX K ocircumflex -25
+KPX K odieresis -25
+KPX K ograve -25
+KPX K ohungarumlaut -25
+KPX K omacron -25
+KPX K oslash -25
+KPX K otilde -25
+KPX K u -20
+KPX K uacute -20
+KPX K ucircumflex -20
+KPX K udieresis -20
+KPX K ugrave -20
+KPX K uhungarumlaut -20
+KPX K umacron -20
+KPX K uogonek -20
+KPX K uring -20
+KPX K y -20
+KPX K yacute -20
+KPX K ydieresis -20
+KPX Kcommaaccent O -30
+KPX Kcommaaccent Oacute -30
+KPX Kcommaaccent Ocircumflex -30
+KPX Kcommaaccent Odieresis -30
+KPX Kcommaaccent Ograve -30
+KPX Kcommaaccent Ohungarumlaut -30
+KPX Kcommaaccent Omacron -30
+KPX Kcommaaccent Oslash -30
+KPX Kcommaaccent Otilde -30
+KPX Kcommaaccent e -25
+KPX Kcommaaccent eacute -25
+KPX Kcommaaccent ecaron -25
+KPX Kcommaaccent ecircumflex -25
+KPX Kcommaaccent edieresis -25
+KPX Kcommaaccent edotaccent -25
+KPX Kcommaaccent egrave -25
+KPX Kcommaaccent emacron -25
+KPX Kcommaaccent eogonek -25
+KPX Kcommaaccent o -25
+KPX Kcommaaccent oacute -25
+KPX Kcommaaccent ocircumflex -25
+KPX Kcommaaccent odieresis -25
+KPX Kcommaaccent ograve -25
+KPX Kcommaaccent ohungarumlaut -25
+KPX Kcommaaccent omacron -25
+KPX Kcommaaccent oslash -25
+KPX Kcommaaccent otilde -25
+KPX Kcommaaccent u -20
+KPX Kcommaaccent uacute -20
+KPX Kcommaaccent ucircumflex -20
+KPX Kcommaaccent udieresis -20
+KPX Kcommaaccent ugrave -20
+KPX Kcommaaccent uhungarumlaut -20
+KPX Kcommaaccent umacron -20
+KPX Kcommaaccent uogonek -20
+KPX Kcommaaccent uring -20
+KPX Kcommaaccent y -20
+KPX Kcommaaccent yacute -20
+KPX Kcommaaccent ydieresis -20
+KPX L T -18
+KPX L Tcaron -18
+KPX L Tcommaaccent -18
+KPX L V -37
+KPX L W -37
+KPX L Y -37
+KPX L Yacute -37
+KPX L Ydieresis -37
+KPX L quoteright -55
+KPX L y -37
+KPX L yacute -37
+KPX L ydieresis -37
+KPX Lacute T -18
+KPX Lacute Tcaron -18
+KPX Lacute Tcommaaccent -18
+KPX Lacute V -37
+KPX Lacute W -37
+KPX Lacute Y -37
+KPX Lacute Yacute -37
+KPX Lacute Ydieresis -37
+KPX Lacute quoteright -55
+KPX Lacute y -37
+KPX Lacute yacute -37
+KPX Lacute ydieresis -37
+KPX Lcommaaccent T -18
+KPX Lcommaaccent Tcaron -18
+KPX Lcommaaccent Tcommaaccent -18
+KPX Lcommaaccent V -37
+KPX Lcommaaccent W -37
+KPX Lcommaaccent Y -37
+KPX Lcommaaccent Yacute -37
+KPX Lcommaaccent Ydieresis -37
+KPX Lcommaaccent quoteright -55
+KPX Lcommaaccent y -37
+KPX Lcommaaccent yacute -37
+KPX Lcommaaccent ydieresis -37
+KPX Lslash T -18
+KPX Lslash Tcaron -18
+KPX Lslash Tcommaaccent -18
+KPX Lslash V -37
+KPX Lslash W -37
+KPX Lslash Y -37
+KPX Lslash Yacute -37
+KPX Lslash Ydieresis -37
+KPX Lslash quoteright -55
+KPX Lslash y -37
+KPX Lslash yacute -37
+KPX Lslash ydieresis -37
+KPX N A -30
+KPX N Aacute -30
+KPX N Abreve -30
+KPX N Acircumflex -30
+KPX N Adieresis -30
+KPX N Agrave -30
+KPX N Amacron -30
+KPX N Aogonek -30
+KPX N Aring -30
+KPX N Atilde -30
+KPX Nacute A -30
+KPX Nacute Aacute -30
+KPX Nacute Abreve -30
+KPX Nacute Acircumflex -30
+KPX Nacute Adieresis -30
+KPX Nacute Agrave -30
+KPX Nacute Amacron -30
+KPX Nacute Aogonek -30
+KPX Nacute Aring -30
+KPX Nacute Atilde -30
+KPX Ncaron A -30
+KPX Ncaron Aacute -30
+KPX Ncaron Abreve -30
+KPX Ncaron Acircumflex -30
+KPX Ncaron Adieresis -30
+KPX Ncaron Agrave -30
+KPX Ncaron Amacron -30
+KPX Ncaron Aogonek -30
+KPX Ncaron Aring -30
+KPX Ncaron Atilde -30
+KPX Ncommaaccent A -30
+KPX Ncommaaccent Aacute -30
+KPX Ncommaaccent Abreve -30
+KPX Ncommaaccent Acircumflex -30
+KPX Ncommaaccent Adieresis -30
+KPX Ncommaaccent Agrave -30
+KPX Ncommaaccent Amacron -30
+KPX Ncommaaccent Aogonek -30
+KPX Ncommaaccent Aring -30
+KPX Ncommaaccent Atilde -30
+KPX Ntilde A -30
+KPX Ntilde Aacute -30
+KPX Ntilde Abreve -30
+KPX Ntilde Acircumflex -30
+KPX Ntilde Adieresis -30
+KPX Ntilde Agrave -30
+KPX Ntilde Amacron -30
+KPX Ntilde Aogonek -30
+KPX Ntilde Aring -30
+KPX Ntilde Atilde -30
+KPX O A -40
+KPX O Aacute -40
+KPX O Abreve -40
+KPX O Acircumflex -40
+KPX O Adieresis -40
+KPX O Agrave -40
+KPX O Amacron -40
+KPX O Aogonek -40
+KPX O Aring -40
+KPX O Atilde -40
+KPX O T -40
+KPX O Tcaron -40
+KPX O Tcommaaccent -40
+KPX O V -50
+KPX O W -50
+KPX O X -40
+KPX O Y -50
+KPX O Yacute -50
+KPX O Ydieresis -50
+KPX Oacute A -40
+KPX Oacute Aacute -40
+KPX Oacute Abreve -40
+KPX Oacute Acircumflex -40
+KPX Oacute Adieresis -40
+KPX Oacute Agrave -40
+KPX Oacute Amacron -40
+KPX Oacute Aogonek -40
+KPX Oacute Aring -40
+KPX Oacute Atilde -40
+KPX Oacute T -40
+KPX Oacute Tcaron -40
+KPX Oacute Tcommaaccent -40
+KPX Oacute V -50
+KPX Oacute W -50
+KPX Oacute X -40
+KPX Oacute Y -50
+KPX Oacute Yacute -50
+KPX Oacute Ydieresis -50
+KPX Ocircumflex A -40
+KPX Ocircumflex Aacute -40
+KPX Ocircumflex Abreve -40
+KPX Ocircumflex Acircumflex -40
+KPX Ocircumflex Adieresis -40
+KPX Ocircumflex Agrave -40
+KPX Ocircumflex Amacron -40
+KPX Ocircumflex Aogonek -40
+KPX Ocircumflex Aring -40
+KPX Ocircumflex Atilde -40
+KPX Ocircumflex T -40
+KPX Ocircumflex Tcaron -40
+KPX Ocircumflex Tcommaaccent -40
+KPX Ocircumflex V -50
+KPX Ocircumflex W -50
+KPX Ocircumflex X -40
+KPX Ocircumflex Y -50
+KPX Ocircumflex Yacute -50
+KPX Ocircumflex Ydieresis -50
+KPX Odieresis A -40
+KPX Odieresis Aacute -40
+KPX Odieresis Abreve -40
+KPX Odieresis Acircumflex -40
+KPX Odieresis Adieresis -40
+KPX Odieresis Agrave -40
+KPX Odieresis Amacron -40
+KPX Odieresis Aogonek -40
+KPX Odieresis Aring -40
+KPX Odieresis Atilde -40
+KPX Odieresis T -40
+KPX Odieresis Tcaron -40
+KPX Odieresis Tcommaaccent -40
+KPX Odieresis V -50
+KPX Odieresis W -50
+KPX Odieresis X -40
+KPX Odieresis Y -50
+KPX Odieresis Yacute -50
+KPX Odieresis Ydieresis -50
+KPX Ograve A -40
+KPX Ograve Aacute -40
+KPX Ograve Abreve -40
+KPX Ograve Acircumflex -40
+KPX Ograve Adieresis -40
+KPX Ograve Agrave -40
+KPX Ograve Amacron -40
+KPX Ograve Aogonek -40
+KPX Ograve Aring -40
+KPX Ograve Atilde -40
+KPX Ograve T -40
+KPX Ograve Tcaron -40
+KPX Ograve Tcommaaccent -40
+KPX Ograve V -50
+KPX Ograve W -50
+KPX Ograve X -40
+KPX Ograve Y -50
+KPX Ograve Yacute -50
+KPX Ograve Ydieresis -50
+KPX Ohungarumlaut A -40
+KPX Ohungarumlaut Aacute -40
+KPX Ohungarumlaut Abreve -40
+KPX Ohungarumlaut Acircumflex -40
+KPX Ohungarumlaut Adieresis -40
+KPX Ohungarumlaut Agrave -40
+KPX Ohungarumlaut Amacron -40
+KPX Ohungarumlaut Aogonek -40
+KPX Ohungarumlaut Aring -40
+KPX Ohungarumlaut Atilde -40
+KPX Ohungarumlaut T -40
+KPX Ohungarumlaut Tcaron -40
+KPX Ohungarumlaut Tcommaaccent -40
+KPX Ohungarumlaut V -50
+KPX Ohungarumlaut W -50
+KPX Ohungarumlaut X -40
+KPX Ohungarumlaut Y -50
+KPX Ohungarumlaut Yacute -50
+KPX Ohungarumlaut Ydieresis -50
+KPX Omacron A -40
+KPX Omacron Aacute -40
+KPX Omacron Abreve -40
+KPX Omacron Acircumflex -40
+KPX Omacron Adieresis -40
+KPX Omacron Agrave -40
+KPX Omacron Amacron -40
+KPX Omacron Aogonek -40
+KPX Omacron Aring -40
+KPX Omacron Atilde -40
+KPX Omacron T -40
+KPX Omacron Tcaron -40
+KPX Omacron Tcommaaccent -40
+KPX Omacron V -50
+KPX Omacron W -50
+KPX Omacron X -40
+KPX Omacron Y -50
+KPX Omacron Yacute -50
+KPX Omacron Ydieresis -50
+KPX Oslash A -40
+KPX Oslash Aacute -40
+KPX Oslash Abreve -40
+KPX Oslash Acircumflex -40
+KPX Oslash Adieresis -40
+KPX Oslash Agrave -40
+KPX Oslash Amacron -40
+KPX Oslash Aogonek -40
+KPX Oslash Aring -40
+KPX Oslash Atilde -40
+KPX Oslash T -40
+KPX Oslash Tcaron -40
+KPX Oslash Tcommaaccent -40
+KPX Oslash V -50
+KPX Oslash W -50
+KPX Oslash X -40
+KPX Oslash Y -50
+KPX Oslash Yacute -50
+KPX Oslash Ydieresis -50
+KPX Otilde A -40
+KPX Otilde Aacute -40
+KPX Otilde Abreve -40
+KPX Otilde Acircumflex -40
+KPX Otilde Adieresis -40
+KPX Otilde Agrave -40
+KPX Otilde Amacron -40
+KPX Otilde Aogonek -40
+KPX Otilde Aring -40
+KPX Otilde Atilde -40
+KPX Otilde T -40
+KPX Otilde Tcaron -40
+KPX Otilde Tcommaaccent -40
+KPX Otilde V -50
+KPX Otilde W -50
+KPX Otilde X -40
+KPX Otilde Y -50
+KPX Otilde Yacute -50
+KPX Otilde Ydieresis -50
+KPX P A -85
+KPX P Aacute -85
+KPX P Abreve -85
+KPX P Acircumflex -85
+KPX P Adieresis -85
+KPX P Agrave -85
+KPX P Amacron -85
+KPX P Aogonek -85
+KPX P Aring -85
+KPX P Atilde -85
+KPX P a -40
+KPX P aacute -40
+KPX P abreve -40
+KPX P acircumflex -40
+KPX P adieresis -40
+KPX P agrave -40
+KPX P amacron -40
+KPX P aogonek -40
+KPX P aring -40
+KPX P atilde -40
+KPX P comma -129
+KPX P e -50
+KPX P eacute -50
+KPX P ecaron -50
+KPX P ecircumflex -50
+KPX P edieresis -50
+KPX P edotaccent -50
+KPX P egrave -50
+KPX P emacron -50
+KPX P eogonek -50
+KPX P o -55
+KPX P oacute -55
+KPX P ocircumflex -55
+KPX P odieresis -55
+KPX P ograve -55
+KPX P ohungarumlaut -55
+KPX P omacron -55
+KPX P oslash -55
+KPX P otilde -55
+KPX P period -129
+KPX Q U -10
+KPX Q Uacute -10
+KPX Q Ucircumflex -10
+KPX Q Udieresis -10
+KPX Q Ugrave -10
+KPX Q Uhungarumlaut -10
+KPX Q Umacron -10
+KPX Q Uogonek -10
+KPX Q Uring -10
+KPX R O -40
+KPX R Oacute -40
+KPX R Ocircumflex -40
+KPX R Odieresis -40
+KPX R Ograve -40
+KPX R Ohungarumlaut -40
+KPX R Omacron -40
+KPX R Oslash -40
+KPX R Otilde -40
+KPX R T -30
+KPX R Tcaron -30
+KPX R Tcommaaccent -30
+KPX R U -40
+KPX R Uacute -40
+KPX R Ucircumflex -40
+KPX R Udieresis -40
+KPX R Ugrave -40
+KPX R Uhungarumlaut -40
+KPX R Umacron -40
+KPX R Uogonek -40
+KPX R Uring -40
+KPX R V -18
+KPX R W -18
+KPX R Y -18
+KPX R Yacute -18
+KPX R Ydieresis -18
+KPX Racute O -40
+KPX Racute Oacute -40
+KPX Racute Ocircumflex -40
+KPX Racute Odieresis -40
+KPX Racute Ograve -40
+KPX Racute Ohungarumlaut -40
+KPX Racute Omacron -40
+KPX Racute Oslash -40
+KPX Racute Otilde -40
+KPX Racute T -30
+KPX Racute Tcaron -30
+KPX Racute Tcommaaccent -30
+KPX Racute U -40
+KPX Racute Uacute -40
+KPX Racute Ucircumflex -40
+KPX Racute Udieresis -40
+KPX Racute Ugrave -40
+KPX Racute Uhungarumlaut -40
+KPX Racute Umacron -40
+KPX Racute Uogonek -40
+KPX Racute Uring -40
+KPX Racute V -18
+KPX Racute W -18
+KPX Racute Y -18
+KPX Racute Yacute -18
+KPX Racute Ydieresis -18
+KPX Rcaron O -40
+KPX Rcaron Oacute -40
+KPX Rcaron Ocircumflex -40
+KPX Rcaron Odieresis -40
+KPX Rcaron Ograve -40
+KPX Rcaron Ohungarumlaut -40
+KPX Rcaron Omacron -40
+KPX Rcaron Oslash -40
+KPX Rcaron Otilde -40
+KPX Rcaron T -30
+KPX Rcaron Tcaron -30
+KPX Rcaron Tcommaaccent -30
+KPX Rcaron U -40
+KPX Rcaron Uacute -40
+KPX Rcaron Ucircumflex -40
+KPX Rcaron Udieresis -40
+KPX Rcaron Ugrave -40
+KPX Rcaron Uhungarumlaut -40
+KPX Rcaron Umacron -40
+KPX Rcaron Uogonek -40
+KPX Rcaron Uring -40
+KPX Rcaron V -18
+KPX Rcaron W -18
+KPX Rcaron Y -18
+KPX Rcaron Yacute -18
+KPX Rcaron Ydieresis -18
+KPX Rcommaaccent O -40
+KPX Rcommaaccent Oacute -40
+KPX Rcommaaccent Ocircumflex -40
+KPX Rcommaaccent Odieresis -40
+KPX Rcommaaccent Ograve -40
+KPX Rcommaaccent Ohungarumlaut -40
+KPX Rcommaaccent Omacron -40
+KPX Rcommaaccent Oslash -40
+KPX Rcommaaccent Otilde -40
+KPX Rcommaaccent T -30
+KPX Rcommaaccent Tcaron -30
+KPX Rcommaaccent Tcommaaccent -30
+KPX Rcommaaccent U -40
+KPX Rcommaaccent Uacute -40
+KPX Rcommaaccent Ucircumflex -40
+KPX Rcommaaccent Udieresis -40
+KPX Rcommaaccent Ugrave -40
+KPX Rcommaaccent Uhungarumlaut -40
+KPX Rcommaaccent Umacron -40
+KPX Rcommaaccent Uogonek -40
+KPX Rcommaaccent Uring -40
+KPX Rcommaaccent V -18
+KPX Rcommaaccent W -18
+KPX Rcommaaccent Y -18
+KPX Rcommaaccent Yacute -18
+KPX Rcommaaccent Ydieresis -18
+KPX T A -55
+KPX T Aacute -55
+KPX T Abreve -55
+KPX T Acircumflex -55
+KPX T Adieresis -55
+KPX T Agrave -55
+KPX T Amacron -55
+KPX T Aogonek -55
+KPX T Aring -55
+KPX T Atilde -55
+KPX T O -18
+KPX T Oacute -18
+KPX T Ocircumflex -18
+KPX T Odieresis -18
+KPX T Ograve -18
+KPX T Ohungarumlaut -18
+KPX T Omacron -18
+KPX T Oslash -18
+KPX T Otilde -18
+KPX T a -92
+KPX T aacute -92
+KPX T abreve -92
+KPX T acircumflex -92
+KPX T adieresis -92
+KPX T agrave -92
+KPX T amacron -92
+KPX T aogonek -92
+KPX T aring -92
+KPX T atilde -92
+KPX T colon -74
+KPX T comma -92
+KPX T e -92
+KPX T eacute -92
+KPX T ecaron -92
+KPX T ecircumflex -92
+KPX T edieresis -52
+KPX T edotaccent -92
+KPX T egrave -52
+KPX T emacron -52
+KPX T eogonek -92
+KPX T hyphen -92
+KPX T i -37
+KPX T iacute -37
+KPX T iogonek -37
+KPX T o -95
+KPX T oacute -95
+KPX T ocircumflex -95
+KPX T odieresis -95
+KPX T ograve -95
+KPX T ohungarumlaut -95
+KPX T omacron -95
+KPX T oslash -95
+KPX T otilde -95
+KPX T period -92
+KPX T r -37
+KPX T racute -37
+KPX T rcaron -37
+KPX T rcommaaccent -37
+KPX T semicolon -74
+KPX T u -37
+KPX T uacute -37
+KPX T ucircumflex -37
+KPX T udieresis -37
+KPX T ugrave -37
+KPX T uhungarumlaut -37
+KPX T umacron -37
+KPX T uogonek -37
+KPX T uring -37
+KPX T w -37
+KPX T y -37
+KPX T yacute -37
+KPX T ydieresis -37
+KPX Tcaron A -55
+KPX Tcaron Aacute -55
+KPX Tcaron Abreve -55
+KPX Tcaron Acircumflex -55
+KPX Tcaron Adieresis -55
+KPX Tcaron Agrave -55
+KPX Tcaron Amacron -55
+KPX Tcaron Aogonek -55
+KPX Tcaron Aring -55
+KPX Tcaron Atilde -55
+KPX Tcaron O -18
+KPX Tcaron Oacute -18
+KPX Tcaron Ocircumflex -18
+KPX Tcaron Odieresis -18
+KPX Tcaron Ograve -18
+KPX Tcaron Ohungarumlaut -18
+KPX Tcaron Omacron -18
+KPX Tcaron Oslash -18
+KPX Tcaron Otilde -18
+KPX Tcaron a -92
+KPX Tcaron aacute -92
+KPX Tcaron abreve -92
+KPX Tcaron acircumflex -92
+KPX Tcaron adieresis -92
+KPX Tcaron agrave -92
+KPX Tcaron amacron -92
+KPX Tcaron aogonek -92
+KPX Tcaron aring -92
+KPX Tcaron atilde -92
+KPX Tcaron colon -74
+KPX Tcaron comma -92
+KPX Tcaron e -92
+KPX Tcaron eacute -92
+KPX Tcaron ecaron -92
+KPX Tcaron ecircumflex -92
+KPX Tcaron edieresis -52
+KPX Tcaron edotaccent -92
+KPX Tcaron egrave -52
+KPX Tcaron emacron -52
+KPX Tcaron eogonek -92
+KPX Tcaron hyphen -92
+KPX Tcaron i -37
+KPX Tcaron iacute -37
+KPX Tcaron iogonek -37
+KPX Tcaron o -95
+KPX Tcaron oacute -95
+KPX Tcaron ocircumflex -95
+KPX Tcaron odieresis -95
+KPX Tcaron ograve -95
+KPX Tcaron ohungarumlaut -95
+KPX Tcaron omacron -95
+KPX Tcaron oslash -95
+KPX Tcaron otilde -95
+KPX Tcaron period -92
+KPX Tcaron r -37
+KPX Tcaron racute -37
+KPX Tcaron rcaron -37
+KPX Tcaron rcommaaccent -37
+KPX Tcaron semicolon -74
+KPX Tcaron u -37
+KPX Tcaron uacute -37
+KPX Tcaron ucircumflex -37
+KPX Tcaron udieresis -37
+KPX Tcaron ugrave -37
+KPX Tcaron uhungarumlaut -37
+KPX Tcaron umacron -37
+KPX Tcaron uogonek -37
+KPX Tcaron uring -37
+KPX Tcaron w -37
+KPX Tcaron y -37
+KPX Tcaron yacute -37
+KPX Tcaron ydieresis -37
+KPX Tcommaaccent A -55
+KPX Tcommaaccent Aacute -55
+KPX Tcommaaccent Abreve -55
+KPX Tcommaaccent Acircumflex -55
+KPX Tcommaaccent Adieresis -55
+KPX Tcommaaccent Agrave -55
+KPX Tcommaaccent Amacron -55
+KPX Tcommaaccent Aogonek -55
+KPX Tcommaaccent Aring -55
+KPX Tcommaaccent Atilde -55
+KPX Tcommaaccent O -18
+KPX Tcommaaccent Oacute -18
+KPX Tcommaaccent Ocircumflex -18
+KPX Tcommaaccent Odieresis -18
+KPX Tcommaaccent Ograve -18
+KPX Tcommaaccent Ohungarumlaut -18
+KPX Tcommaaccent Omacron -18
+KPX Tcommaaccent Oslash -18
+KPX Tcommaaccent Otilde -18
+KPX Tcommaaccent a -92
+KPX Tcommaaccent aacute -92
+KPX Tcommaaccent abreve -92
+KPX Tcommaaccent acircumflex -92
+KPX Tcommaaccent adieresis -92
+KPX Tcommaaccent agrave -92
+KPX Tcommaaccent amacron -92
+KPX Tcommaaccent aogonek -92
+KPX Tcommaaccent aring -92
+KPX Tcommaaccent atilde -92
+KPX Tcommaaccent colon -74
+KPX Tcommaaccent comma -92
+KPX Tcommaaccent e -92
+KPX Tcommaaccent eacute -92
+KPX Tcommaaccent ecaron -92
+KPX Tcommaaccent ecircumflex -92
+KPX Tcommaaccent edieresis -52
+KPX Tcommaaccent edotaccent -92
+KPX Tcommaaccent egrave -52
+KPX Tcommaaccent emacron -52
+KPX Tcommaaccent eogonek -92
+KPX Tcommaaccent hyphen -92
+KPX Tcommaaccent i -37
+KPX Tcommaaccent iacute -37
+KPX Tcommaaccent iogonek -37
+KPX Tcommaaccent o -95
+KPX Tcommaaccent oacute -95
+KPX Tcommaaccent ocircumflex -95
+KPX Tcommaaccent odieresis -95
+KPX Tcommaaccent ograve -95
+KPX Tcommaaccent ohungarumlaut -95
+KPX Tcommaaccent omacron -95
+KPX Tcommaaccent oslash -95
+KPX Tcommaaccent otilde -95
+KPX Tcommaaccent period -92
+KPX Tcommaaccent r -37
+KPX Tcommaaccent racute -37
+KPX Tcommaaccent rcaron -37
+KPX Tcommaaccent rcommaaccent -37
+KPX Tcommaaccent semicolon -74
+KPX Tcommaaccent u -37
+KPX Tcommaaccent uacute -37
+KPX Tcommaaccent ucircumflex -37
+KPX Tcommaaccent udieresis -37
+KPX Tcommaaccent ugrave -37
+KPX Tcommaaccent uhungarumlaut -37
+KPX Tcommaaccent umacron -37
+KPX Tcommaaccent uogonek -37
+KPX Tcommaaccent uring -37
+KPX Tcommaaccent w -37
+KPX Tcommaaccent y -37
+KPX Tcommaaccent yacute -37
+KPX Tcommaaccent ydieresis -37
+KPX U A -45
+KPX U Aacute -45
+KPX U Abreve -45
+KPX U Acircumflex -45
+KPX U Adieresis -45
+KPX U Agrave -45
+KPX U Amacron -45
+KPX U Aogonek -45
+KPX U Aring -45
+KPX U Atilde -45
+KPX Uacute A -45
+KPX Uacute Aacute -45
+KPX Uacute Abreve -45
+KPX Uacute Acircumflex -45
+KPX Uacute Adieresis -45
+KPX Uacute Agrave -45
+KPX Uacute Amacron -45
+KPX Uacute Aogonek -45
+KPX Uacute Aring -45
+KPX Uacute Atilde -45
+KPX Ucircumflex A -45
+KPX Ucircumflex Aacute -45
+KPX Ucircumflex Abreve -45
+KPX Ucircumflex Acircumflex -45
+KPX Ucircumflex Adieresis -45
+KPX Ucircumflex Agrave -45
+KPX Ucircumflex Amacron -45
+KPX Ucircumflex Aogonek -45
+KPX Ucircumflex Aring -45
+KPX Ucircumflex Atilde -45
+KPX Udieresis A -45
+KPX Udieresis Aacute -45
+KPX Udieresis Abreve -45
+KPX Udieresis Acircumflex -45
+KPX Udieresis Adieresis -45
+KPX Udieresis Agrave -45
+KPX Udieresis Amacron -45
+KPX Udieresis Aogonek -45
+KPX Udieresis Aring -45
+KPX Udieresis Atilde -45
+KPX Ugrave A -45
+KPX Ugrave Aacute -45
+KPX Ugrave Abreve -45
+KPX Ugrave Acircumflex -45
+KPX Ugrave Adieresis -45
+KPX Ugrave Agrave -45
+KPX Ugrave Amacron -45
+KPX Ugrave Aogonek -45
+KPX Ugrave Aring -45
+KPX Ugrave Atilde -45
+KPX Uhungarumlaut A -45
+KPX Uhungarumlaut Aacute -45
+KPX Uhungarumlaut Abreve -45
+KPX Uhungarumlaut Acircumflex -45
+KPX Uhungarumlaut Adieresis -45
+KPX Uhungarumlaut Agrave -45
+KPX Uhungarumlaut Amacron -45
+KPX Uhungarumlaut Aogonek -45
+KPX Uhungarumlaut Aring -45
+KPX Uhungarumlaut Atilde -45
+KPX Umacron A -45
+KPX Umacron Aacute -45
+KPX Umacron Abreve -45
+KPX Umacron Acircumflex -45
+KPX Umacron Adieresis -45
+KPX Umacron Agrave -45
+KPX Umacron Amacron -45
+KPX Umacron Aogonek -45
+KPX Umacron Aring -45
+KPX Umacron Atilde -45
+KPX Uogonek A -45
+KPX Uogonek Aacute -45
+KPX Uogonek Abreve -45
+KPX Uogonek Acircumflex -45
+KPX Uogonek Adieresis -45
+KPX Uogonek Agrave -45
+KPX Uogonek Amacron -45
+KPX Uogonek Aogonek -45
+KPX Uogonek Aring -45
+KPX Uogonek Atilde -45
+KPX Uring A -45
+KPX Uring Aacute -45
+KPX Uring Abreve -45
+KPX Uring Acircumflex -45
+KPX Uring Adieresis -45
+KPX Uring Agrave -45
+KPX Uring Amacron -45
+KPX Uring Aogonek -45
+KPX Uring Aring -45
+KPX Uring Atilde -45
+KPX V A -85
+KPX V Aacute -85
+KPX V Abreve -85
+KPX V Acircumflex -85
+KPX V Adieresis -85
+KPX V Agrave -85
+KPX V Amacron -85
+KPX V Aogonek -85
+KPX V Aring -85
+KPX V Atilde -85
+KPX V G -10
+KPX V Gbreve -10
+KPX V Gcommaaccent -10
+KPX V O -30
+KPX V Oacute -30
+KPX V Ocircumflex -30
+KPX V Odieresis -30
+KPX V Ograve -30
+KPX V Ohungarumlaut -30
+KPX V Omacron -30
+KPX V Oslash -30
+KPX V Otilde -30
+KPX V a -111
+KPX V aacute -111
+KPX V abreve -111
+KPX V acircumflex -111
+KPX V adieresis -111
+KPX V agrave -111
+KPX V amacron -111
+KPX V aogonek -111
+KPX V aring -111
+KPX V atilde -111
+KPX V colon -74
+KPX V comma -129
+KPX V e -111
+KPX V eacute -111
+KPX V ecaron -111
+KPX V ecircumflex -111
+KPX V edieresis -71
+KPX V edotaccent -111
+KPX V egrave -71
+KPX V emacron -71
+KPX V eogonek -111
+KPX V hyphen -70
+KPX V i -55
+KPX V iacute -55
+KPX V iogonek -55
+KPX V o -111
+KPX V oacute -111
+KPX V ocircumflex -111
+KPX V odieresis -111
+KPX V ograve -111
+KPX V ohungarumlaut -111
+KPX V omacron -111
+KPX V oslash -111
+KPX V otilde -111
+KPX V period -129
+KPX V semicolon -74
+KPX V u -55
+KPX V uacute -55
+KPX V ucircumflex -55
+KPX V udieresis -55
+KPX V ugrave -55
+KPX V uhungarumlaut -55
+KPX V umacron -55
+KPX V uogonek -55
+KPX V uring -55
+KPX W A -74
+KPX W Aacute -74
+KPX W Abreve -74
+KPX W Acircumflex -74
+KPX W Adieresis -74
+KPX W Agrave -74
+KPX W Amacron -74
+KPX W Aogonek -74
+KPX W Aring -74
+KPX W Atilde -74
+KPX W O -15
+KPX W Oacute -15
+KPX W Ocircumflex -15
+KPX W Odieresis -15
+KPX W Ograve -15
+KPX W Ohungarumlaut -15
+KPX W Omacron -15
+KPX W Oslash -15
+KPX W Otilde -15
+KPX W a -85
+KPX W aacute -85
+KPX W abreve -85
+KPX W acircumflex -85
+KPX W adieresis -85
+KPX W agrave -85
+KPX W amacron -85
+KPX W aogonek -85
+KPX W aring -85
+KPX W atilde -85
+KPX W colon -55
+KPX W comma -74
+KPX W e -90
+KPX W eacute -90
+KPX W ecaron -90
+KPX W ecircumflex -90
+KPX W edieresis -50
+KPX W edotaccent -90
+KPX W egrave -50
+KPX W emacron -50
+KPX W eogonek -90
+KPX W hyphen -50
+KPX W i -37
+KPX W iacute -37
+KPX W iogonek -37
+KPX W o -80
+KPX W oacute -80
+KPX W ocircumflex -80
+KPX W odieresis -80
+KPX W ograve -80
+KPX W ohungarumlaut -80
+KPX W omacron -80
+KPX W oslash -80
+KPX W otilde -80
+KPX W period -74
+KPX W semicolon -55
+KPX W u -55
+KPX W uacute -55
+KPX W ucircumflex -55
+KPX W udieresis -55
+KPX W ugrave -55
+KPX W uhungarumlaut -55
+KPX W umacron -55
+KPX W uogonek -55
+KPX W uring -55
+KPX W y -55
+KPX W yacute -55
+KPX W ydieresis -55
+KPX Y A -74
+KPX Y Aacute -74
+KPX Y Abreve -74
+KPX Y Acircumflex -74
+KPX Y Adieresis -74
+KPX Y Agrave -74
+KPX Y Amacron -74
+KPX Y Aogonek -74
+KPX Y Aring -74
+KPX Y Atilde -74
+KPX Y O -25
+KPX Y Oacute -25
+KPX Y Ocircumflex -25
+KPX Y Odieresis -25
+KPX Y Ograve -25
+KPX Y Ohungarumlaut -25
+KPX Y Omacron -25
+KPX Y Oslash -25
+KPX Y Otilde -25
+KPX Y a -92
+KPX Y aacute -92
+KPX Y abreve -92
+KPX Y acircumflex -92
+KPX Y adieresis -92
+KPX Y agrave -92
+KPX Y amacron -92
+KPX Y aogonek -92
+KPX Y aring -92
+KPX Y atilde -92
+KPX Y colon -92
+KPX Y comma -92
+KPX Y e -111
+KPX Y eacute -111
+KPX Y ecaron -111
+KPX Y ecircumflex -71
+KPX Y edieresis -71
+KPX Y edotaccent -111
+KPX Y egrave -71
+KPX Y emacron -71
+KPX Y eogonek -111
+KPX Y hyphen -92
+KPX Y i -55
+KPX Y iacute -55
+KPX Y iogonek -55
+KPX Y o -111
+KPX Y oacute -111
+KPX Y ocircumflex -111
+KPX Y odieresis -111
+KPX Y ograve -111
+KPX Y ohungarumlaut -111
+KPX Y omacron -111
+KPX Y oslash -111
+KPX Y otilde -111
+KPX Y period -74
+KPX Y semicolon -92
+KPX Y u -92
+KPX Y uacute -92
+KPX Y ucircumflex -92
+KPX Y udieresis -92
+KPX Y ugrave -92
+KPX Y uhungarumlaut -92
+KPX Y umacron -92
+KPX Y uogonek -92
+KPX Y uring -92
+KPX Yacute A -74
+KPX Yacute Aacute -74
+KPX Yacute Abreve -74
+KPX Yacute Acircumflex -74
+KPX Yacute Adieresis -74
+KPX Yacute Agrave -74
+KPX Yacute Amacron -74
+KPX Yacute Aogonek -74
+KPX Yacute Aring -74
+KPX Yacute Atilde -74
+KPX Yacute O -25
+KPX Yacute Oacute -25
+KPX Yacute Ocircumflex -25
+KPX Yacute Odieresis -25
+KPX Yacute Ograve -25
+KPX Yacute Ohungarumlaut -25
+KPX Yacute Omacron -25
+KPX Yacute Oslash -25
+KPX Yacute Otilde -25
+KPX Yacute a -92
+KPX Yacute aacute -92
+KPX Yacute abreve -92
+KPX Yacute acircumflex -92
+KPX Yacute adieresis -92
+KPX Yacute agrave -92
+KPX Yacute amacron -92
+KPX Yacute aogonek -92
+KPX Yacute aring -92
+KPX Yacute atilde -92
+KPX Yacute colon -92
+KPX Yacute comma -92
+KPX Yacute e -111
+KPX Yacute eacute -111
+KPX Yacute ecaron -111
+KPX Yacute ecircumflex -71
+KPX Yacute edieresis -71
+KPX Yacute edotaccent -111
+KPX Yacute egrave -71
+KPX Yacute emacron -71
+KPX Yacute eogonek -111
+KPX Yacute hyphen -92
+KPX Yacute i -55
+KPX Yacute iacute -55
+KPX Yacute iogonek -55
+KPX Yacute o -111
+KPX Yacute oacute -111
+KPX Yacute ocircumflex -111
+KPX Yacute odieresis -111
+KPX Yacute ograve -111
+KPX Yacute ohungarumlaut -111
+KPX Yacute omacron -111
+KPX Yacute oslash -111
+KPX Yacute otilde -111
+KPX Yacute period -74
+KPX Yacute semicolon -92
+KPX Yacute u -92
+KPX Yacute uacute -92
+KPX Yacute ucircumflex -92
+KPX Yacute udieresis -92
+KPX Yacute ugrave -92
+KPX Yacute uhungarumlaut -92
+KPX Yacute umacron -92
+KPX Yacute uogonek -92
+KPX Yacute uring -92
+KPX Ydieresis A -74
+KPX Ydieresis Aacute -74
+KPX Ydieresis Abreve -74
+KPX Ydieresis Acircumflex -74
+KPX Ydieresis Adieresis -74
+KPX Ydieresis Agrave -74
+KPX Ydieresis Amacron -74
+KPX Ydieresis Aogonek -74
+KPX Ydieresis Aring -74
+KPX Ydieresis Atilde -74
+KPX Ydieresis O -25
+KPX Ydieresis Oacute -25
+KPX Ydieresis Ocircumflex -25
+KPX Ydieresis Odieresis -25
+KPX Ydieresis Ograve -25
+KPX Ydieresis Ohungarumlaut -25
+KPX Ydieresis Omacron -25
+KPX Ydieresis Oslash -25
+KPX Ydieresis Otilde -25
+KPX Ydieresis a -92
+KPX Ydieresis aacute -92
+KPX Ydieresis abreve -92
+KPX Ydieresis acircumflex -92
+KPX Ydieresis adieresis -92
+KPX Ydieresis agrave -92
+KPX Ydieresis amacron -92
+KPX Ydieresis aogonek -92
+KPX Ydieresis aring -92
+KPX Ydieresis atilde -92
+KPX Ydieresis colon -92
+KPX Ydieresis comma -92
+KPX Ydieresis e -111
+KPX Ydieresis eacute -111
+KPX Ydieresis ecaron -111
+KPX Ydieresis ecircumflex -71
+KPX Ydieresis edieresis -71
+KPX Ydieresis edotaccent -111
+KPX Ydieresis egrave -71
+KPX Ydieresis emacron -71
+KPX Ydieresis eogonek -111
+KPX Ydieresis hyphen -92
+KPX Ydieresis i -55
+KPX Ydieresis iacute -55
+KPX Ydieresis iogonek -55
+KPX Ydieresis o -111
+KPX Ydieresis oacute -111
+KPX Ydieresis ocircumflex -111
+KPX Ydieresis odieresis -111
+KPX Ydieresis ograve -111
+KPX Ydieresis ohungarumlaut -111
+KPX Ydieresis omacron -111
+KPX Ydieresis oslash -111
+KPX Ydieresis otilde -111
+KPX Ydieresis period -74
+KPX Ydieresis semicolon -92
+KPX Ydieresis u -92
+KPX Ydieresis uacute -92
+KPX Ydieresis ucircumflex -92
+KPX Ydieresis udieresis -92
+KPX Ydieresis ugrave -92
+KPX Ydieresis uhungarumlaut -92
+KPX Ydieresis umacron -92
+KPX Ydieresis uogonek -92
+KPX Ydieresis uring -92
+KPX b b -10
+KPX b period -40
+KPX b u -20
+KPX b uacute -20
+KPX b ucircumflex -20
+KPX b udieresis -20
+KPX b ugrave -20
+KPX b uhungarumlaut -20
+KPX b umacron -20
+KPX b uogonek -20
+KPX b uring -20
+KPX c h -10
+KPX c k -10
+KPX c kcommaaccent -10
+KPX cacute h -10
+KPX cacute k -10
+KPX cacute kcommaaccent -10
+KPX ccaron h -10
+KPX ccaron k -10
+KPX ccaron kcommaaccent -10
+KPX ccedilla h -10
+KPX ccedilla k -10
+KPX ccedilla kcommaaccent -10
+KPX comma quotedblright -95
+KPX comma quoteright -95
+KPX e b -10
+KPX eacute b -10
+KPX ecaron b -10
+KPX ecircumflex b -10
+KPX edieresis b -10
+KPX edotaccent b -10
+KPX egrave b -10
+KPX emacron b -10
+KPX eogonek b -10
+KPX f comma -10
+KPX f dotlessi -30
+KPX f e -10
+KPX f eacute -10
+KPX f edotaccent -10
+KPX f eogonek -10
+KPX f f -18
+KPX f o -10
+KPX f oacute -10
+KPX f ocircumflex -10
+KPX f ograve -10
+KPX f ohungarumlaut -10
+KPX f oslash -10
+KPX f otilde -10
+KPX f period -10
+KPX f quoteright 55
+KPX k e -30
+KPX k eacute -30
+KPX k ecaron -30
+KPX k ecircumflex -30
+KPX k edieresis -30
+KPX k edotaccent -30
+KPX k egrave -30
+KPX k emacron -30
+KPX k eogonek -30
+KPX k o -10
+KPX k oacute -10
+KPX k ocircumflex -10
+KPX k odieresis -10
+KPX k ograve -10
+KPX k ohungarumlaut -10
+KPX k omacron -10
+KPX k oslash -10
+KPX k otilde -10
+KPX kcommaaccent e -30
+KPX kcommaaccent eacute -30
+KPX kcommaaccent ecaron -30
+KPX kcommaaccent ecircumflex -30
+KPX kcommaaccent edieresis -30
+KPX kcommaaccent edotaccent -30
+KPX kcommaaccent egrave -30
+KPX kcommaaccent emacron -30
+KPX kcommaaccent eogonek -30
+KPX kcommaaccent o -10
+KPX kcommaaccent oacute -10
+KPX kcommaaccent ocircumflex -10
+KPX kcommaaccent odieresis -10
+KPX kcommaaccent ograve -10
+KPX kcommaaccent ohungarumlaut -10
+KPX kcommaaccent omacron -10
+KPX kcommaaccent oslash -10
+KPX kcommaaccent otilde -10
+KPX n v -40
+KPX nacute v -40
+KPX ncaron v -40
+KPX ncommaaccent v -40
+KPX ntilde v -40
+KPX o v -15
+KPX o w -25
+KPX o x -10
+KPX o y -10
+KPX o yacute -10
+KPX o ydieresis -10
+KPX oacute v -15
+KPX oacute w -25
+KPX oacute x -10
+KPX oacute y -10
+KPX oacute yacute -10
+KPX oacute ydieresis -10
+KPX ocircumflex v -15
+KPX ocircumflex w -25
+KPX ocircumflex x -10
+KPX ocircumflex y -10
+KPX ocircumflex yacute -10
+KPX ocircumflex ydieresis -10
+KPX odieresis v -15
+KPX odieresis w -25
+KPX odieresis x -10
+KPX odieresis y -10
+KPX odieresis yacute -10
+KPX odieresis ydieresis -10
+KPX ograve v -15
+KPX ograve w -25
+KPX ograve x -10
+KPX ograve y -10
+KPX ograve yacute -10
+KPX ograve ydieresis -10
+KPX ohungarumlaut v -15
+KPX ohungarumlaut w -25
+KPX ohungarumlaut x -10
+KPX ohungarumlaut y -10
+KPX ohungarumlaut yacute -10
+KPX ohungarumlaut ydieresis -10
+KPX omacron v -15
+KPX omacron w -25
+KPX omacron x -10
+KPX omacron y -10
+KPX omacron yacute -10
+KPX omacron ydieresis -10
+KPX oslash v -15
+KPX oslash w -25
+KPX oslash x -10
+KPX oslash y -10
+KPX oslash yacute -10
+KPX oslash ydieresis -10
+KPX otilde v -15
+KPX otilde w -25
+KPX otilde x -10
+KPX otilde y -10
+KPX otilde yacute -10
+KPX otilde ydieresis -10
+KPX period quotedblright -95
+KPX period quoteright -95
+KPX quoteleft quoteleft -74
+KPX quoteright d -15
+KPX quoteright dcroat -15
+KPX quoteright quoteright -74
+KPX quoteright r -15
+KPX quoteright racute -15
+KPX quoteright rcaron -15
+KPX quoteright rcommaaccent -15
+KPX quoteright s -74
+KPX quoteright sacute -74
+KPX quoteright scaron -74
+KPX quoteright scedilla -74
+KPX quoteright scommaaccent -74
+KPX quoteright space -74
+KPX quoteright t -37
+KPX quoteright tcommaaccent -37
+KPX quoteright v -15
+KPX r comma -65
+KPX r period -65
+KPX racute comma -65
+KPX racute period -65
+KPX rcaron comma -65
+KPX rcaron period -65
+KPX rcommaaccent comma -65
+KPX rcommaaccent period -65
+KPX space A -37
+KPX space Aacute -37
+KPX space Abreve -37
+KPX space Acircumflex -37
+KPX space Adieresis -37
+KPX space Agrave -37
+KPX space Amacron -37
+KPX space Aogonek -37
+KPX space Aring -37
+KPX space Atilde -37
+KPX space V -70
+KPX space W -70
+KPX space Y -70
+KPX space Yacute -70
+KPX space Ydieresis -70
+KPX v comma -37
+KPX v e -15
+KPX v eacute -15
+KPX v ecaron -15
+KPX v ecircumflex -15
+KPX v edieresis -15
+KPX v edotaccent -15
+KPX v egrave -15
+KPX v emacron -15
+KPX v eogonek -15
+KPX v o -15
+KPX v oacute -15
+KPX v ocircumflex -15
+KPX v odieresis -15
+KPX v ograve -15
+KPX v ohungarumlaut -15
+KPX v omacron -15
+KPX v oslash -15
+KPX v otilde -15
+KPX v period -37
+KPX w a -10
+KPX w aacute -10
+KPX w abreve -10
+KPX w acircumflex -10
+KPX w adieresis -10
+KPX w agrave -10
+KPX w amacron -10
+KPX w aogonek -10
+KPX w aring -10
+KPX w atilde -10
+KPX w comma -37
+KPX w e -10
+KPX w eacute -10
+KPX w ecaron -10
+KPX w ecircumflex -10
+KPX w edieresis -10
+KPX w edotaccent -10
+KPX w egrave -10
+KPX w emacron -10
+KPX w eogonek -10
+KPX w o -15
+KPX w oacute -15
+KPX w ocircumflex -15
+KPX w odieresis -15
+KPX w ograve -15
+KPX w ohungarumlaut -15
+KPX w omacron -15
+KPX w oslash -15
+KPX w otilde -15
+KPX w period -37
+KPX x e -10
+KPX x eacute -10
+KPX x ecaron -10
+KPX x ecircumflex -10
+KPX x edieresis -10
+KPX x edotaccent -10
+KPX x egrave -10
+KPX x emacron -10
+KPX x eogonek -10
+KPX y comma -37
+KPX y period -37
+KPX yacute comma -37
+KPX yacute period -37
+KPX ydieresis comma -37
+KPX ydieresis period -37
+EndKernPairs
+EndKernData
+EndFontMetrics
diff --git a/program/libraries/afm/Times-Italic.afm b/program/libraries/afm/Times-Italic.afm
--- /dev/null
@@ -0,0 +1,2667 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
+Comment Creation Date: Thu May 1 12:56:55 1997
+Comment UniqueID 43067
+Comment VMusage 47727 58752
+FontName Times-Italic
+FullName Times Italic
+FamilyName Times
+Weight Medium
+ItalicAngle -15.5
+IsFixedPitch false
+CharacterSet ExtendedRoman
+FontBBox -169 -217 1010 883
+UnderlinePosition -100
+UnderlineThickness 50
+Version 002.000
+Notice Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.Times is a trademark of Linotype-Hell AG and/or its subsidiaries.
+EncodingScheme AdobeStandardEncoding
+CapHeight 653
+XHeight 441
+Ascender 683
+Descender -217
+StdHW 32
+StdVW 76
+StartCharMetrics 315
+C 32 ; WX 250 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 333 ; N exclam ; B 39 -11 302 667 ;
+C 34 ; WX 420 ; N quotedbl ; B 144 421 432 666 ;
+C 35 ; WX 500 ; N numbersign ; B 2 0 540 676 ;
+C 36 ; WX 500 ; N dollar ; B 31 -89 497 731 ;
+C 37 ; WX 833 ; N percent ; B 79 -13 790 676 ;
+C 38 ; WX 778 ; N ampersand ; B 76 -18 723 666 ;
+C 39 ; WX 333 ; N quoteright ; B 151 436 290 666 ;
+C 40 ; WX 333 ; N parenleft ; B 42 -181 315 669 ;
+C 41 ; WX 333 ; N parenright ; B 16 -180 289 669 ;
+C 42 ; WX 500 ; N asterisk ; B 128 255 492 666 ;
+C 43 ; WX 675 ; N plus ; B 86 0 590 506 ;
+C 44 ; WX 250 ; N comma ; B -4 -129 135 101 ;
+C 45 ; WX 333 ; N hyphen ; B 49 192 282 255 ;
+C 46 ; WX 250 ; N period ; B 27 -11 138 100 ;
+C 47 ; WX 278 ; N slash ; B -65 -18 386 666 ;
+C 48 ; WX 500 ; N zero ; B 32 -7 497 676 ;
+C 49 ; WX 500 ; N one ; B 49 0 409 676 ;
+C 50 ; WX 500 ; N two ; B 12 0 452 676 ;
+C 51 ; WX 500 ; N three ; B 15 -7 465 676 ;
+C 52 ; WX 500 ; N four ; B 1 0 479 676 ;
+C 53 ; WX 500 ; N five ; B 15 -7 491 666 ;
+C 54 ; WX 500 ; N six ; B 30 -7 521 686 ;
+C 55 ; WX 500 ; N seven ; B 75 -8 537 666 ;
+C 56 ; WX 500 ; N eight ; B 30 -7 493 676 ;
+C 57 ; WX 500 ; N nine ; B 23 -17 492 676 ;
+C 58 ; WX 333 ; N colon ; B 50 -11 261 441 ;
+C 59 ; WX 333 ; N semicolon ; B 27 -129 261 441 ;
+C 60 ; WX 675 ; N less ; B 84 -8 592 514 ;
+C 61 ; WX 675 ; N equal ; B 86 120 590 386 ;
+C 62 ; WX 675 ; N greater ; B 84 -8 592 514 ;
+C 63 ; WX 500 ; N question ; B 132 -12 472 664 ;
+C 64 ; WX 920 ; N at ; B 118 -18 806 666 ;
+C 65 ; WX 611 ; N A ; B -51 0 564 668 ;
+C 66 ; WX 611 ; N B ; B -8 0 588 653 ;
+C 67 ; WX 667 ; N C ; B 66 -18 689 666 ;
+C 68 ; WX 722 ; N D ; B -8 0 700 653 ;
+C 69 ; WX 611 ; N E ; B -1 0 634 653 ;
+C 70 ; WX 611 ; N F ; B 8 0 645 653 ;
+C 71 ; WX 722 ; N G ; B 52 -18 722 666 ;
+C 72 ; WX 722 ; N H ; B -8 0 767 653 ;
+C 73 ; WX 333 ; N I ; B -8 0 384 653 ;
+C 74 ; WX 444 ; N J ; B -6 -18 491 653 ;
+C 75 ; WX 667 ; N K ; B 7 0 722 653 ;
+C 76 ; WX 556 ; N L ; B -8 0 559 653 ;
+C 77 ; WX 833 ; N M ; B -18 0 873 653 ;
+C 78 ; WX 667 ; N N ; B -20 -15 727 653 ;
+C 79 ; WX 722 ; N O ; B 60 -18 699 666 ;
+C 80 ; WX 611 ; N P ; B 0 0 605 653 ;
+C 81 ; WX 722 ; N Q ; B 59 -182 699 666 ;
+C 82 ; WX 611 ; N R ; B -13 0 588 653 ;
+C 83 ; WX 500 ; N S ; B 17 -18 508 667 ;
+C 84 ; WX 556 ; N T ; B 59 0 633 653 ;
+C 85 ; WX 722 ; N U ; B 102 -18 765 653 ;
+C 86 ; WX 611 ; N V ; B 76 -18 688 653 ;
+C 87 ; WX 833 ; N W ; B 71 -18 906 653 ;
+C 88 ; WX 611 ; N X ; B -29 0 655 653 ;
+C 89 ; WX 556 ; N Y ; B 78 0 633 653 ;
+C 90 ; WX 556 ; N Z ; B -6 0 606 653 ;
+C 91 ; WX 389 ; N bracketleft ; B 21 -153 391 663 ;
+C 92 ; WX 278 ; N backslash ; B -41 -18 319 666 ;
+C 93 ; WX 389 ; N bracketright ; B 12 -153 382 663 ;
+C 94 ; WX 422 ; N asciicircum ; B 0 301 422 666 ;
+C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ;
+C 96 ; WX 333 ; N quoteleft ; B 171 436 310 666 ;
+C 97 ; WX 500 ; N a ; B 17 -11 476 441 ;
+C 98 ; WX 500 ; N b ; B 23 -11 473 683 ;
+C 99 ; WX 444 ; N c ; B 30 -11 425 441 ;
+C 100 ; WX 500 ; N d ; B 15 -13 527 683 ;
+C 101 ; WX 444 ; N e ; B 31 -11 412 441 ;
+C 102 ; WX 278 ; N f ; B -147 -207 424 678 ; L i fi ; L l fl ;
+C 103 ; WX 500 ; N g ; B 8 -206 472 441 ;
+C 104 ; WX 500 ; N h ; B 19 -9 478 683 ;
+C 105 ; WX 278 ; N i ; B 49 -11 264 654 ;
+C 106 ; WX 278 ; N j ; B -124 -207 276 654 ;
+C 107 ; WX 444 ; N k ; B 14 -11 461 683 ;
+C 108 ; WX 278 ; N l ; B 41 -11 279 683 ;
+C 109 ; WX 722 ; N m ; B 12 -9 704 441 ;
+C 110 ; WX 500 ; N n ; B 14 -9 474 441 ;
+C 111 ; WX 500 ; N o ; B 27 -11 468 441 ;
+C 112 ; WX 500 ; N p ; B -75 -205 469 441 ;
+C 113 ; WX 500 ; N q ; B 25 -209 483 441 ;
+C 114 ; WX 389 ; N r ; B 45 0 412 441 ;
+C 115 ; WX 389 ; N s ; B 16 -13 366 442 ;
+C 116 ; WX 278 ; N t ; B 37 -11 296 546 ;
+C 117 ; WX 500 ; N u ; B 42 -11 475 441 ;
+C 118 ; WX 444 ; N v ; B 21 -18 426 441 ;
+C 119 ; WX 667 ; N w ; B 16 -18 648 441 ;
+C 120 ; WX 444 ; N x ; B -27 -11 447 441 ;
+C 121 ; WX 444 ; N y ; B -24 -206 426 441 ;
+C 122 ; WX 389 ; N z ; B -2 -81 380 428 ;
+C 123 ; WX 400 ; N braceleft ; B 51 -177 407 687 ;
+C 124 ; WX 275 ; N bar ; B 105 -217 171 783 ;
+C 125 ; WX 400 ; N braceright ; B -7 -177 349 687 ;
+C 126 ; WX 541 ; N asciitilde ; B 40 183 502 323 ;
+C 161 ; WX 389 ; N exclamdown ; B 59 -205 322 473 ;
+C 162 ; WX 500 ; N cent ; B 77 -143 472 560 ;
+C 163 ; WX 500 ; N sterling ; B 10 -6 517 670 ;
+C 164 ; WX 167 ; N fraction ; B -169 -10 337 676 ;
+C 165 ; WX 500 ; N yen ; B 27 0 603 653 ;
+C 166 ; WX 500 ; N florin ; B 25 -182 507 682 ;
+C 167 ; WX 500 ; N section ; B 53 -162 461 666 ;
+C 168 ; WX 500 ; N currency ; B -22 53 522 597 ;
+C 169 ; WX 214 ; N quotesingle ; B 132 421 241 666 ;
+C 170 ; WX 556 ; N quotedblleft ; B 166 436 514 666 ;
+C 171 ; WX 500 ; N guillemotleft ; B 53 37 445 403 ;
+C 172 ; WX 333 ; N guilsinglleft ; B 51 37 281 403 ;
+C 173 ; WX 333 ; N guilsinglright ; B 52 37 282 403 ;
+C 174 ; WX 500 ; N fi ; B -141 -207 481 681 ;
+C 175 ; WX 500 ; N fl ; B -141 -204 518 682 ;
+C 177 ; WX 500 ; N endash ; B -6 197 505 243 ;
+C 178 ; WX 500 ; N dagger ; B 101 -159 488 666 ;
+C 179 ; WX 500 ; N daggerdbl ; B 22 -143 491 666 ;
+C 180 ; WX 250 ; N periodcentered ; B 70 199 181 310 ;
+C 182 ; WX 523 ; N paragraph ; B 55 -123 616 653 ;
+C 183 ; WX 350 ; N bullet ; B 40 191 310 461 ;
+C 184 ; WX 333 ; N quotesinglbase ; B 44 -129 183 101 ;
+C 185 ; WX 556 ; N quotedblbase ; B 57 -129 405 101 ;
+C 186 ; WX 556 ; N quotedblright ; B 151 436 499 666 ;
+C 187 ; WX 500 ; N guillemotright ; B 55 37 447 403 ;
+C 188 ; WX 889 ; N ellipsis ; B 57 -11 762 100 ;
+C 189 ; WX 1000 ; N perthousand ; B 25 -19 1010 706 ;
+C 191 ; WX 500 ; N questiondown ; B 28 -205 368 471 ;
+C 193 ; WX 333 ; N grave ; B 121 492 311 664 ;
+C 194 ; WX 333 ; N acute ; B 180 494 403 664 ;
+C 195 ; WX 333 ; N circumflex ; B 91 492 385 661 ;
+C 196 ; WX 333 ; N tilde ; B 100 517 427 624 ;
+C 197 ; WX 333 ; N macron ; B 99 532 411 583 ;
+C 198 ; WX 333 ; N breve ; B 117 492 418 650 ;
+C 199 ; WX 333 ; N dotaccent ; B 207 548 305 646 ;
+C 200 ; WX 333 ; N dieresis ; B 107 548 405 646 ;
+C 202 ; WX 333 ; N ring ; B 155 492 355 691 ;
+C 203 ; WX 333 ; N cedilla ; B -30 -217 182 0 ;
+C 205 ; WX 333 ; N hungarumlaut ; B 93 494 486 664 ;
+C 206 ; WX 333 ; N ogonek ; B 20 -169 203 40 ;
+C 207 ; WX 333 ; N caron ; B 121 492 426 661 ;
+C 208 ; WX 889 ; N emdash ; B -6 197 894 243 ;
+C 225 ; WX 889 ; N AE ; B -27 0 911 653 ;
+C 227 ; WX 276 ; N ordfeminine ; B 42 406 352 676 ;
+C 232 ; WX 556 ; N Lslash ; B -8 0 559 653 ;
+C 233 ; WX 722 ; N Oslash ; B 60 -105 699 722 ;
+C 234 ; WX 944 ; N OE ; B 49 -8 964 666 ;
+C 235 ; WX 310 ; N ordmasculine ; B 67 406 362 676 ;
+C 241 ; WX 667 ; N ae ; B 23 -11 640 441 ;
+C 245 ; WX 278 ; N dotlessi ; B 49 -11 235 441 ;
+C 248 ; WX 278 ; N lslash ; B 41 -11 312 683 ;
+C 249 ; WX 500 ; N oslash ; B 28 -135 469 554 ;
+C 250 ; WX 667 ; N oe ; B 20 -12 646 441 ;
+C 251 ; WX 500 ; N germandbls ; B -168 -207 493 679 ;
+C -1 ; WX 333 ; N Idieresis ; B -8 0 435 818 ;
+C -1 ; WX 444 ; N eacute ; B 31 -11 459 664 ;
+C -1 ; WX 500 ; N abreve ; B 17 -11 502 650 ;
+C -1 ; WX 500 ; N uhungarumlaut ; B 42 -11 580 664 ;
+C -1 ; WX 444 ; N ecaron ; B 31 -11 482 661 ;
+C -1 ; WX 556 ; N Ydieresis ; B 78 0 633 818 ;
+C -1 ; WX 675 ; N divide ; B 86 -11 590 517 ;
+C -1 ; WX 556 ; N Yacute ; B 78 0 633 876 ;
+C -1 ; WX 611 ; N Acircumflex ; B -51 0 564 873 ;
+C -1 ; WX 500 ; N aacute ; B 17 -11 487 664 ;
+C -1 ; WX 722 ; N Ucircumflex ; B 102 -18 765 873 ;
+C -1 ; WX 444 ; N yacute ; B -24 -206 459 664 ;
+C -1 ; WX 389 ; N scommaaccent ; B 16 -217 366 442 ;
+C -1 ; WX 444 ; N ecircumflex ; B 31 -11 441 661 ;
+C -1 ; WX 722 ; N Uring ; B 102 -18 765 883 ;
+C -1 ; WX 722 ; N Udieresis ; B 102 -18 765 818 ;
+C -1 ; WX 500 ; N aogonek ; B 17 -169 476 441 ;
+C -1 ; WX 722 ; N Uacute ; B 102 -18 765 876 ;
+C -1 ; WX 500 ; N uogonek ; B 42 -169 477 441 ;
+C -1 ; WX 611 ; N Edieresis ; B -1 0 634 818 ;
+C -1 ; WX 722 ; N Dcroat ; B -8 0 700 653 ;
+C -1 ; WX 250 ; N commaaccent ; B 8 -217 133 -50 ;
+C -1 ; WX 760 ; N copyright ; B 41 -18 719 666 ;
+C -1 ; WX 611 ; N Emacron ; B -1 0 634 795 ;
+C -1 ; WX 444 ; N ccaron ; B 30 -11 482 661 ;
+C -1 ; WX 500 ; N aring ; B 17 -11 476 691 ;
+C -1 ; WX 667 ; N Ncommaaccent ; B -20 -187 727 653 ;
+C -1 ; WX 278 ; N lacute ; B 41 -11 395 876 ;
+C -1 ; WX 500 ; N agrave ; B 17 -11 476 664 ;
+C -1 ; WX 556 ; N Tcommaaccent ; B 59 -217 633 653 ;
+C -1 ; WX 667 ; N Cacute ; B 66 -18 690 876 ;
+C -1 ; WX 500 ; N atilde ; B 17 -11 511 624 ;
+C -1 ; WX 611 ; N Edotaccent ; B -1 0 634 818 ;
+C -1 ; WX 389 ; N scaron ; B 16 -13 454 661 ;
+C -1 ; WX 389 ; N scedilla ; B 16 -217 366 442 ;
+C -1 ; WX 278 ; N iacute ; B 49 -11 355 664 ;
+C -1 ; WX 471 ; N lozenge ; B 13 0 459 724 ;
+C -1 ; WX 611 ; N Rcaron ; B -13 0 588 873 ;
+C -1 ; WX 722 ; N Gcommaaccent ; B 52 -217 722 666 ;
+C -1 ; WX 500 ; N ucircumflex ; B 42 -11 475 661 ;
+C -1 ; WX 500 ; N acircumflex ; B 17 -11 476 661 ;
+C -1 ; WX 611 ; N Amacron ; B -51 0 564 795 ;
+C -1 ; WX 389 ; N rcaron ; B 45 0 434 661 ;
+C -1 ; WX 444 ; N ccedilla ; B 30 -217 425 441 ;
+C -1 ; WX 556 ; N Zdotaccent ; B -6 0 606 818 ;
+C -1 ; WX 611 ; N Thorn ; B 0 0 569 653 ;
+C -1 ; WX 722 ; N Omacron ; B 60 -18 699 795 ;
+C -1 ; WX 611 ; N Racute ; B -13 0 588 876 ;
+C -1 ; WX 500 ; N Sacute ; B 17 -18 508 876 ;
+C -1 ; WX 544 ; N dcaron ; B 15 -13 658 683 ;
+C -1 ; WX 722 ; N Umacron ; B 102 -18 765 795 ;
+C -1 ; WX 500 ; N uring ; B 42 -11 475 691 ;
+C -1 ; WX 300 ; N threesuperior ; B 43 268 339 676 ;
+C -1 ; WX 722 ; N Ograve ; B 60 -18 699 876 ;
+C -1 ; WX 611 ; N Agrave ; B -51 0 564 876 ;
+C -1 ; WX 611 ; N Abreve ; B -51 0 564 862 ;
+C -1 ; WX 675 ; N multiply ; B 93 8 582 497 ;
+C -1 ; WX 500 ; N uacute ; B 42 -11 477 664 ;
+C -1 ; WX 556 ; N Tcaron ; B 59 0 633 873 ;
+C -1 ; WX 476 ; N partialdiff ; B 17 -38 459 710 ;
+C -1 ; WX 444 ; N ydieresis ; B -24 -206 441 606 ;
+C -1 ; WX 667 ; N Nacute ; B -20 -15 727 876 ;
+C -1 ; WX 278 ; N icircumflex ; B 33 -11 327 661 ;
+C -1 ; WX 611 ; N Ecircumflex ; B -1 0 634 873 ;
+C -1 ; WX 500 ; N adieresis ; B 17 -11 489 606 ;
+C -1 ; WX 444 ; N edieresis ; B 31 -11 451 606 ;
+C -1 ; WX 444 ; N cacute ; B 30 -11 459 664 ;
+C -1 ; WX 500 ; N nacute ; B 14 -9 477 664 ;
+C -1 ; WX 500 ; N umacron ; B 42 -11 485 583 ;
+C -1 ; WX 667 ; N Ncaron ; B -20 -15 727 873 ;
+C -1 ; WX 333 ; N Iacute ; B -8 0 433 876 ;
+C -1 ; WX 675 ; N plusminus ; B 86 0 590 506 ;
+C -1 ; WX 275 ; N brokenbar ; B 105 -142 171 708 ;
+C -1 ; WX 760 ; N registered ; B 41 -18 719 666 ;
+C -1 ; WX 722 ; N Gbreve ; B 52 -18 722 862 ;
+C -1 ; WX 333 ; N Idotaccent ; B -8 0 384 818 ;
+C -1 ; WX 600 ; N summation ; B 15 -10 585 706 ;
+C -1 ; WX 611 ; N Egrave ; B -1 0 634 876 ;
+C -1 ; WX 389 ; N racute ; B 45 0 431 664 ;
+C -1 ; WX 500 ; N omacron ; B 27 -11 495 583 ;
+C -1 ; WX 556 ; N Zacute ; B -6 0 606 876 ;
+C -1 ; WX 556 ; N Zcaron ; B -6 0 606 873 ;
+C -1 ; WX 549 ; N greaterequal ; B 26 0 523 658 ;
+C -1 ; WX 722 ; N Eth ; B -8 0 700 653 ;
+C -1 ; WX 667 ; N Ccedilla ; B 66 -217 689 666 ;
+C -1 ; WX 278 ; N lcommaaccent ; B 22 -217 279 683 ;
+C -1 ; WX 300 ; N tcaron ; B 37 -11 407 681 ;
+C -1 ; WX 444 ; N eogonek ; B 31 -169 412 441 ;
+C -1 ; WX 722 ; N Uogonek ; B 102 -184 765 653 ;
+C -1 ; WX 611 ; N Aacute ; B -51 0 564 876 ;
+C -1 ; WX 611 ; N Adieresis ; B -51 0 564 818 ;
+C -1 ; WX 444 ; N egrave ; B 31 -11 412 664 ;
+C -1 ; WX 389 ; N zacute ; B -2 -81 431 664 ;
+C -1 ; WX 278 ; N iogonek ; B 49 -169 264 654 ;
+C -1 ; WX 722 ; N Oacute ; B 60 -18 699 876 ;
+C -1 ; WX 500 ; N oacute ; B 27 -11 487 664 ;
+C -1 ; WX 500 ; N amacron ; B 17 -11 495 583 ;
+C -1 ; WX 389 ; N sacute ; B 16 -13 431 664 ;
+C -1 ; WX 278 ; N idieresis ; B 49 -11 352 606 ;
+C -1 ; WX 722 ; N Ocircumflex ; B 60 -18 699 873 ;
+C -1 ; WX 722 ; N Ugrave ; B 102 -18 765 876 ;
+C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ;
+C -1 ; WX 500 ; N thorn ; B -75 -205 469 683 ;
+C -1 ; WX 300 ; N twosuperior ; B 33 271 324 676 ;
+C -1 ; WX 722 ; N Odieresis ; B 60 -18 699 818 ;
+C -1 ; WX 500 ; N mu ; B -30 -209 497 428 ;
+C -1 ; WX 278 ; N igrave ; B 49 -11 284 664 ;
+C -1 ; WX 500 ; N ohungarumlaut ; B 27 -11 590 664 ;
+C -1 ; WX 611 ; N Eogonek ; B -1 -169 634 653 ;
+C -1 ; WX 500 ; N dcroat ; B 15 -13 572 683 ;
+C -1 ; WX 750 ; N threequarters ; B 23 -10 736 676 ;
+C -1 ; WX 500 ; N Scedilla ; B 17 -217 508 667 ;
+C -1 ; WX 300 ; N lcaron ; B 41 -11 407 683 ;
+C -1 ; WX 667 ; N Kcommaaccent ; B 7 -217 722 653 ;
+C -1 ; WX 556 ; N Lacute ; B -8 0 559 876 ;
+C -1 ; WX 980 ; N trademark ; B 30 247 957 653 ;
+C -1 ; WX 444 ; N edotaccent ; B 31 -11 412 606 ;
+C -1 ; WX 333 ; N Igrave ; B -8 0 384 876 ;
+C -1 ; WX 333 ; N Imacron ; B -8 0 441 795 ;
+C -1 ; WX 611 ; N Lcaron ; B -8 0 586 653 ;
+C -1 ; WX 750 ; N onehalf ; B 34 -10 749 676 ;
+C -1 ; WX 549 ; N lessequal ; B 26 0 523 658 ;
+C -1 ; WX 500 ; N ocircumflex ; B 27 -11 468 661 ;
+C -1 ; WX 500 ; N ntilde ; B 14 -9 476 624 ;
+C -1 ; WX 722 ; N Uhungarumlaut ; B 102 -18 765 876 ;
+C -1 ; WX 611 ; N Eacute ; B -1 0 634 876 ;
+C -1 ; WX 444 ; N emacron ; B 31 -11 457 583 ;
+C -1 ; WX 500 ; N gbreve ; B 8 -206 487 650 ;
+C -1 ; WX 750 ; N onequarter ; B 33 -10 736 676 ;
+C -1 ; WX 500 ; N Scaron ; B 17 -18 520 873 ;
+C -1 ; WX 500 ; N Scommaaccent ; B 17 -217 508 667 ;
+C -1 ; WX 722 ; N Ohungarumlaut ; B 60 -18 699 876 ;
+C -1 ; WX 400 ; N degree ; B 101 390 387 676 ;
+C -1 ; WX 500 ; N ograve ; B 27 -11 468 664 ;
+C -1 ; WX 667 ; N Ccaron ; B 66 -18 689 873 ;
+C -1 ; WX 500 ; N ugrave ; B 42 -11 475 664 ;
+C -1 ; WX 453 ; N radical ; B 2 -60 452 768 ;
+C -1 ; WX 722 ; N Dcaron ; B -8 0 700 873 ;
+C -1 ; WX 389 ; N rcommaaccent ; B -3 -217 412 441 ;
+C -1 ; WX 667 ; N Ntilde ; B -20 -15 727 836 ;
+C -1 ; WX 500 ; N otilde ; B 27 -11 496 624 ;
+C -1 ; WX 611 ; N Rcommaaccent ; B -13 -187 588 653 ;
+C -1 ; WX 556 ; N Lcommaaccent ; B -8 -217 559 653 ;
+C -1 ; WX 611 ; N Atilde ; B -51 0 566 836 ;
+C -1 ; WX 611 ; N Aogonek ; B -51 -169 566 668 ;
+C -1 ; WX 611 ; N Aring ; B -51 0 564 883 ;
+C -1 ; WX 722 ; N Otilde ; B 60 -18 699 836 ;
+C -1 ; WX 389 ; N zdotaccent ; B -2 -81 380 606 ;
+C -1 ; WX 611 ; N Ecaron ; B -1 0 634 873 ;
+C -1 ; WX 333 ; N Iogonek ; B -8 -169 384 653 ;
+C -1 ; WX 444 ; N kcommaaccent ; B 14 -187 461 683 ;
+C -1 ; WX 675 ; N minus ; B 86 220 590 286 ;
+C -1 ; WX 333 ; N Icircumflex ; B -8 0 425 873 ;
+C -1 ; WX 500 ; N ncaron ; B 14 -9 510 661 ;
+C -1 ; WX 278 ; N tcommaaccent ; B 2 -217 296 546 ;
+C -1 ; WX 675 ; N logicalnot ; B 86 108 590 386 ;
+C -1 ; WX 500 ; N odieresis ; B 27 -11 489 606 ;
+C -1 ; WX 500 ; N udieresis ; B 42 -11 479 606 ;
+C -1 ; WX 549 ; N notequal ; B 12 -29 537 541 ;
+C -1 ; WX 500 ; N gcommaaccent ; B 8 -206 472 706 ;
+C -1 ; WX 500 ; N eth ; B 27 -11 482 683 ;
+C -1 ; WX 389 ; N zcaron ; B -2 -81 434 661 ;
+C -1 ; WX 500 ; N ncommaaccent ; B 14 -187 474 441 ;
+C -1 ; WX 300 ; N onesuperior ; B 43 271 284 676 ;
+C -1 ; WX 278 ; N imacron ; B 46 -11 311 583 ;
+C -1 ; WX 500 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+StartKernData
+StartKernPairs 2321
+KPX A C -30
+KPX A Cacute -30
+KPX A Ccaron -30
+KPX A Ccedilla -30
+KPX A G -35
+KPX A Gbreve -35
+KPX A Gcommaaccent -35
+KPX A O -40
+KPX A Oacute -40
+KPX A Ocircumflex -40
+KPX A Odieresis -40
+KPX A Ograve -40
+KPX A Ohungarumlaut -40
+KPX A Omacron -40
+KPX A Oslash -40
+KPX A Otilde -40
+KPX A Q -40
+KPX A T -37
+KPX A Tcaron -37
+KPX A Tcommaaccent -37
+KPX A U -50
+KPX A Uacute -50
+KPX A Ucircumflex -50
+KPX A Udieresis -50
+KPX A Ugrave -50
+KPX A Uhungarumlaut -50
+KPX A Umacron -50
+KPX A Uogonek -50
+KPX A Uring -50
+KPX A V -105
+KPX A W -95
+KPX A Y -55
+KPX A Yacute -55
+KPX A Ydieresis -55
+KPX A quoteright -37
+KPX A u -20
+KPX A uacute -20
+KPX A ucircumflex -20
+KPX A udieresis -20
+KPX A ugrave -20
+KPX A uhungarumlaut -20
+KPX A umacron -20
+KPX A uogonek -20
+KPX A uring -20
+KPX A v -55
+KPX A w -55
+KPX A y -55
+KPX A yacute -55
+KPX A ydieresis -55
+KPX Aacute C -30
+KPX Aacute Cacute -30
+KPX Aacute Ccaron -30
+KPX Aacute Ccedilla -30
+KPX Aacute G -35
+KPX Aacute Gbreve -35
+KPX Aacute Gcommaaccent -35
+KPX Aacute O -40
+KPX Aacute Oacute -40
+KPX Aacute Ocircumflex -40
+KPX Aacute Odieresis -40
+KPX Aacute Ograve -40
+KPX Aacute Ohungarumlaut -40
+KPX Aacute Omacron -40
+KPX Aacute Oslash -40
+KPX Aacute Otilde -40
+KPX Aacute Q -40
+KPX Aacute T -37
+KPX Aacute Tcaron -37
+KPX Aacute Tcommaaccent -37
+KPX Aacute U -50
+KPX Aacute Uacute -50
+KPX Aacute Ucircumflex -50
+KPX Aacute Udieresis -50
+KPX Aacute Ugrave -50
+KPX Aacute Uhungarumlaut -50
+KPX Aacute Umacron -50
+KPX Aacute Uogonek -50
+KPX Aacute Uring -50
+KPX Aacute V -105
+KPX Aacute W -95
+KPX Aacute Y -55
+KPX Aacute Yacute -55
+KPX Aacute Ydieresis -55
+KPX Aacute quoteright -37
+KPX Aacute u -20
+KPX Aacute uacute -20
+KPX Aacute ucircumflex -20
+KPX Aacute udieresis -20
+KPX Aacute ugrave -20
+KPX Aacute uhungarumlaut -20
+KPX Aacute umacron -20
+KPX Aacute uogonek -20
+KPX Aacute uring -20
+KPX Aacute v -55
+KPX Aacute w -55
+KPX Aacute y -55
+KPX Aacute yacute -55
+KPX Aacute ydieresis -55
+KPX Abreve C -30
+KPX Abreve Cacute -30
+KPX Abreve Ccaron -30
+KPX Abreve Ccedilla -30
+KPX Abreve G -35
+KPX Abreve Gbreve -35
+KPX Abreve Gcommaaccent -35
+KPX Abreve O -40
+KPX Abreve Oacute -40
+KPX Abreve Ocircumflex -40
+KPX Abreve Odieresis -40
+KPX Abreve Ograve -40
+KPX Abreve Ohungarumlaut -40
+KPX Abreve Omacron -40
+KPX Abreve Oslash -40
+KPX Abreve Otilde -40
+KPX Abreve Q -40
+KPX Abreve T -37
+KPX Abreve Tcaron -37
+KPX Abreve Tcommaaccent -37
+KPX Abreve U -50
+KPX Abreve Uacute -50
+KPX Abreve Ucircumflex -50
+KPX Abreve Udieresis -50
+KPX Abreve Ugrave -50
+KPX Abreve Uhungarumlaut -50
+KPX Abreve Umacron -50
+KPX Abreve Uogonek -50
+KPX Abreve Uring -50
+KPX Abreve V -105
+KPX Abreve W -95
+KPX Abreve Y -55
+KPX Abreve Yacute -55
+KPX Abreve Ydieresis -55
+KPX Abreve quoteright -37
+KPX Abreve u -20
+KPX Abreve uacute -20
+KPX Abreve ucircumflex -20
+KPX Abreve udieresis -20
+KPX Abreve ugrave -20
+KPX Abreve uhungarumlaut -20
+KPX Abreve umacron -20
+KPX Abreve uogonek -20
+KPX Abreve uring -20
+KPX Abreve v -55
+KPX Abreve w -55
+KPX Abreve y -55
+KPX Abreve yacute -55
+KPX Abreve ydieresis -55
+KPX Acircumflex C -30
+KPX Acircumflex Cacute -30
+KPX Acircumflex Ccaron -30
+KPX Acircumflex Ccedilla -30
+KPX Acircumflex G -35
+KPX Acircumflex Gbreve -35
+KPX Acircumflex Gcommaaccent -35
+KPX Acircumflex O -40
+KPX Acircumflex Oacute -40
+KPX Acircumflex Ocircumflex -40
+KPX Acircumflex Odieresis -40
+KPX Acircumflex Ograve -40
+KPX Acircumflex Ohungarumlaut -40
+KPX Acircumflex Omacron -40
+KPX Acircumflex Oslash -40
+KPX Acircumflex Otilde -40
+KPX Acircumflex Q -40
+KPX Acircumflex T -37
+KPX Acircumflex Tcaron -37
+KPX Acircumflex Tcommaaccent -37
+KPX Acircumflex U -50
+KPX Acircumflex Uacute -50
+KPX Acircumflex Ucircumflex -50
+KPX Acircumflex Udieresis -50
+KPX Acircumflex Ugrave -50
+KPX Acircumflex Uhungarumlaut -50
+KPX Acircumflex Umacron -50
+KPX Acircumflex Uogonek -50
+KPX Acircumflex Uring -50
+KPX Acircumflex V -105
+KPX Acircumflex W -95
+KPX Acircumflex Y -55
+KPX Acircumflex Yacute -55
+KPX Acircumflex Ydieresis -55
+KPX Acircumflex quoteright -37
+KPX Acircumflex u -20
+KPX Acircumflex uacute -20
+KPX Acircumflex ucircumflex -20
+KPX Acircumflex udieresis -20
+KPX Acircumflex ugrave -20
+KPX Acircumflex uhungarumlaut -20
+KPX Acircumflex umacron -20
+KPX Acircumflex uogonek -20
+KPX Acircumflex uring -20
+KPX Acircumflex v -55
+KPX Acircumflex w -55
+KPX Acircumflex y -55
+KPX Acircumflex yacute -55
+KPX Acircumflex ydieresis -55
+KPX Adieresis C -30
+KPX Adieresis Cacute -30
+KPX Adieresis Ccaron -30
+KPX Adieresis Ccedilla -30
+KPX Adieresis G -35
+KPX Adieresis Gbreve -35
+KPX Adieresis Gcommaaccent -35
+KPX Adieresis O -40
+KPX Adieresis Oacute -40
+KPX Adieresis Ocircumflex -40
+KPX Adieresis Odieresis -40
+KPX Adieresis Ograve -40
+KPX Adieresis Ohungarumlaut -40
+KPX Adieresis Omacron -40
+KPX Adieresis Oslash -40
+KPX Adieresis Otilde -40
+KPX Adieresis Q -40
+KPX Adieresis T -37
+KPX Adieresis Tcaron -37
+KPX Adieresis Tcommaaccent -37
+KPX Adieresis U -50
+KPX Adieresis Uacute -50
+KPX Adieresis Ucircumflex -50
+KPX Adieresis Udieresis -50
+KPX Adieresis Ugrave -50
+KPX Adieresis Uhungarumlaut -50
+KPX Adieresis Umacron -50
+KPX Adieresis Uogonek -50
+KPX Adieresis Uring -50
+KPX Adieresis V -105
+KPX Adieresis W -95
+KPX Adieresis Y -55
+KPX Adieresis Yacute -55
+KPX Adieresis Ydieresis -55
+KPX Adieresis quoteright -37
+KPX Adieresis u -20
+KPX Adieresis uacute -20
+KPX Adieresis ucircumflex -20
+KPX Adieresis udieresis -20
+KPX Adieresis ugrave -20
+KPX Adieresis uhungarumlaut -20
+KPX Adieresis umacron -20
+KPX Adieresis uogonek -20
+KPX Adieresis uring -20
+KPX Adieresis v -55
+KPX Adieresis w -55
+KPX Adieresis y -55
+KPX Adieresis yacute -55
+KPX Adieresis ydieresis -55
+KPX Agrave C -30
+KPX Agrave Cacute -30
+KPX Agrave Ccaron -30
+KPX Agrave Ccedilla -30
+KPX Agrave G -35
+KPX Agrave Gbreve -35
+KPX Agrave Gcommaaccent -35
+KPX Agrave O -40
+KPX Agrave Oacute -40
+KPX Agrave Ocircumflex -40
+KPX Agrave Odieresis -40
+KPX Agrave Ograve -40
+KPX Agrave Ohungarumlaut -40
+KPX Agrave Omacron -40
+KPX Agrave Oslash -40
+KPX Agrave Otilde -40
+KPX Agrave Q -40
+KPX Agrave T -37
+KPX Agrave Tcaron -37
+KPX Agrave Tcommaaccent -37
+KPX Agrave U -50
+KPX Agrave Uacute -50
+KPX Agrave Ucircumflex -50
+KPX Agrave Udieresis -50
+KPX Agrave Ugrave -50
+KPX Agrave Uhungarumlaut -50
+KPX Agrave Umacron -50
+KPX Agrave Uogonek -50
+KPX Agrave Uring -50
+KPX Agrave V -105
+KPX Agrave W -95
+KPX Agrave Y -55
+KPX Agrave Yacute -55
+KPX Agrave Ydieresis -55
+KPX Agrave quoteright -37
+KPX Agrave u -20
+KPX Agrave uacute -20
+KPX Agrave ucircumflex -20
+KPX Agrave udieresis -20
+KPX Agrave ugrave -20
+KPX Agrave uhungarumlaut -20
+KPX Agrave umacron -20
+KPX Agrave uogonek -20
+KPX Agrave uring -20
+KPX Agrave v -55
+KPX Agrave w -55
+KPX Agrave y -55
+KPX Agrave yacute -55
+KPX Agrave ydieresis -55
+KPX Amacron C -30
+KPX Amacron Cacute -30
+KPX Amacron Ccaron -30
+KPX Amacron Ccedilla -30
+KPX Amacron G -35
+KPX Amacron Gbreve -35
+KPX Amacron Gcommaaccent -35
+KPX Amacron O -40
+KPX Amacron Oacute -40
+KPX Amacron Ocircumflex -40
+KPX Amacron Odieresis -40
+KPX Amacron Ograve -40
+KPX Amacron Ohungarumlaut -40
+KPX Amacron Omacron -40
+KPX Amacron Oslash -40
+KPX Amacron Otilde -40
+KPX Amacron Q -40
+KPX Amacron T -37
+KPX Amacron Tcaron -37
+KPX Amacron Tcommaaccent -37
+KPX Amacron U -50
+KPX Amacron Uacute -50
+KPX Amacron Ucircumflex -50
+KPX Amacron Udieresis -50
+KPX Amacron Ugrave -50
+KPX Amacron Uhungarumlaut -50
+KPX Amacron Umacron -50
+KPX Amacron Uogonek -50
+KPX Amacron Uring -50
+KPX Amacron V -105
+KPX Amacron W -95
+KPX Amacron Y -55
+KPX Amacron Yacute -55
+KPX Amacron Ydieresis -55
+KPX Amacron quoteright -37
+KPX Amacron u -20
+KPX Amacron uacute -20
+KPX Amacron ucircumflex -20
+KPX Amacron udieresis -20
+KPX Amacron ugrave -20
+KPX Amacron uhungarumlaut -20
+KPX Amacron umacron -20
+KPX Amacron uogonek -20
+KPX Amacron uring -20
+KPX Amacron v -55
+KPX Amacron w -55
+KPX Amacron y -55
+KPX Amacron yacute -55
+KPX Amacron ydieresis -55
+KPX Aogonek C -30
+KPX Aogonek Cacute -30
+KPX Aogonek Ccaron -30
+KPX Aogonek Ccedilla -30
+KPX Aogonek G -35
+KPX Aogonek Gbreve -35
+KPX Aogonek Gcommaaccent -35
+KPX Aogonek O -40
+KPX Aogonek Oacute -40
+KPX Aogonek Ocircumflex -40
+KPX Aogonek Odieresis -40
+KPX Aogonek Ograve -40
+KPX Aogonek Ohungarumlaut -40
+KPX Aogonek Omacron -40
+KPX Aogonek Oslash -40
+KPX Aogonek Otilde -40
+KPX Aogonek Q -40
+KPX Aogonek T -37
+KPX Aogonek Tcaron -37
+KPX Aogonek Tcommaaccent -37
+KPX Aogonek U -50
+KPX Aogonek Uacute -50
+KPX Aogonek Ucircumflex -50
+KPX Aogonek Udieresis -50
+KPX Aogonek Ugrave -50
+KPX Aogonek Uhungarumlaut -50
+KPX Aogonek Umacron -50
+KPX Aogonek Uogonek -50
+KPX Aogonek Uring -50
+KPX Aogonek V -105
+KPX Aogonek W -95
+KPX Aogonek Y -55
+KPX Aogonek Yacute -55
+KPX Aogonek Ydieresis -55
+KPX Aogonek quoteright -37
+KPX Aogonek u -20
+KPX Aogonek uacute -20
+KPX Aogonek ucircumflex -20
+KPX Aogonek udieresis -20
+KPX Aogonek ugrave -20
+KPX Aogonek uhungarumlaut -20
+KPX Aogonek umacron -20
+KPX Aogonek uogonek -20
+KPX Aogonek uring -20
+KPX Aogonek v -55
+KPX Aogonek w -55
+KPX Aogonek y -55
+KPX Aogonek yacute -55
+KPX Aogonek ydieresis -55
+KPX Aring C -30
+KPX Aring Cacute -30
+KPX Aring Ccaron -30
+KPX Aring Ccedilla -30
+KPX Aring G -35
+KPX Aring Gbreve -35
+KPX Aring Gcommaaccent -35
+KPX Aring O -40
+KPX Aring Oacute -40
+KPX Aring Ocircumflex -40
+KPX Aring Odieresis -40
+KPX Aring Ograve -40
+KPX Aring Ohungarumlaut -40
+KPX Aring Omacron -40
+KPX Aring Oslash -40
+KPX Aring Otilde -40
+KPX Aring Q -40
+KPX Aring T -37
+KPX Aring Tcaron -37
+KPX Aring Tcommaaccent -37
+KPX Aring U -50
+KPX Aring Uacute -50
+KPX Aring Ucircumflex -50
+KPX Aring Udieresis -50
+KPX Aring Ugrave -50
+KPX Aring Uhungarumlaut -50
+KPX Aring Umacron -50
+KPX Aring Uogonek -50
+KPX Aring Uring -50
+KPX Aring V -105
+KPX Aring W -95
+KPX Aring Y -55
+KPX Aring Yacute -55
+KPX Aring Ydieresis -55
+KPX Aring quoteright -37
+KPX Aring u -20
+KPX Aring uacute -20
+KPX Aring ucircumflex -20
+KPX Aring udieresis -20
+KPX Aring ugrave -20
+KPX Aring uhungarumlaut -20
+KPX Aring umacron -20
+KPX Aring uogonek -20
+KPX Aring uring -20
+KPX Aring v -55
+KPX Aring w -55
+KPX Aring y -55
+KPX Aring yacute -55
+KPX Aring ydieresis -55
+KPX Atilde C -30
+KPX Atilde Cacute -30
+KPX Atilde Ccaron -30
+KPX Atilde Ccedilla -30
+KPX Atilde G -35
+KPX Atilde Gbreve -35
+KPX Atilde Gcommaaccent -35
+KPX Atilde O -40
+KPX Atilde Oacute -40
+KPX Atilde Ocircumflex -40
+KPX Atilde Odieresis -40
+KPX Atilde Ograve -40
+KPX Atilde Ohungarumlaut -40
+KPX Atilde Omacron -40
+KPX Atilde Oslash -40
+KPX Atilde Otilde -40
+KPX Atilde Q -40
+KPX Atilde T -37
+KPX Atilde Tcaron -37
+KPX Atilde Tcommaaccent -37
+KPX Atilde U -50
+KPX Atilde Uacute -50
+KPX Atilde Ucircumflex -50
+KPX Atilde Udieresis -50
+KPX Atilde Ugrave -50
+KPX Atilde Uhungarumlaut -50
+KPX Atilde Umacron -50
+KPX Atilde Uogonek -50
+KPX Atilde Uring -50
+KPX Atilde V -105
+KPX Atilde W -95
+KPX Atilde Y -55
+KPX Atilde Yacute -55
+KPX Atilde Ydieresis -55
+KPX Atilde quoteright -37
+KPX Atilde u -20
+KPX Atilde uacute -20
+KPX Atilde ucircumflex -20
+KPX Atilde udieresis -20
+KPX Atilde ugrave -20
+KPX Atilde uhungarumlaut -20
+KPX Atilde umacron -20
+KPX Atilde uogonek -20
+KPX Atilde uring -20
+KPX Atilde v -55
+KPX Atilde w -55
+KPX Atilde y -55
+KPX Atilde yacute -55
+KPX Atilde ydieresis -55
+KPX B A -25
+KPX B Aacute -25
+KPX B Abreve -25
+KPX B Acircumflex -25
+KPX B Adieresis -25
+KPX B Agrave -25
+KPX B Amacron -25
+KPX B Aogonek -25
+KPX B Aring -25
+KPX B Atilde -25
+KPX B U -10
+KPX B Uacute -10
+KPX B Ucircumflex -10
+KPX B Udieresis -10
+KPX B Ugrave -10
+KPX B Uhungarumlaut -10
+KPX B Umacron -10
+KPX B Uogonek -10
+KPX B Uring -10
+KPX D A -35
+KPX D Aacute -35
+KPX D Abreve -35
+KPX D Acircumflex -35
+KPX D Adieresis -35
+KPX D Agrave -35
+KPX D Amacron -35
+KPX D Aogonek -35
+KPX D Aring -35
+KPX D Atilde -35
+KPX D V -40
+KPX D W -40
+KPX D Y -40
+KPX D Yacute -40
+KPX D Ydieresis -40
+KPX Dcaron A -35
+KPX Dcaron Aacute -35
+KPX Dcaron Abreve -35
+KPX Dcaron Acircumflex -35
+KPX Dcaron Adieresis -35
+KPX Dcaron Agrave -35
+KPX Dcaron Amacron -35
+KPX Dcaron Aogonek -35
+KPX Dcaron Aring -35
+KPX Dcaron Atilde -35
+KPX Dcaron V -40
+KPX Dcaron W -40
+KPX Dcaron Y -40
+KPX Dcaron Yacute -40
+KPX Dcaron Ydieresis -40
+KPX Dcroat A -35
+KPX Dcroat Aacute -35
+KPX Dcroat Abreve -35
+KPX Dcroat Acircumflex -35
+KPX Dcroat Adieresis -35
+KPX Dcroat Agrave -35
+KPX Dcroat Amacron -35
+KPX Dcroat Aogonek -35
+KPX Dcroat Aring -35
+KPX Dcroat Atilde -35
+KPX Dcroat V -40
+KPX Dcroat W -40
+KPX Dcroat Y -40
+KPX Dcroat Yacute -40
+KPX Dcroat Ydieresis -40
+KPX F A -115
+KPX F Aacute -115
+KPX F Abreve -115
+KPX F Acircumflex -115
+KPX F Adieresis -115
+KPX F Agrave -115
+KPX F Amacron -115
+KPX F Aogonek -115
+KPX F Aring -115
+KPX F Atilde -115
+KPX F a -75
+KPX F aacute -75
+KPX F abreve -75
+KPX F acircumflex -75
+KPX F adieresis -75
+KPX F agrave -75
+KPX F amacron -75
+KPX F aogonek -75
+KPX F aring -75
+KPX F atilde -75
+KPX F comma -135
+KPX F e -75
+KPX F eacute -75
+KPX F ecaron -75
+KPX F ecircumflex -75
+KPX F edieresis -75
+KPX F edotaccent -75
+KPX F egrave -75
+KPX F emacron -75
+KPX F eogonek -75
+KPX F i -45
+KPX F iacute -45
+KPX F icircumflex -45
+KPX F idieresis -45
+KPX F igrave -45
+KPX F imacron -45
+KPX F iogonek -45
+KPX F o -105
+KPX F oacute -105
+KPX F ocircumflex -105
+KPX F odieresis -105
+KPX F ograve -105
+KPX F ohungarumlaut -105
+KPX F omacron -105
+KPX F oslash -105
+KPX F otilde -105
+KPX F period -135
+KPX F r -55
+KPX F racute -55
+KPX F rcaron -55
+KPX F rcommaaccent -55
+KPX J A -40
+KPX J Aacute -40
+KPX J Abreve -40
+KPX J Acircumflex -40
+KPX J Adieresis -40
+KPX J Agrave -40
+KPX J Amacron -40
+KPX J Aogonek -40
+KPX J Aring -40
+KPX J Atilde -40
+KPX J a -35
+KPX J aacute -35
+KPX J abreve -35
+KPX J acircumflex -35
+KPX J adieresis -35
+KPX J agrave -35
+KPX J amacron -35
+KPX J aogonek -35
+KPX J aring -35
+KPX J atilde -35
+KPX J comma -25
+KPX J e -25
+KPX J eacute -25
+KPX J ecaron -25
+KPX J ecircumflex -25
+KPX J edieresis -25
+KPX J edotaccent -25
+KPX J egrave -25
+KPX J emacron -25
+KPX J eogonek -25
+KPX J o -25
+KPX J oacute -25
+KPX J ocircumflex -25
+KPX J odieresis -25
+KPX J ograve -25
+KPX J ohungarumlaut -25
+KPX J omacron -25
+KPX J oslash -25
+KPX J otilde -25
+KPX J period -25
+KPX J u -35
+KPX J uacute -35
+KPX J ucircumflex -35
+KPX J udieresis -35
+KPX J ugrave -35
+KPX J uhungarumlaut -35
+KPX J umacron -35
+KPX J uogonek -35
+KPX J uring -35
+KPX K O -50
+KPX K Oacute -50
+KPX K Ocircumflex -50
+KPX K Odieresis -50
+KPX K Ograve -50
+KPX K Ohungarumlaut -50
+KPX K Omacron -50
+KPX K Oslash -50
+KPX K Otilde -50
+KPX K e -35
+KPX K eacute -35
+KPX K ecaron -35
+KPX K ecircumflex -35
+KPX K edieresis -35
+KPX K edotaccent -35
+KPX K egrave -35
+KPX K emacron -35
+KPX K eogonek -35
+KPX K o -40
+KPX K oacute -40
+KPX K ocircumflex -40
+KPX K odieresis -40
+KPX K ograve -40
+KPX K ohungarumlaut -40
+KPX K omacron -40
+KPX K oslash -40
+KPX K otilde -40
+KPX K u -40
+KPX K uacute -40
+KPX K ucircumflex -40
+KPX K udieresis -40
+KPX K ugrave -40
+KPX K uhungarumlaut -40
+KPX K umacron -40
+KPX K uogonek -40
+KPX K uring -40
+KPX K y -40
+KPX K yacute -40
+KPX K ydieresis -40
+KPX Kcommaaccent O -50
+KPX Kcommaaccent Oacute -50
+KPX Kcommaaccent Ocircumflex -50
+KPX Kcommaaccent Odieresis -50
+KPX Kcommaaccent Ograve -50
+KPX Kcommaaccent Ohungarumlaut -50
+KPX Kcommaaccent Omacron -50
+KPX Kcommaaccent Oslash -50
+KPX Kcommaaccent Otilde -50
+KPX Kcommaaccent e -35
+KPX Kcommaaccent eacute -35
+KPX Kcommaaccent ecaron -35
+KPX Kcommaaccent ecircumflex -35
+KPX Kcommaaccent edieresis -35
+KPX Kcommaaccent edotaccent -35
+KPX Kcommaaccent egrave -35
+KPX Kcommaaccent emacron -35
+KPX Kcommaaccent eogonek -35
+KPX Kcommaaccent o -40
+KPX Kcommaaccent oacute -40
+KPX Kcommaaccent ocircumflex -40
+KPX Kcommaaccent odieresis -40
+KPX Kcommaaccent ograve -40
+KPX Kcommaaccent ohungarumlaut -40
+KPX Kcommaaccent omacron -40
+KPX Kcommaaccent oslash -40
+KPX Kcommaaccent otilde -40
+KPX Kcommaaccent u -40
+KPX Kcommaaccent uacute -40
+KPX Kcommaaccent ucircumflex -40
+KPX Kcommaaccent udieresis -40
+KPX Kcommaaccent ugrave -40
+KPX Kcommaaccent uhungarumlaut -40
+KPX Kcommaaccent umacron -40
+KPX Kcommaaccent uogonek -40
+KPX Kcommaaccent uring -40
+KPX Kcommaaccent y -40
+KPX Kcommaaccent yacute -40
+KPX Kcommaaccent ydieresis -40
+KPX L T -20
+KPX L Tcaron -20
+KPX L Tcommaaccent -20
+KPX L V -55
+KPX L W -55
+KPX L Y -20
+KPX L Yacute -20
+KPX L Ydieresis -20
+KPX L quoteright -37
+KPX L y -30
+KPX L yacute -30
+KPX L ydieresis -30
+KPX Lacute T -20
+KPX Lacute Tcaron -20
+KPX Lacute Tcommaaccent -20
+KPX Lacute V -55
+KPX Lacute W -55
+KPX Lacute Y -20
+KPX Lacute Yacute -20
+KPX Lacute Ydieresis -20
+KPX Lacute quoteright -37
+KPX Lacute y -30
+KPX Lacute yacute -30
+KPX Lacute ydieresis -30
+KPX Lcommaaccent T -20
+KPX Lcommaaccent Tcaron -20
+KPX Lcommaaccent Tcommaaccent -20
+KPX Lcommaaccent V -55
+KPX Lcommaaccent W -55
+KPX Lcommaaccent Y -20
+KPX Lcommaaccent Yacute -20
+KPX Lcommaaccent Ydieresis -20
+KPX Lcommaaccent quoteright -37
+KPX Lcommaaccent y -30
+KPX Lcommaaccent yacute -30
+KPX Lcommaaccent ydieresis -30
+KPX Lslash T -20
+KPX Lslash Tcaron -20
+KPX Lslash Tcommaaccent -20
+KPX Lslash V -55
+KPX Lslash W -55
+KPX Lslash Y -20
+KPX Lslash Yacute -20
+KPX Lslash Ydieresis -20
+KPX Lslash quoteright -37
+KPX Lslash y -30
+KPX Lslash yacute -30
+KPX Lslash ydieresis -30
+KPX N A -27
+KPX N Aacute -27
+KPX N Abreve -27
+KPX N Acircumflex -27
+KPX N Adieresis -27
+KPX N Agrave -27
+KPX N Amacron -27
+KPX N Aogonek -27
+KPX N Aring -27
+KPX N Atilde -27
+KPX Nacute A -27
+KPX Nacute Aacute -27
+KPX Nacute Abreve -27
+KPX Nacute Acircumflex -27
+KPX Nacute Adieresis -27
+KPX Nacute Agrave -27
+KPX Nacute Amacron -27
+KPX Nacute Aogonek -27
+KPX Nacute Aring -27
+KPX Nacute Atilde -27
+KPX Ncaron A -27
+KPX Ncaron Aacute -27
+KPX Ncaron Abreve -27
+KPX Ncaron Acircumflex -27
+KPX Ncaron Adieresis -27
+KPX Ncaron Agrave -27
+KPX Ncaron Amacron -27
+KPX Ncaron Aogonek -27
+KPX Ncaron Aring -27
+KPX Ncaron Atilde -27
+KPX Ncommaaccent A -27
+KPX Ncommaaccent Aacute -27
+KPX Ncommaaccent Abreve -27
+KPX Ncommaaccent Acircumflex -27
+KPX Ncommaaccent Adieresis -27
+KPX Ncommaaccent Agrave -27
+KPX Ncommaaccent Amacron -27
+KPX Ncommaaccent Aogonek -27
+KPX Ncommaaccent Aring -27
+KPX Ncommaaccent Atilde -27
+KPX Ntilde A -27
+KPX Ntilde Aacute -27
+KPX Ntilde Abreve -27
+KPX Ntilde Acircumflex -27
+KPX Ntilde Adieresis -27
+KPX Ntilde Agrave -27
+KPX Ntilde Amacron -27
+KPX Ntilde Aogonek -27
+KPX Ntilde Aring -27
+KPX Ntilde Atilde -27
+KPX O A -55
+KPX O Aacute -55
+KPX O Abreve -55
+KPX O Acircumflex -55
+KPX O Adieresis -55
+KPX O Agrave -55
+KPX O Amacron -55
+KPX O Aogonek -55
+KPX O Aring -55
+KPX O Atilde -55
+KPX O T -40
+KPX O Tcaron -40
+KPX O Tcommaaccent -40
+KPX O V -50
+KPX O W -50
+KPX O X -40
+KPX O Y -50
+KPX O Yacute -50
+KPX O Ydieresis -50
+KPX Oacute A -55
+KPX Oacute Aacute -55
+KPX Oacute Abreve -55
+KPX Oacute Acircumflex -55
+KPX Oacute Adieresis -55
+KPX Oacute Agrave -55
+KPX Oacute Amacron -55
+KPX Oacute Aogonek -55
+KPX Oacute Aring -55
+KPX Oacute Atilde -55
+KPX Oacute T -40
+KPX Oacute Tcaron -40
+KPX Oacute Tcommaaccent -40
+KPX Oacute V -50
+KPX Oacute W -50
+KPX Oacute X -40
+KPX Oacute Y -50
+KPX Oacute Yacute -50
+KPX Oacute Ydieresis -50
+KPX Ocircumflex A -55
+KPX Ocircumflex Aacute -55
+KPX Ocircumflex Abreve -55
+KPX Ocircumflex Acircumflex -55
+KPX Ocircumflex Adieresis -55
+KPX Ocircumflex Agrave -55
+KPX Ocircumflex Amacron -55
+KPX Ocircumflex Aogonek -55
+KPX Ocircumflex Aring -55
+KPX Ocircumflex Atilde -55
+KPX Ocircumflex T -40
+KPX Ocircumflex Tcaron -40
+KPX Ocircumflex Tcommaaccent -40
+KPX Ocircumflex V -50
+KPX Ocircumflex W -50
+KPX Ocircumflex X -40
+KPX Ocircumflex Y -50
+KPX Ocircumflex Yacute -50
+KPX Ocircumflex Ydieresis -50
+KPX Odieresis A -55
+KPX Odieresis Aacute -55
+KPX Odieresis Abreve -55
+KPX Odieresis Acircumflex -55
+KPX Odieresis Adieresis -55
+KPX Odieresis Agrave -55
+KPX Odieresis Amacron -55
+KPX Odieresis Aogonek -55
+KPX Odieresis Aring -55
+KPX Odieresis Atilde -55
+KPX Odieresis T -40
+KPX Odieresis Tcaron -40
+KPX Odieresis Tcommaaccent -40
+KPX Odieresis V -50
+KPX Odieresis W -50
+KPX Odieresis X -40
+KPX Odieresis Y -50
+KPX Odieresis Yacute -50
+KPX Odieresis Ydieresis -50
+KPX Ograve A -55
+KPX Ograve Aacute -55
+KPX Ograve Abreve -55
+KPX Ograve Acircumflex -55
+KPX Ograve Adieresis -55
+KPX Ograve Agrave -55
+KPX Ograve Amacron -55
+KPX Ograve Aogonek -55
+KPX Ograve Aring -55
+KPX Ograve Atilde -55
+KPX Ograve T -40
+KPX Ograve Tcaron -40
+KPX Ograve Tcommaaccent -40
+KPX Ograve V -50
+KPX Ograve W -50
+KPX Ograve X -40
+KPX Ograve Y -50
+KPX Ograve Yacute -50
+KPX Ograve Ydieresis -50
+KPX Ohungarumlaut A -55
+KPX Ohungarumlaut Aacute -55
+KPX Ohungarumlaut Abreve -55
+KPX Ohungarumlaut Acircumflex -55
+KPX Ohungarumlaut Adieresis -55
+KPX Ohungarumlaut Agrave -55
+KPX Ohungarumlaut Amacron -55
+KPX Ohungarumlaut Aogonek -55
+KPX Ohungarumlaut Aring -55
+KPX Ohungarumlaut Atilde -55
+KPX Ohungarumlaut T -40
+KPX Ohungarumlaut Tcaron -40
+KPX Ohungarumlaut Tcommaaccent -40
+KPX Ohungarumlaut V -50
+KPX Ohungarumlaut W -50
+KPX Ohungarumlaut X -40
+KPX Ohungarumlaut Y -50
+KPX Ohungarumlaut Yacute -50
+KPX Ohungarumlaut Ydieresis -50
+KPX Omacron A -55
+KPX Omacron Aacute -55
+KPX Omacron Abreve -55
+KPX Omacron Acircumflex -55
+KPX Omacron Adieresis -55
+KPX Omacron Agrave -55
+KPX Omacron Amacron -55
+KPX Omacron Aogonek -55
+KPX Omacron Aring -55
+KPX Omacron Atilde -55
+KPX Omacron T -40
+KPX Omacron Tcaron -40
+KPX Omacron Tcommaaccent -40
+KPX Omacron V -50
+KPX Omacron W -50
+KPX Omacron X -40
+KPX Omacron Y -50
+KPX Omacron Yacute -50
+KPX Omacron Ydieresis -50
+KPX Oslash A -55
+KPX Oslash Aacute -55
+KPX Oslash Abreve -55
+KPX Oslash Acircumflex -55
+KPX Oslash Adieresis -55
+KPX Oslash Agrave -55
+KPX Oslash Amacron -55
+KPX Oslash Aogonek -55
+KPX Oslash Aring -55
+KPX Oslash Atilde -55
+KPX Oslash T -40
+KPX Oslash Tcaron -40
+KPX Oslash Tcommaaccent -40
+KPX Oslash V -50
+KPX Oslash W -50
+KPX Oslash X -40
+KPX Oslash Y -50
+KPX Oslash Yacute -50
+KPX Oslash Ydieresis -50
+KPX Otilde A -55
+KPX Otilde Aacute -55
+KPX Otilde Abreve -55
+KPX Otilde Acircumflex -55
+KPX Otilde Adieresis -55
+KPX Otilde Agrave -55
+KPX Otilde Amacron -55
+KPX Otilde Aogonek -55
+KPX Otilde Aring -55
+KPX Otilde Atilde -55
+KPX Otilde T -40
+KPX Otilde Tcaron -40
+KPX Otilde Tcommaaccent -40
+KPX Otilde V -50
+KPX Otilde W -50
+KPX Otilde X -40
+KPX Otilde Y -50
+KPX Otilde Yacute -50
+KPX Otilde Ydieresis -50
+KPX P A -90
+KPX P Aacute -90
+KPX P Abreve -90
+KPX P Acircumflex -90
+KPX P Adieresis -90
+KPX P Agrave -90
+KPX P Amacron -90
+KPX P Aogonek -90
+KPX P Aring -90
+KPX P Atilde -90
+KPX P a -80
+KPX P aacute -80
+KPX P abreve -80
+KPX P acircumflex -80
+KPX P adieresis -80
+KPX P agrave -80
+KPX P amacron -80
+KPX P aogonek -80
+KPX P aring -80
+KPX P atilde -80
+KPX P comma -135
+KPX P e -80
+KPX P eacute -80
+KPX P ecaron -80
+KPX P ecircumflex -80
+KPX P edieresis -80
+KPX P edotaccent -80
+KPX P egrave -80
+KPX P emacron -80
+KPX P eogonek -80
+KPX P o -80
+KPX P oacute -80
+KPX P ocircumflex -80
+KPX P odieresis -80
+KPX P ograve -80
+KPX P ohungarumlaut -80
+KPX P omacron -80
+KPX P oslash -80
+KPX P otilde -80
+KPX P period -135
+KPX Q U -10
+KPX Q Uacute -10
+KPX Q Ucircumflex -10
+KPX Q Udieresis -10
+KPX Q Ugrave -10
+KPX Q Uhungarumlaut -10
+KPX Q Umacron -10
+KPX Q Uogonek -10
+KPX Q Uring -10
+KPX R O -40
+KPX R Oacute -40
+KPX R Ocircumflex -40
+KPX R Odieresis -40
+KPX R Ograve -40
+KPX R Ohungarumlaut -40
+KPX R Omacron -40
+KPX R Oslash -40
+KPX R Otilde -40
+KPX R U -40
+KPX R Uacute -40
+KPX R Ucircumflex -40
+KPX R Udieresis -40
+KPX R Ugrave -40
+KPX R Uhungarumlaut -40
+KPX R Umacron -40
+KPX R Uogonek -40
+KPX R Uring -40
+KPX R V -18
+KPX R W -18
+KPX R Y -18
+KPX R Yacute -18
+KPX R Ydieresis -18
+KPX Racute O -40
+KPX Racute Oacute -40
+KPX Racute Ocircumflex -40
+KPX Racute Odieresis -40
+KPX Racute Ograve -40
+KPX Racute Ohungarumlaut -40
+KPX Racute Omacron -40
+KPX Racute Oslash -40
+KPX Racute Otilde -40
+KPX Racute U -40
+KPX Racute Uacute -40
+KPX Racute Ucircumflex -40
+KPX Racute Udieresis -40
+KPX Racute Ugrave -40
+KPX Racute Uhungarumlaut -40
+KPX Racute Umacron -40
+KPX Racute Uogonek -40
+KPX Racute Uring -40
+KPX Racute V -18
+KPX Racute W -18
+KPX Racute Y -18
+KPX Racute Yacute -18
+KPX Racute Ydieresis -18
+KPX Rcaron O -40
+KPX Rcaron Oacute -40
+KPX Rcaron Ocircumflex -40
+KPX Rcaron Odieresis -40
+KPX Rcaron Ograve -40
+KPX Rcaron Ohungarumlaut -40
+KPX Rcaron Omacron -40
+KPX Rcaron Oslash -40
+KPX Rcaron Otilde -40
+KPX Rcaron U -40
+KPX Rcaron Uacute -40
+KPX Rcaron Ucircumflex -40
+KPX Rcaron Udieresis -40
+KPX Rcaron Ugrave -40
+KPX Rcaron Uhungarumlaut -40
+KPX Rcaron Umacron -40
+KPX Rcaron Uogonek -40
+KPX Rcaron Uring -40
+KPX Rcaron V -18
+KPX Rcaron W -18
+KPX Rcaron Y -18
+KPX Rcaron Yacute -18
+KPX Rcaron Ydieresis -18
+KPX Rcommaaccent O -40
+KPX Rcommaaccent Oacute -40
+KPX Rcommaaccent Ocircumflex -40
+KPX Rcommaaccent Odieresis -40
+KPX Rcommaaccent Ograve -40
+KPX Rcommaaccent Ohungarumlaut -40
+KPX Rcommaaccent Omacron -40
+KPX Rcommaaccent Oslash -40
+KPX Rcommaaccent Otilde -40
+KPX Rcommaaccent U -40
+KPX Rcommaaccent Uacute -40
+KPX Rcommaaccent Ucircumflex -40
+KPX Rcommaaccent Udieresis -40
+KPX Rcommaaccent Ugrave -40
+KPX Rcommaaccent Uhungarumlaut -40
+KPX Rcommaaccent Umacron -40
+KPX Rcommaaccent Uogonek -40
+KPX Rcommaaccent Uring -40
+KPX Rcommaaccent V -18
+KPX Rcommaaccent W -18
+KPX Rcommaaccent Y -18
+KPX Rcommaaccent Yacute -18
+KPX Rcommaaccent Ydieresis -18
+KPX T A -50
+KPX T Aacute -50
+KPX T Abreve -50
+KPX T Acircumflex -50
+KPX T Adieresis -50
+KPX T Agrave -50
+KPX T Amacron -50
+KPX T Aogonek -50
+KPX T Aring -50
+KPX T Atilde -50
+KPX T O -18
+KPX T Oacute -18
+KPX T Ocircumflex -18
+KPX T Odieresis -18
+KPX T Ograve -18
+KPX T Ohungarumlaut -18
+KPX T Omacron -18
+KPX T Oslash -18
+KPX T Otilde -18
+KPX T a -92
+KPX T aacute -92
+KPX T abreve -92
+KPX T acircumflex -92
+KPX T adieresis -92
+KPX T agrave -92
+KPX T amacron -92
+KPX T aogonek -92
+KPX T aring -92
+KPX T atilde -92
+KPX T colon -55
+KPX T comma -74
+KPX T e -92
+KPX T eacute -92
+KPX T ecaron -92
+KPX T ecircumflex -52
+KPX T edieresis -52
+KPX T edotaccent -92
+KPX T egrave -52
+KPX T emacron -52
+KPX T eogonek -92
+KPX T hyphen -74
+KPX T i -55
+KPX T iacute -55
+KPX T iogonek -55
+KPX T o -92
+KPX T oacute -92
+KPX T ocircumflex -92
+KPX T odieresis -92
+KPX T ograve -92
+KPX T ohungarumlaut -92
+KPX T omacron -92
+KPX T oslash -92
+KPX T otilde -92
+KPX T period -74
+KPX T r -55
+KPX T racute -55
+KPX T rcaron -55
+KPX T rcommaaccent -55
+KPX T semicolon -65
+KPX T u -55
+KPX T uacute -55
+KPX T ucircumflex -55
+KPX T udieresis -55
+KPX T ugrave -55
+KPX T uhungarumlaut -55
+KPX T umacron -55
+KPX T uogonek -55
+KPX T uring -55
+KPX T w -74
+KPX T y -74
+KPX T yacute -74
+KPX T ydieresis -34
+KPX Tcaron A -50
+KPX Tcaron Aacute -50
+KPX Tcaron Abreve -50
+KPX Tcaron Acircumflex -50
+KPX Tcaron Adieresis -50
+KPX Tcaron Agrave -50
+KPX Tcaron Amacron -50
+KPX Tcaron Aogonek -50
+KPX Tcaron Aring -50
+KPX Tcaron Atilde -50
+KPX Tcaron O -18
+KPX Tcaron Oacute -18
+KPX Tcaron Ocircumflex -18
+KPX Tcaron Odieresis -18
+KPX Tcaron Ograve -18
+KPX Tcaron Ohungarumlaut -18
+KPX Tcaron Omacron -18
+KPX Tcaron Oslash -18
+KPX Tcaron Otilde -18
+KPX Tcaron a -92
+KPX Tcaron aacute -92
+KPX Tcaron abreve -92
+KPX Tcaron acircumflex -92
+KPX Tcaron adieresis -92
+KPX Tcaron agrave -92
+KPX Tcaron amacron -92
+KPX Tcaron aogonek -92
+KPX Tcaron aring -92
+KPX Tcaron atilde -92
+KPX Tcaron colon -55
+KPX Tcaron comma -74
+KPX Tcaron e -92
+KPX Tcaron eacute -92
+KPX Tcaron ecaron -92
+KPX Tcaron ecircumflex -52
+KPX Tcaron edieresis -52
+KPX Tcaron edotaccent -92
+KPX Tcaron egrave -52
+KPX Tcaron emacron -52
+KPX Tcaron eogonek -92
+KPX Tcaron hyphen -74
+KPX Tcaron i -55
+KPX Tcaron iacute -55
+KPX Tcaron iogonek -55
+KPX Tcaron o -92
+KPX Tcaron oacute -92
+KPX Tcaron ocircumflex -92
+KPX Tcaron odieresis -92
+KPX Tcaron ograve -92
+KPX Tcaron ohungarumlaut -92
+KPX Tcaron omacron -92
+KPX Tcaron oslash -92
+KPX Tcaron otilde -92
+KPX Tcaron period -74
+KPX Tcaron r -55
+KPX Tcaron racute -55
+KPX Tcaron rcaron -55
+KPX Tcaron rcommaaccent -55
+KPX Tcaron semicolon -65
+KPX Tcaron u -55
+KPX Tcaron uacute -55
+KPX Tcaron ucircumflex -55
+KPX Tcaron udieresis -55
+KPX Tcaron ugrave -55
+KPX Tcaron uhungarumlaut -55
+KPX Tcaron umacron -55
+KPX Tcaron uogonek -55
+KPX Tcaron uring -55
+KPX Tcaron w -74
+KPX Tcaron y -74
+KPX Tcaron yacute -74
+KPX Tcaron ydieresis -34
+KPX Tcommaaccent A -50
+KPX Tcommaaccent Aacute -50
+KPX Tcommaaccent Abreve -50
+KPX Tcommaaccent Acircumflex -50
+KPX Tcommaaccent Adieresis -50
+KPX Tcommaaccent Agrave -50
+KPX Tcommaaccent Amacron -50
+KPX Tcommaaccent Aogonek -50
+KPX Tcommaaccent Aring -50
+KPX Tcommaaccent Atilde -50
+KPX Tcommaaccent O -18
+KPX Tcommaaccent Oacute -18
+KPX Tcommaaccent Ocircumflex -18
+KPX Tcommaaccent Odieresis -18
+KPX Tcommaaccent Ograve -18
+KPX Tcommaaccent Ohungarumlaut -18
+KPX Tcommaaccent Omacron -18
+KPX Tcommaaccent Oslash -18
+KPX Tcommaaccent Otilde -18
+KPX Tcommaaccent a -92
+KPX Tcommaaccent aacute -92
+KPX Tcommaaccent abreve -92
+KPX Tcommaaccent acircumflex -92
+KPX Tcommaaccent adieresis -92
+KPX Tcommaaccent agrave -92
+KPX Tcommaaccent amacron -92
+KPX Tcommaaccent aogonek -92
+KPX Tcommaaccent aring -92
+KPX Tcommaaccent atilde -92
+KPX Tcommaaccent colon -55
+KPX Tcommaaccent comma -74
+KPX Tcommaaccent e -92
+KPX Tcommaaccent eacute -92
+KPX Tcommaaccent ecaron -92
+KPX Tcommaaccent ecircumflex -52
+KPX Tcommaaccent edieresis -52
+KPX Tcommaaccent edotaccent -92
+KPX Tcommaaccent egrave -52
+KPX Tcommaaccent emacron -52
+KPX Tcommaaccent eogonek -92
+KPX Tcommaaccent hyphen -74
+KPX Tcommaaccent i -55
+KPX Tcommaaccent iacute -55
+KPX Tcommaaccent iogonek -55
+KPX Tcommaaccent o -92
+KPX Tcommaaccent oacute -92
+KPX Tcommaaccent ocircumflex -92
+KPX Tcommaaccent odieresis -92
+KPX Tcommaaccent ograve -92
+KPX Tcommaaccent ohungarumlaut -92
+KPX Tcommaaccent omacron -92
+KPX Tcommaaccent oslash -92
+KPX Tcommaaccent otilde -92
+KPX Tcommaaccent period -74
+KPX Tcommaaccent r -55
+KPX Tcommaaccent racute -55
+KPX Tcommaaccent rcaron -55
+KPX Tcommaaccent rcommaaccent -55
+KPX Tcommaaccent semicolon -65
+KPX Tcommaaccent u -55
+KPX Tcommaaccent uacute -55
+KPX Tcommaaccent ucircumflex -55
+KPX Tcommaaccent udieresis -55
+KPX Tcommaaccent ugrave -55
+KPX Tcommaaccent uhungarumlaut -55
+KPX Tcommaaccent umacron -55
+KPX Tcommaaccent uogonek -55
+KPX Tcommaaccent uring -55
+KPX Tcommaaccent w -74
+KPX Tcommaaccent y -74
+KPX Tcommaaccent yacute -74
+KPX Tcommaaccent ydieresis -34
+KPX U A -40
+KPX U Aacute -40
+KPX U Abreve -40
+KPX U Acircumflex -40
+KPX U Adieresis -40
+KPX U Agrave -40
+KPX U Amacron -40
+KPX U Aogonek -40
+KPX U Aring -40
+KPX U Atilde -40
+KPX U comma -25
+KPX U period -25
+KPX Uacute A -40
+KPX Uacute Aacute -40
+KPX Uacute Abreve -40
+KPX Uacute Acircumflex -40
+KPX Uacute Adieresis -40
+KPX Uacute Agrave -40
+KPX Uacute Amacron -40
+KPX Uacute Aogonek -40
+KPX Uacute Aring -40
+KPX Uacute Atilde -40
+KPX Uacute comma -25
+KPX Uacute period -25
+KPX Ucircumflex A -40
+KPX Ucircumflex Aacute -40
+KPX Ucircumflex Abreve -40
+KPX Ucircumflex Acircumflex -40
+KPX Ucircumflex Adieresis -40
+KPX Ucircumflex Agrave -40
+KPX Ucircumflex Amacron -40
+KPX Ucircumflex Aogonek -40
+KPX Ucircumflex Aring -40
+KPX Ucircumflex Atilde -40
+KPX Ucircumflex comma -25
+KPX Ucircumflex period -25
+KPX Udieresis A -40
+KPX Udieresis Aacute -40
+KPX Udieresis Abreve -40
+KPX Udieresis Acircumflex -40
+KPX Udieresis Adieresis -40
+KPX Udieresis Agrave -40
+KPX Udieresis Amacron -40
+KPX Udieresis Aogonek -40
+KPX Udieresis Aring -40
+KPX Udieresis Atilde -40
+KPX Udieresis comma -25
+KPX Udieresis period -25
+KPX Ugrave A -40
+KPX Ugrave Aacute -40
+KPX Ugrave Abreve -40
+KPX Ugrave Acircumflex -40
+KPX Ugrave Adieresis -40
+KPX Ugrave Agrave -40
+KPX Ugrave Amacron -40
+KPX Ugrave Aogonek -40
+KPX Ugrave Aring -40
+KPX Ugrave Atilde -40
+KPX Ugrave comma -25
+KPX Ugrave period -25
+KPX Uhungarumlaut A -40
+KPX Uhungarumlaut Aacute -40
+KPX Uhungarumlaut Abreve -40
+KPX Uhungarumlaut Acircumflex -40
+KPX Uhungarumlaut Adieresis -40
+KPX Uhungarumlaut Agrave -40
+KPX Uhungarumlaut Amacron -40
+KPX Uhungarumlaut Aogonek -40
+KPX Uhungarumlaut Aring -40
+KPX Uhungarumlaut Atilde -40
+KPX Uhungarumlaut comma -25
+KPX Uhungarumlaut period -25
+KPX Umacron A -40
+KPX Umacron Aacute -40
+KPX Umacron Abreve -40
+KPX Umacron Acircumflex -40
+KPX Umacron Adieresis -40
+KPX Umacron Agrave -40
+KPX Umacron Amacron -40
+KPX Umacron Aogonek -40
+KPX Umacron Aring -40
+KPX Umacron Atilde -40
+KPX Umacron comma -25
+KPX Umacron period -25
+KPX Uogonek A -40
+KPX Uogonek Aacute -40
+KPX Uogonek Abreve -40
+KPX Uogonek Acircumflex -40
+KPX Uogonek Adieresis -40
+KPX Uogonek Agrave -40
+KPX Uogonek Amacron -40
+KPX Uogonek Aogonek -40
+KPX Uogonek Aring -40
+KPX Uogonek Atilde -40
+KPX Uogonek comma -25
+KPX Uogonek period -25
+KPX Uring A -40
+KPX Uring Aacute -40
+KPX Uring Abreve -40
+KPX Uring Acircumflex -40
+KPX Uring Adieresis -40
+KPX Uring Agrave -40
+KPX Uring Amacron -40
+KPX Uring Aogonek -40
+KPX Uring Aring -40
+KPX Uring Atilde -40
+KPX Uring comma -25
+KPX Uring period -25
+KPX V A -60
+KPX V Aacute -60
+KPX V Abreve -60
+KPX V Acircumflex -60
+KPX V Adieresis -60
+KPX V Agrave -60
+KPX V Amacron -60
+KPX V Aogonek -60
+KPX V Aring -60
+KPX V Atilde -60
+KPX V O -30
+KPX V Oacute -30
+KPX V Ocircumflex -30
+KPX V Odieresis -30
+KPX V Ograve -30
+KPX V Ohungarumlaut -30
+KPX V Omacron -30
+KPX V Oslash -30
+KPX V Otilde -30
+KPX V a -111
+KPX V aacute -111
+KPX V abreve -111
+KPX V acircumflex -111
+KPX V adieresis -111
+KPX V agrave -111
+KPX V amacron -111
+KPX V aogonek -111
+KPX V aring -111
+KPX V atilde -111
+KPX V colon -65
+KPX V comma -129
+KPX V e -111
+KPX V eacute -111
+KPX V ecaron -111
+KPX V ecircumflex -111
+KPX V edieresis -71
+KPX V edotaccent -111
+KPX V egrave -71
+KPX V emacron -71
+KPX V eogonek -111
+KPX V hyphen -55
+KPX V i -74
+KPX V iacute -74
+KPX V icircumflex -34
+KPX V idieresis -34
+KPX V igrave -34
+KPX V imacron -34
+KPX V iogonek -74
+KPX V o -111
+KPX V oacute -111
+KPX V ocircumflex -111
+KPX V odieresis -111
+KPX V ograve -111
+KPX V ohungarumlaut -111
+KPX V omacron -111
+KPX V oslash -111
+KPX V otilde -111
+KPX V period -129
+KPX V semicolon -74
+KPX V u -74
+KPX V uacute -74
+KPX V ucircumflex -74
+KPX V udieresis -74
+KPX V ugrave -74
+KPX V uhungarumlaut -74
+KPX V umacron -74
+KPX V uogonek -74
+KPX V uring -74
+KPX W A -60
+KPX W Aacute -60
+KPX W Abreve -60
+KPX W Acircumflex -60
+KPX W Adieresis -60
+KPX W Agrave -60
+KPX W Amacron -60
+KPX W Aogonek -60
+KPX W Aring -60
+KPX W Atilde -60
+KPX W O -25
+KPX W Oacute -25
+KPX W Ocircumflex -25
+KPX W Odieresis -25
+KPX W Ograve -25
+KPX W Ohungarumlaut -25
+KPX W Omacron -25
+KPX W Oslash -25
+KPX W Otilde -25
+KPX W a -92
+KPX W aacute -92
+KPX W abreve -92
+KPX W acircumflex -92
+KPX W adieresis -92
+KPX W agrave -92
+KPX W amacron -92
+KPX W aogonek -92
+KPX W aring -92
+KPX W atilde -92
+KPX W colon -65
+KPX W comma -92
+KPX W e -92
+KPX W eacute -92
+KPX W ecaron -92
+KPX W ecircumflex -92
+KPX W edieresis -52
+KPX W edotaccent -92
+KPX W egrave -52
+KPX W emacron -52
+KPX W eogonek -92
+KPX W hyphen -37
+KPX W i -55
+KPX W iacute -55
+KPX W iogonek -55
+KPX W o -92
+KPX W oacute -92
+KPX W ocircumflex -92
+KPX W odieresis -92
+KPX W ograve -92
+KPX W ohungarumlaut -92
+KPX W omacron -92
+KPX W oslash -92
+KPX W otilde -92
+KPX W period -92
+KPX W semicolon -65
+KPX W u -55
+KPX W uacute -55
+KPX W ucircumflex -55
+KPX W udieresis -55
+KPX W ugrave -55
+KPX W uhungarumlaut -55
+KPX W umacron -55
+KPX W uogonek -55
+KPX W uring -55
+KPX W y -70
+KPX W yacute -70
+KPX W ydieresis -70
+KPX Y A -50
+KPX Y Aacute -50
+KPX Y Abreve -50
+KPX Y Acircumflex -50
+KPX Y Adieresis -50
+KPX Y Agrave -50
+KPX Y Amacron -50
+KPX Y Aogonek -50
+KPX Y Aring -50
+KPX Y Atilde -50
+KPX Y O -15
+KPX Y Oacute -15
+KPX Y Ocircumflex -15
+KPX Y Odieresis -15
+KPX Y Ograve -15
+KPX Y Ohungarumlaut -15
+KPX Y Omacron -15
+KPX Y Oslash -15
+KPX Y Otilde -15
+KPX Y a -92
+KPX Y aacute -92
+KPX Y abreve -92
+KPX Y acircumflex -92
+KPX Y adieresis -92
+KPX Y agrave -92
+KPX Y amacron -92
+KPX Y aogonek -92
+KPX Y aring -92
+KPX Y atilde -92
+KPX Y colon -65
+KPX Y comma -92
+KPX Y e -92
+KPX Y eacute -92
+KPX Y ecaron -92
+KPX Y ecircumflex -92
+KPX Y edieresis -52
+KPX Y edotaccent -92
+KPX Y egrave -52
+KPX Y emacron -52
+KPX Y eogonek -92
+KPX Y hyphen -74
+KPX Y i -74
+KPX Y iacute -74
+KPX Y icircumflex -34
+KPX Y idieresis -34
+KPX Y igrave -34
+KPX Y imacron -34
+KPX Y iogonek -74
+KPX Y o -92
+KPX Y oacute -92
+KPX Y ocircumflex -92
+KPX Y odieresis -92
+KPX Y ograve -92
+KPX Y ohungarumlaut -92
+KPX Y omacron -92
+KPX Y oslash -92
+KPX Y otilde -92
+KPX Y period -92
+KPX Y semicolon -65
+KPX Y u -92
+KPX Y uacute -92
+KPX Y ucircumflex -92
+KPX Y udieresis -92
+KPX Y ugrave -92
+KPX Y uhungarumlaut -92
+KPX Y umacron -92
+KPX Y uogonek -92
+KPX Y uring -92
+KPX Yacute A -50
+KPX Yacute Aacute -50
+KPX Yacute Abreve -50
+KPX Yacute Acircumflex -50
+KPX Yacute Adieresis -50
+KPX Yacute Agrave -50
+KPX Yacute Amacron -50
+KPX Yacute Aogonek -50
+KPX Yacute Aring -50
+KPX Yacute Atilde -50
+KPX Yacute O -15
+KPX Yacute Oacute -15
+KPX Yacute Ocircumflex -15
+KPX Yacute Odieresis -15
+KPX Yacute Ograve -15
+KPX Yacute Ohungarumlaut -15
+KPX Yacute Omacron -15
+KPX Yacute Oslash -15
+KPX Yacute Otilde -15
+KPX Yacute a -92
+KPX Yacute aacute -92
+KPX Yacute abreve -92
+KPX Yacute acircumflex -92
+KPX Yacute adieresis -92
+KPX Yacute agrave -92
+KPX Yacute amacron -92
+KPX Yacute aogonek -92
+KPX Yacute aring -92
+KPX Yacute atilde -92
+KPX Yacute colon -65
+KPX Yacute comma -92
+KPX Yacute e -92
+KPX Yacute eacute -92
+KPX Yacute ecaron -92
+KPX Yacute ecircumflex -92
+KPX Yacute edieresis -52
+KPX Yacute edotaccent -92
+KPX Yacute egrave -52
+KPX Yacute emacron -52
+KPX Yacute eogonek -92
+KPX Yacute hyphen -74
+KPX Yacute i -74
+KPX Yacute iacute -74
+KPX Yacute icircumflex -34
+KPX Yacute idieresis -34
+KPX Yacute igrave -34
+KPX Yacute imacron -34
+KPX Yacute iogonek -74
+KPX Yacute o -92
+KPX Yacute oacute -92
+KPX Yacute ocircumflex -92
+KPX Yacute odieresis -92
+KPX Yacute ograve -92
+KPX Yacute ohungarumlaut -92
+KPX Yacute omacron -92
+KPX Yacute oslash -92
+KPX Yacute otilde -92
+KPX Yacute period -92
+KPX Yacute semicolon -65
+KPX Yacute u -92
+KPX Yacute uacute -92
+KPX Yacute ucircumflex -92
+KPX Yacute udieresis -92
+KPX Yacute ugrave -92
+KPX Yacute uhungarumlaut -92
+KPX Yacute umacron -92
+KPX Yacute uogonek -92
+KPX Yacute uring -92
+KPX Ydieresis A -50
+KPX Ydieresis Aacute -50
+KPX Ydieresis Abreve -50
+KPX Ydieresis Acircumflex -50
+KPX Ydieresis Adieresis -50
+KPX Ydieresis Agrave -50
+KPX Ydieresis Amacron -50
+KPX Ydieresis Aogonek -50
+KPX Ydieresis Aring -50
+KPX Ydieresis Atilde -50
+KPX Ydieresis O -15
+KPX Ydieresis Oacute -15
+KPX Ydieresis Ocircumflex -15
+KPX Ydieresis Odieresis -15
+KPX Ydieresis Ograve -15
+KPX Ydieresis Ohungarumlaut -15
+KPX Ydieresis Omacron -15
+KPX Ydieresis Oslash -15
+KPX Ydieresis Otilde -15
+KPX Ydieresis a -92
+KPX Ydieresis aacute -92
+KPX Ydieresis abreve -92
+KPX Ydieresis acircumflex -92
+KPX Ydieresis adieresis -92
+KPX Ydieresis agrave -92
+KPX Ydieresis amacron -92
+KPX Ydieresis aogonek -92
+KPX Ydieresis aring -92
+KPX Ydieresis atilde -92
+KPX Ydieresis colon -65
+KPX Ydieresis comma -92
+KPX Ydieresis e -92
+KPX Ydieresis eacute -92
+KPX Ydieresis ecaron -92
+KPX Ydieresis ecircumflex -92
+KPX Ydieresis edieresis -52
+KPX Ydieresis edotaccent -92
+KPX Ydieresis egrave -52
+KPX Ydieresis emacron -52
+KPX Ydieresis eogonek -92
+KPX Ydieresis hyphen -74
+KPX Ydieresis i -74
+KPX Ydieresis iacute -74
+KPX Ydieresis icircumflex -34
+KPX Ydieresis idieresis -34
+KPX Ydieresis igrave -34
+KPX Ydieresis imacron -34
+KPX Ydieresis iogonek -74
+KPX Ydieresis o -92
+KPX Ydieresis oacute -92
+KPX Ydieresis ocircumflex -92
+KPX Ydieresis odieresis -92
+KPX Ydieresis ograve -92
+KPX Ydieresis ohungarumlaut -92
+KPX Ydieresis omacron -92
+KPX Ydieresis oslash -92
+KPX Ydieresis otilde -92
+KPX Ydieresis period -92
+KPX Ydieresis semicolon -65
+KPX Ydieresis u -92
+KPX Ydieresis uacute -92
+KPX Ydieresis ucircumflex -92
+KPX Ydieresis udieresis -92
+KPX Ydieresis ugrave -92
+KPX Ydieresis uhungarumlaut -92
+KPX Ydieresis umacron -92
+KPX Ydieresis uogonek -92
+KPX Ydieresis uring -92
+KPX a g -10
+KPX a gbreve -10
+KPX a gcommaaccent -10
+KPX aacute g -10
+KPX aacute gbreve -10
+KPX aacute gcommaaccent -10
+KPX abreve g -10
+KPX abreve gbreve -10
+KPX abreve gcommaaccent -10
+KPX acircumflex g -10
+KPX acircumflex gbreve -10
+KPX acircumflex gcommaaccent -10
+KPX adieresis g -10
+KPX adieresis gbreve -10
+KPX adieresis gcommaaccent -10
+KPX agrave g -10
+KPX agrave gbreve -10
+KPX agrave gcommaaccent -10
+KPX amacron g -10
+KPX amacron gbreve -10
+KPX amacron gcommaaccent -10
+KPX aogonek g -10
+KPX aogonek gbreve -10
+KPX aogonek gcommaaccent -10
+KPX aring g -10
+KPX aring gbreve -10
+KPX aring gcommaaccent -10
+KPX atilde g -10
+KPX atilde gbreve -10
+KPX atilde gcommaaccent -10
+KPX b period -40
+KPX b u -20
+KPX b uacute -20
+KPX b ucircumflex -20
+KPX b udieresis -20
+KPX b ugrave -20
+KPX b uhungarumlaut -20
+KPX b umacron -20
+KPX b uogonek -20
+KPX b uring -20
+KPX c h -15
+KPX c k -20
+KPX c kcommaaccent -20
+KPX cacute h -15
+KPX cacute k -20
+KPX cacute kcommaaccent -20
+KPX ccaron h -15
+KPX ccaron k -20
+KPX ccaron kcommaaccent -20
+KPX ccedilla h -15
+KPX ccedilla k -20
+KPX ccedilla kcommaaccent -20
+KPX comma quotedblright -140
+KPX comma quoteright -140
+KPX e comma -10
+KPX e g -40
+KPX e gbreve -40
+KPX e gcommaaccent -40
+KPX e period -15
+KPX e v -15
+KPX e w -15
+KPX e x -20
+KPX e y -30
+KPX e yacute -30
+KPX e ydieresis -30
+KPX eacute comma -10
+KPX eacute g -40
+KPX eacute gbreve -40
+KPX eacute gcommaaccent -40
+KPX eacute period -15
+KPX eacute v -15
+KPX eacute w -15
+KPX eacute x -20
+KPX eacute y -30
+KPX eacute yacute -30
+KPX eacute ydieresis -30
+KPX ecaron comma -10
+KPX ecaron g -40
+KPX ecaron gbreve -40
+KPX ecaron gcommaaccent -40
+KPX ecaron period -15
+KPX ecaron v -15
+KPX ecaron w -15
+KPX ecaron x -20
+KPX ecaron y -30
+KPX ecaron yacute -30
+KPX ecaron ydieresis -30
+KPX ecircumflex comma -10
+KPX ecircumflex g -40
+KPX ecircumflex gbreve -40
+KPX ecircumflex gcommaaccent -40
+KPX ecircumflex period -15
+KPX ecircumflex v -15
+KPX ecircumflex w -15
+KPX ecircumflex x -20
+KPX ecircumflex y -30
+KPX ecircumflex yacute -30
+KPX ecircumflex ydieresis -30
+KPX edieresis comma -10
+KPX edieresis g -40
+KPX edieresis gbreve -40
+KPX edieresis gcommaaccent -40
+KPX edieresis period -15
+KPX edieresis v -15
+KPX edieresis w -15
+KPX edieresis x -20
+KPX edieresis y -30
+KPX edieresis yacute -30
+KPX edieresis ydieresis -30
+KPX edotaccent comma -10
+KPX edotaccent g -40
+KPX edotaccent gbreve -40
+KPX edotaccent gcommaaccent -40
+KPX edotaccent period -15
+KPX edotaccent v -15
+KPX edotaccent w -15
+KPX edotaccent x -20
+KPX edotaccent y -30
+KPX edotaccent yacute -30
+KPX edotaccent ydieresis -30
+KPX egrave comma -10
+KPX egrave g -40
+KPX egrave gbreve -40
+KPX egrave gcommaaccent -40
+KPX egrave period -15
+KPX egrave v -15
+KPX egrave w -15
+KPX egrave x -20
+KPX egrave y -30
+KPX egrave yacute -30
+KPX egrave ydieresis -30
+KPX emacron comma -10
+KPX emacron g -40
+KPX emacron gbreve -40
+KPX emacron gcommaaccent -40
+KPX emacron period -15
+KPX emacron v -15
+KPX emacron w -15
+KPX emacron x -20
+KPX emacron y -30
+KPX emacron yacute -30
+KPX emacron ydieresis -30
+KPX eogonek comma -10
+KPX eogonek g -40
+KPX eogonek gbreve -40
+KPX eogonek gcommaaccent -40
+KPX eogonek period -15
+KPX eogonek v -15
+KPX eogonek w -15
+KPX eogonek x -20
+KPX eogonek y -30
+KPX eogonek yacute -30
+KPX eogonek ydieresis -30
+KPX f comma -10
+KPX f dotlessi -60
+KPX f f -18
+KPX f i -20
+KPX f iogonek -20
+KPX f period -15
+KPX f quoteright 92
+KPX g comma -10
+KPX g e -10
+KPX g eacute -10
+KPX g ecaron -10
+KPX g ecircumflex -10
+KPX g edieresis -10
+KPX g edotaccent -10
+KPX g egrave -10
+KPX g emacron -10
+KPX g eogonek -10
+KPX g g -10
+KPX g gbreve -10
+KPX g gcommaaccent -10
+KPX g period -15
+KPX gbreve comma -10
+KPX gbreve e -10
+KPX gbreve eacute -10
+KPX gbreve ecaron -10
+KPX gbreve ecircumflex -10
+KPX gbreve edieresis -10
+KPX gbreve edotaccent -10
+KPX gbreve egrave -10
+KPX gbreve emacron -10
+KPX gbreve eogonek -10
+KPX gbreve g -10
+KPX gbreve gbreve -10
+KPX gbreve gcommaaccent -10
+KPX gbreve period -15
+KPX gcommaaccent comma -10
+KPX gcommaaccent e -10
+KPX gcommaaccent eacute -10
+KPX gcommaaccent ecaron -10
+KPX gcommaaccent ecircumflex -10
+KPX gcommaaccent edieresis -10
+KPX gcommaaccent edotaccent -10
+KPX gcommaaccent egrave -10
+KPX gcommaaccent emacron -10
+KPX gcommaaccent eogonek -10
+KPX gcommaaccent g -10
+KPX gcommaaccent gbreve -10
+KPX gcommaaccent gcommaaccent -10
+KPX gcommaaccent period -15
+KPX k e -10
+KPX k eacute -10
+KPX k ecaron -10
+KPX k ecircumflex -10
+KPX k edieresis -10
+KPX k edotaccent -10
+KPX k egrave -10
+KPX k emacron -10
+KPX k eogonek -10
+KPX k o -10
+KPX k oacute -10
+KPX k ocircumflex -10
+KPX k odieresis -10
+KPX k ograve -10
+KPX k ohungarumlaut -10
+KPX k omacron -10
+KPX k oslash -10
+KPX k otilde -10
+KPX k y -10
+KPX k yacute -10
+KPX k ydieresis -10
+KPX kcommaaccent e -10
+KPX kcommaaccent eacute -10
+KPX kcommaaccent ecaron -10
+KPX kcommaaccent ecircumflex -10
+KPX kcommaaccent edieresis -10
+KPX kcommaaccent edotaccent -10
+KPX kcommaaccent egrave -10
+KPX kcommaaccent emacron -10
+KPX kcommaaccent eogonek -10
+KPX kcommaaccent o -10
+KPX kcommaaccent oacute -10
+KPX kcommaaccent ocircumflex -10
+KPX kcommaaccent odieresis -10
+KPX kcommaaccent ograve -10
+KPX kcommaaccent ohungarumlaut -10
+KPX kcommaaccent omacron -10
+KPX kcommaaccent oslash -10
+KPX kcommaaccent otilde -10
+KPX kcommaaccent y -10
+KPX kcommaaccent yacute -10
+KPX kcommaaccent ydieresis -10
+KPX n v -40
+KPX nacute v -40
+KPX ncaron v -40
+KPX ncommaaccent v -40
+KPX ntilde v -40
+KPX o g -10
+KPX o gbreve -10
+KPX o gcommaaccent -10
+KPX o v -10
+KPX oacute g -10
+KPX oacute gbreve -10
+KPX oacute gcommaaccent -10
+KPX oacute v -10
+KPX ocircumflex g -10
+KPX ocircumflex gbreve -10
+KPX ocircumflex gcommaaccent -10
+KPX ocircumflex v -10
+KPX odieresis g -10
+KPX odieresis gbreve -10
+KPX odieresis gcommaaccent -10
+KPX odieresis v -10
+KPX ograve g -10
+KPX ograve gbreve -10
+KPX ograve gcommaaccent -10
+KPX ograve v -10
+KPX ohungarumlaut g -10
+KPX ohungarumlaut gbreve -10
+KPX ohungarumlaut gcommaaccent -10
+KPX ohungarumlaut v -10
+KPX omacron g -10
+KPX omacron gbreve -10
+KPX omacron gcommaaccent -10
+KPX omacron v -10
+KPX oslash g -10
+KPX oslash gbreve -10
+KPX oslash gcommaaccent -10
+KPX oslash v -10
+KPX otilde g -10
+KPX otilde gbreve -10
+KPX otilde gcommaaccent -10
+KPX otilde v -10
+KPX period quotedblright -140
+KPX period quoteright -140
+KPX quoteleft quoteleft -111
+KPX quoteright d -25
+KPX quoteright dcroat -25
+KPX quoteright quoteright -111
+KPX quoteright r -25
+KPX quoteright racute -25
+KPX quoteright rcaron -25
+KPX quoteright rcommaaccent -25
+KPX quoteright s -40
+KPX quoteright sacute -40
+KPX quoteright scaron -40
+KPX quoteright scedilla -40
+KPX quoteright scommaaccent -40
+KPX quoteright space -111
+KPX quoteright t -30
+KPX quoteright tcommaaccent -30
+KPX quoteright v -10
+KPX r a -15
+KPX r aacute -15
+KPX r abreve -15
+KPX r acircumflex -15
+KPX r adieresis -15
+KPX r agrave -15
+KPX r amacron -15
+KPX r aogonek -15
+KPX r aring -15
+KPX r atilde -15
+KPX r c -37
+KPX r cacute -37
+KPX r ccaron -37
+KPX r ccedilla -37
+KPX r comma -111
+KPX r d -37
+KPX r dcroat -37
+KPX r e -37
+KPX r eacute -37
+KPX r ecaron -37
+KPX r ecircumflex -37
+KPX r edieresis -37
+KPX r edotaccent -37
+KPX r egrave -37
+KPX r emacron -37
+KPX r eogonek -37
+KPX r g -37
+KPX r gbreve -37
+KPX r gcommaaccent -37
+KPX r hyphen -20
+KPX r o -45
+KPX r oacute -45
+KPX r ocircumflex -45
+KPX r odieresis -45
+KPX r ograve -45
+KPX r ohungarumlaut -45
+KPX r omacron -45
+KPX r oslash -45
+KPX r otilde -45
+KPX r period -111
+KPX r q -37
+KPX r s -10
+KPX r sacute -10
+KPX r scaron -10
+KPX r scedilla -10
+KPX r scommaaccent -10
+KPX racute a -15
+KPX racute aacute -15
+KPX racute abreve -15
+KPX racute acircumflex -15
+KPX racute adieresis -15
+KPX racute agrave -15
+KPX racute amacron -15
+KPX racute aogonek -15
+KPX racute aring -15
+KPX racute atilde -15
+KPX racute c -37
+KPX racute cacute -37
+KPX racute ccaron -37
+KPX racute ccedilla -37
+KPX racute comma -111
+KPX racute d -37
+KPX racute dcroat -37
+KPX racute e -37
+KPX racute eacute -37
+KPX racute ecaron -37
+KPX racute ecircumflex -37
+KPX racute edieresis -37
+KPX racute edotaccent -37
+KPX racute egrave -37
+KPX racute emacron -37
+KPX racute eogonek -37
+KPX racute g -37
+KPX racute gbreve -37
+KPX racute gcommaaccent -37
+KPX racute hyphen -20
+KPX racute o -45
+KPX racute oacute -45
+KPX racute ocircumflex -45
+KPX racute odieresis -45
+KPX racute ograve -45
+KPX racute ohungarumlaut -45
+KPX racute omacron -45
+KPX racute oslash -45
+KPX racute otilde -45
+KPX racute period -111
+KPX racute q -37
+KPX racute s -10
+KPX racute sacute -10
+KPX racute scaron -10
+KPX racute scedilla -10
+KPX racute scommaaccent -10
+KPX rcaron a -15
+KPX rcaron aacute -15
+KPX rcaron abreve -15
+KPX rcaron acircumflex -15
+KPX rcaron adieresis -15
+KPX rcaron agrave -15
+KPX rcaron amacron -15
+KPX rcaron aogonek -15
+KPX rcaron aring -15
+KPX rcaron atilde -15
+KPX rcaron c -37
+KPX rcaron cacute -37
+KPX rcaron ccaron -37
+KPX rcaron ccedilla -37
+KPX rcaron comma -111
+KPX rcaron d -37
+KPX rcaron dcroat -37
+KPX rcaron e -37
+KPX rcaron eacute -37
+KPX rcaron ecaron -37
+KPX rcaron ecircumflex -37
+KPX rcaron edieresis -37
+KPX rcaron edotaccent -37
+KPX rcaron egrave -37
+KPX rcaron emacron -37
+KPX rcaron eogonek -37
+KPX rcaron g -37
+KPX rcaron gbreve -37
+KPX rcaron gcommaaccent -37
+KPX rcaron hyphen -20
+KPX rcaron o -45
+KPX rcaron oacute -45
+KPX rcaron ocircumflex -45
+KPX rcaron odieresis -45
+KPX rcaron ograve -45
+KPX rcaron ohungarumlaut -45
+KPX rcaron omacron -45
+KPX rcaron oslash -45
+KPX rcaron otilde -45
+KPX rcaron period -111
+KPX rcaron q -37
+KPX rcaron s -10
+KPX rcaron sacute -10
+KPX rcaron scaron -10
+KPX rcaron scedilla -10
+KPX rcaron scommaaccent -10
+KPX rcommaaccent a -15
+KPX rcommaaccent aacute -15
+KPX rcommaaccent abreve -15
+KPX rcommaaccent acircumflex -15
+KPX rcommaaccent adieresis -15
+KPX rcommaaccent agrave -15
+KPX rcommaaccent amacron -15
+KPX rcommaaccent aogonek -15
+KPX rcommaaccent aring -15
+KPX rcommaaccent atilde -15
+KPX rcommaaccent c -37
+KPX rcommaaccent cacute -37
+KPX rcommaaccent ccaron -37
+KPX rcommaaccent ccedilla -37
+KPX rcommaaccent comma -111
+KPX rcommaaccent d -37
+KPX rcommaaccent dcroat -37
+KPX rcommaaccent e -37
+KPX rcommaaccent eacute -37
+KPX rcommaaccent ecaron -37
+KPX rcommaaccent ecircumflex -37
+KPX rcommaaccent edieresis -37
+KPX rcommaaccent edotaccent -37
+KPX rcommaaccent egrave -37
+KPX rcommaaccent emacron -37
+KPX rcommaaccent eogonek -37
+KPX rcommaaccent g -37
+KPX rcommaaccent gbreve -37
+KPX rcommaaccent gcommaaccent -37
+KPX rcommaaccent hyphen -20
+KPX rcommaaccent o -45
+KPX rcommaaccent oacute -45
+KPX rcommaaccent ocircumflex -45
+KPX rcommaaccent odieresis -45
+KPX rcommaaccent ograve -45
+KPX rcommaaccent ohungarumlaut -45
+KPX rcommaaccent omacron -45
+KPX rcommaaccent oslash -45
+KPX rcommaaccent otilde -45
+KPX rcommaaccent period -111
+KPX rcommaaccent q -37
+KPX rcommaaccent s -10
+KPX rcommaaccent sacute -10
+KPX rcommaaccent scaron -10
+KPX rcommaaccent scedilla -10
+KPX rcommaaccent scommaaccent -10
+KPX space A -18
+KPX space Aacute -18
+KPX space Abreve -18
+KPX space Acircumflex -18
+KPX space Adieresis -18
+KPX space Agrave -18
+KPX space Amacron -18
+KPX space Aogonek -18
+KPX space Aring -18
+KPX space Atilde -18
+KPX space T -18
+KPX space Tcaron -18
+KPX space Tcommaaccent -18
+KPX space V -35
+KPX space W -40
+KPX space Y -75
+KPX space Yacute -75
+KPX space Ydieresis -75
+KPX v comma -74
+KPX v period -74
+KPX w comma -74
+KPX w period -74
+KPX y comma -55
+KPX y period -55
+KPX yacute comma -55
+KPX yacute period -55
+KPX ydieresis comma -55
+KPX ydieresis period -55
+EndKernPairs
+EndKernData
+EndFontMetrics
diff --git a/program/libraries/afm/Times-Roman.afm b/program/libraries/afm/Times-Roman.afm
--- /dev/null
@@ -0,0 +1,2419 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.
+Comment Creation Date: Thu May 1 12:49:17 1997
+Comment UniqueID 43068
+Comment VMusage 43909 54934
+FontName Times-Roman
+FullName Times Roman
+FamilyName Times
+Weight Roman
+ItalicAngle 0
+IsFixedPitch false
+CharacterSet ExtendedRoman
+FontBBox -168 -218 1000 898
+UnderlinePosition -100
+UnderlineThickness 50
+Version 002.000
+Notice Copyright (c) 1985, 1987, 1989, 1990, 1993, 1997 Adobe Systems Incorporated. All Rights Reserved.Times is a trademark of Linotype-Hell AG and/or its subsidiaries.
+EncodingScheme AdobeStandardEncoding
+CapHeight 662
+XHeight 450
+Ascender 683
+Descender -217
+StdHW 28
+StdVW 84
+StartCharMetrics 315
+C 32 ; WX 250 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 333 ; N exclam ; B 130 -9 238 676 ;
+C 34 ; WX 408 ; N quotedbl ; B 77 431 331 676 ;
+C 35 ; WX 500 ; N numbersign ; B 5 0 496 662 ;
+C 36 ; WX 500 ; N dollar ; B 44 -87 457 727 ;
+C 37 ; WX 833 ; N percent ; B 61 -13 772 676 ;
+C 38 ; WX 778 ; N ampersand ; B 42 -13 750 676 ;
+C 39 ; WX 333 ; N quoteright ; B 79 433 218 676 ;
+C 40 ; WX 333 ; N parenleft ; B 48 -177 304 676 ;
+C 41 ; WX 333 ; N parenright ; B 29 -177 285 676 ;
+C 42 ; WX 500 ; N asterisk ; B 69 265 432 676 ;
+C 43 ; WX 564 ; N plus ; B 30 0 534 506 ;
+C 44 ; WX 250 ; N comma ; B 56 -141 195 102 ;
+C 45 ; WX 333 ; N hyphen ; B 39 194 285 257 ;
+C 46 ; WX 250 ; N period ; B 70 -11 181 100 ;
+C 47 ; WX 278 ; N slash ; B -9 -14 287 676 ;
+C 48 ; WX 500 ; N zero ; B 24 -14 476 676 ;
+C 49 ; WX 500 ; N one ; B 111 0 394 676 ;
+C 50 ; WX 500 ; N two ; B 30 0 475 676 ;
+C 51 ; WX 500 ; N three ; B 43 -14 431 676 ;
+C 52 ; WX 500 ; N four ; B 12 0 472 676 ;
+C 53 ; WX 500 ; N five ; B 32 -14 438 688 ;
+C 54 ; WX 500 ; N six ; B 34 -14 468 684 ;
+C 55 ; WX 500 ; N seven ; B 20 -8 449 662 ;
+C 56 ; WX 500 ; N eight ; B 56 -14 445 676 ;
+C 57 ; WX 500 ; N nine ; B 30 -22 459 676 ;
+C 58 ; WX 278 ; N colon ; B 81 -11 192 459 ;
+C 59 ; WX 278 ; N semicolon ; B 80 -141 219 459 ;
+C 60 ; WX 564 ; N less ; B 28 -8 536 514 ;
+C 61 ; WX 564 ; N equal ; B 30 120 534 386 ;
+C 62 ; WX 564 ; N greater ; B 28 -8 536 514 ;
+C 63 ; WX 444 ; N question ; B 68 -8 414 676 ;
+C 64 ; WX 921 ; N at ; B 116 -14 809 676 ;
+C 65 ; WX 722 ; N A ; B 15 0 706 674 ;
+C 66 ; WX 667 ; N B ; B 17 0 593 662 ;
+C 67 ; WX 667 ; N C ; B 28 -14 633 676 ;
+C 68 ; WX 722 ; N D ; B 16 0 685 662 ;
+C 69 ; WX 611 ; N E ; B 12 0 597 662 ;
+C 70 ; WX 556 ; N F ; B 12 0 546 662 ;
+C 71 ; WX 722 ; N G ; B 32 -14 709 676 ;
+C 72 ; WX 722 ; N H ; B 19 0 702 662 ;
+C 73 ; WX 333 ; N I ; B 18 0 315 662 ;
+C 74 ; WX 389 ; N J ; B 10 -14 370 662 ;
+C 75 ; WX 722 ; N K ; B 34 0 723 662 ;
+C 76 ; WX 611 ; N L ; B 12 0 598 662 ;
+C 77 ; WX 889 ; N M ; B 12 0 863 662 ;
+C 78 ; WX 722 ; N N ; B 12 -11 707 662 ;
+C 79 ; WX 722 ; N O ; B 34 -14 688 676 ;
+C 80 ; WX 556 ; N P ; B 16 0 542 662 ;
+C 81 ; WX 722 ; N Q ; B 34 -178 701 676 ;
+C 82 ; WX 667 ; N R ; B 17 0 659 662 ;
+C 83 ; WX 556 ; N S ; B 42 -14 491 676 ;
+C 84 ; WX 611 ; N T ; B 17 0 593 662 ;
+C 85 ; WX 722 ; N U ; B 14 -14 705 662 ;
+C 86 ; WX 722 ; N V ; B 16 -11 697 662 ;
+C 87 ; WX 944 ; N W ; B 5 -11 932 662 ;
+C 88 ; WX 722 ; N X ; B 10 0 704 662 ;
+C 89 ; WX 722 ; N Y ; B 22 0 703 662 ;
+C 90 ; WX 611 ; N Z ; B 9 0 597 662 ;
+C 91 ; WX 333 ; N bracketleft ; B 88 -156 299 662 ;
+C 92 ; WX 278 ; N backslash ; B -9 -14 287 676 ;
+C 93 ; WX 333 ; N bracketright ; B 34 -156 245 662 ;
+C 94 ; WX 469 ; N asciicircum ; B 24 297 446 662 ;
+C 95 ; WX 500 ; N underscore ; B 0 -125 500 -75 ;
+C 96 ; WX 333 ; N quoteleft ; B 115 433 254 676 ;
+C 97 ; WX 444 ; N a ; B 37 -10 442 460 ;
+C 98 ; WX 500 ; N b ; B 3 -10 468 683 ;
+C 99 ; WX 444 ; N c ; B 25 -10 412 460 ;
+C 100 ; WX 500 ; N d ; B 27 -10 491 683 ;
+C 101 ; WX 444 ; N e ; B 25 -10 424 460 ;
+C 102 ; WX 333 ; N f ; B 20 0 383 683 ; L i fi ; L l fl ;
+C 103 ; WX 500 ; N g ; B 28 -218 470 460 ;
+C 104 ; WX 500 ; N h ; B 9 0 487 683 ;
+C 105 ; WX 278 ; N i ; B 16 0 253 683 ;
+C 106 ; WX 278 ; N j ; B -70 -218 194 683 ;
+C 107 ; WX 500 ; N k ; B 7 0 505 683 ;
+C 108 ; WX 278 ; N l ; B 19 0 257 683 ;
+C 109 ; WX 778 ; N m ; B 16 0 775 460 ;
+C 110 ; WX 500 ; N n ; B 16 0 485 460 ;
+C 111 ; WX 500 ; N o ; B 29 -10 470 460 ;
+C 112 ; WX 500 ; N p ; B 5 -217 470 460 ;
+C 113 ; WX 500 ; N q ; B 24 -217 488 460 ;
+C 114 ; WX 333 ; N r ; B 5 0 335 460 ;
+C 115 ; WX 389 ; N s ; B 51 -10 348 460 ;
+C 116 ; WX 278 ; N t ; B 13 -10 279 579 ;
+C 117 ; WX 500 ; N u ; B 9 -10 479 450 ;
+C 118 ; WX 500 ; N v ; B 19 -14 477 450 ;
+C 119 ; WX 722 ; N w ; B 21 -14 694 450 ;
+C 120 ; WX 500 ; N x ; B 17 0 479 450 ;
+C 121 ; WX 500 ; N y ; B 14 -218 475 450 ;
+C 122 ; WX 444 ; N z ; B 27 0 418 450 ;
+C 123 ; WX 480 ; N braceleft ; B 100 -181 350 680 ;
+C 124 ; WX 200 ; N bar ; B 67 -218 133 782 ;
+C 125 ; WX 480 ; N braceright ; B 130 -181 380 680 ;
+C 126 ; WX 541 ; N asciitilde ; B 40 183 502 323 ;
+C 161 ; WX 333 ; N exclamdown ; B 97 -218 205 467 ;
+C 162 ; WX 500 ; N cent ; B 53 -138 448 579 ;
+C 163 ; WX 500 ; N sterling ; B 12 -8 490 676 ;
+C 164 ; WX 167 ; N fraction ; B -168 -14 331 676 ;
+C 165 ; WX 500 ; N yen ; B -53 0 512 662 ;
+C 166 ; WX 500 ; N florin ; B 7 -189 490 676 ;
+C 167 ; WX 500 ; N section ; B 70 -148 426 676 ;
+C 168 ; WX 500 ; N currency ; B -22 58 522 602 ;
+C 169 ; WX 180 ; N quotesingle ; B 48 431 133 676 ;
+C 170 ; WX 444 ; N quotedblleft ; B 43 433 414 676 ;
+C 171 ; WX 500 ; N guillemotleft ; B 42 33 456 416 ;
+C 172 ; WX 333 ; N guilsinglleft ; B 63 33 285 416 ;
+C 173 ; WX 333 ; N guilsinglright ; B 48 33 270 416 ;
+C 174 ; WX 556 ; N fi ; B 31 0 521 683 ;
+C 175 ; WX 556 ; N fl ; B 32 0 521 683 ;
+C 177 ; WX 500 ; N endash ; B 0 201 500 250 ;
+C 178 ; WX 500 ; N dagger ; B 59 -149 442 676 ;
+C 179 ; WX 500 ; N daggerdbl ; B 58 -153 442 676 ;
+C 180 ; WX 250 ; N periodcentered ; B 70 199 181 310 ;
+C 182 ; WX 453 ; N paragraph ; B -22 -154 450 662 ;
+C 183 ; WX 350 ; N bullet ; B 40 196 310 466 ;
+C 184 ; WX 333 ; N quotesinglbase ; B 79 -141 218 102 ;
+C 185 ; WX 444 ; N quotedblbase ; B 45 -141 416 102 ;
+C 186 ; WX 444 ; N quotedblright ; B 30 433 401 676 ;
+C 187 ; WX 500 ; N guillemotright ; B 44 33 458 416 ;
+C 188 ; WX 1000 ; N ellipsis ; B 111 -11 888 100 ;
+C 189 ; WX 1000 ; N perthousand ; B 7 -19 994 706 ;
+C 191 ; WX 444 ; N questiondown ; B 30 -218 376 466 ;
+C 193 ; WX 333 ; N grave ; B 19 507 242 678 ;
+C 194 ; WX 333 ; N acute ; B 93 507 317 678 ;
+C 195 ; WX 333 ; N circumflex ; B 11 507 322 674 ;
+C 196 ; WX 333 ; N tilde ; B 1 532 331 638 ;
+C 197 ; WX 333 ; N macron ; B 11 547 322 601 ;
+C 198 ; WX 333 ; N breve ; B 26 507 307 664 ;
+C 199 ; WX 333 ; N dotaccent ; B 118 581 216 681 ;
+C 200 ; WX 333 ; N dieresis ; B 18 581 315 681 ;
+C 202 ; WX 333 ; N ring ; B 67 512 266 711 ;
+C 203 ; WX 333 ; N cedilla ; B 52 -215 261 0 ;
+C 205 ; WX 333 ; N hungarumlaut ; B -3 507 377 678 ;
+C 206 ; WX 333 ; N ogonek ; B 62 -165 243 0 ;
+C 207 ; WX 333 ; N caron ; B 11 507 322 674 ;
+C 208 ; WX 1000 ; N emdash ; B 0 201 1000 250 ;
+C 225 ; WX 889 ; N AE ; B 0 0 863 662 ;
+C 227 ; WX 276 ; N ordfeminine ; B 4 394 270 676 ;
+C 232 ; WX 611 ; N Lslash ; B 12 0 598 662 ;
+C 233 ; WX 722 ; N Oslash ; B 34 -80 688 734 ;
+C 234 ; WX 889 ; N OE ; B 30 -6 885 668 ;
+C 235 ; WX 310 ; N ordmasculine ; B 6 394 304 676 ;
+C 241 ; WX 667 ; N ae ; B 38 -10 632 460 ;
+C 245 ; WX 278 ; N dotlessi ; B 16 0 253 460 ;
+C 248 ; WX 278 ; N lslash ; B 19 0 259 683 ;
+C 249 ; WX 500 ; N oslash ; B 29 -112 470 551 ;
+C 250 ; WX 722 ; N oe ; B 30 -10 690 460 ;
+C 251 ; WX 500 ; N germandbls ; B 12 -9 468 683 ;
+C -1 ; WX 333 ; N Idieresis ; B 18 0 315 835 ;
+C -1 ; WX 444 ; N eacute ; B 25 -10 424 678 ;
+C -1 ; WX 444 ; N abreve ; B 37 -10 442 664 ;
+C -1 ; WX 500 ; N uhungarumlaut ; B 9 -10 501 678 ;
+C -1 ; WX 444 ; N ecaron ; B 25 -10 424 674 ;
+C -1 ; WX 722 ; N Ydieresis ; B 22 0 703 835 ;
+C -1 ; WX 564 ; N divide ; B 30 -10 534 516 ;
+C -1 ; WX 722 ; N Yacute ; B 22 0 703 890 ;
+C -1 ; WX 722 ; N Acircumflex ; B 15 0 706 886 ;
+C -1 ; WX 444 ; N aacute ; B 37 -10 442 678 ;
+C -1 ; WX 722 ; N Ucircumflex ; B 14 -14 705 886 ;
+C -1 ; WX 500 ; N yacute ; B 14 -218 475 678 ;
+C -1 ; WX 389 ; N scommaaccent ; B 51 -218 348 460 ;
+C -1 ; WX 444 ; N ecircumflex ; B 25 -10 424 674 ;
+C -1 ; WX 722 ; N Uring ; B 14 -14 705 898 ;
+C -1 ; WX 722 ; N Udieresis ; B 14 -14 705 835 ;
+C -1 ; WX 444 ; N aogonek ; B 37 -165 469 460 ;
+C -1 ; WX 722 ; N Uacute ; B 14 -14 705 890 ;
+C -1 ; WX 500 ; N uogonek ; B 9 -155 487 450 ;
+C -1 ; WX 611 ; N Edieresis ; B 12 0 597 835 ;
+C -1 ; WX 722 ; N Dcroat ; B 16 0 685 662 ;
+C -1 ; WX 250 ; N commaaccent ; B 59 -218 184 -50 ;
+C -1 ; WX 760 ; N copyright ; B 38 -14 722 676 ;
+C -1 ; WX 611 ; N Emacron ; B 12 0 597 813 ;
+C -1 ; WX 444 ; N ccaron ; B 25 -10 412 674 ;
+C -1 ; WX 444 ; N aring ; B 37 -10 442 711 ;
+C -1 ; WX 722 ; N Ncommaaccent ; B 12 -198 707 662 ;
+C -1 ; WX 278 ; N lacute ; B 19 0 290 890 ;
+C -1 ; WX 444 ; N agrave ; B 37 -10 442 678 ;
+C -1 ; WX 611 ; N Tcommaaccent ; B 17 -218 593 662 ;
+C -1 ; WX 667 ; N Cacute ; B 28 -14 633 890 ;
+C -1 ; WX 444 ; N atilde ; B 37 -10 442 638 ;
+C -1 ; WX 611 ; N Edotaccent ; B 12 0 597 835 ;
+C -1 ; WX 389 ; N scaron ; B 39 -10 350 674 ;
+C -1 ; WX 389 ; N scedilla ; B 51 -215 348 460 ;
+C -1 ; WX 278 ; N iacute ; B 16 0 290 678 ;
+C -1 ; WX 471 ; N lozenge ; B 13 0 459 724 ;
+C -1 ; WX 667 ; N Rcaron ; B 17 0 659 886 ;
+C -1 ; WX 722 ; N Gcommaaccent ; B 32 -218 709 676 ;
+C -1 ; WX 500 ; N ucircumflex ; B 9 -10 479 674 ;
+C -1 ; WX 444 ; N acircumflex ; B 37 -10 442 674 ;
+C -1 ; WX 722 ; N Amacron ; B 15 0 706 813 ;
+C -1 ; WX 333 ; N rcaron ; B 5 0 335 674 ;
+C -1 ; WX 444 ; N ccedilla ; B 25 -215 412 460 ;
+C -1 ; WX 611 ; N Zdotaccent ; B 9 0 597 835 ;
+C -1 ; WX 556 ; N Thorn ; B 16 0 542 662 ;
+C -1 ; WX 722 ; N Omacron ; B 34 -14 688 813 ;
+C -1 ; WX 667 ; N Racute ; B 17 0 659 890 ;
+C -1 ; WX 556 ; N Sacute ; B 42 -14 491 890 ;
+C -1 ; WX 588 ; N dcaron ; B 27 -10 589 695 ;
+C -1 ; WX 722 ; N Umacron ; B 14 -14 705 813 ;
+C -1 ; WX 500 ; N uring ; B 9 -10 479 711 ;
+C -1 ; WX 300 ; N threesuperior ; B 15 262 291 676 ;
+C -1 ; WX 722 ; N Ograve ; B 34 -14 688 890 ;
+C -1 ; WX 722 ; N Agrave ; B 15 0 706 890 ;
+C -1 ; WX 722 ; N Abreve ; B 15 0 706 876 ;
+C -1 ; WX 564 ; N multiply ; B 38 8 527 497 ;
+C -1 ; WX 500 ; N uacute ; B 9 -10 479 678 ;
+C -1 ; WX 611 ; N Tcaron ; B 17 0 593 886 ;
+C -1 ; WX 476 ; N partialdiff ; B 17 -38 459 710 ;
+C -1 ; WX 500 ; N ydieresis ; B 14 -218 475 623 ;
+C -1 ; WX 722 ; N Nacute ; B 12 -11 707 890 ;
+C -1 ; WX 278 ; N icircumflex ; B -16 0 295 674 ;
+C -1 ; WX 611 ; N Ecircumflex ; B 12 0 597 886 ;
+C -1 ; WX 444 ; N adieresis ; B 37 -10 442 623 ;
+C -1 ; WX 444 ; N edieresis ; B 25 -10 424 623 ;
+C -1 ; WX 444 ; N cacute ; B 25 -10 413 678 ;
+C -1 ; WX 500 ; N nacute ; B 16 0 485 678 ;
+C -1 ; WX 500 ; N umacron ; B 9 -10 479 601 ;
+C -1 ; WX 722 ; N Ncaron ; B 12 -11 707 886 ;
+C -1 ; WX 333 ; N Iacute ; B 18 0 317 890 ;
+C -1 ; WX 564 ; N plusminus ; B 30 0 534 506 ;
+C -1 ; WX 200 ; N brokenbar ; B 67 -143 133 707 ;
+C -1 ; WX 760 ; N registered ; B 38 -14 722 676 ;
+C -1 ; WX 722 ; N Gbreve ; B 32 -14 709 876 ;
+C -1 ; WX 333 ; N Idotaccent ; B 18 0 315 835 ;
+C -1 ; WX 600 ; N summation ; B 15 -10 585 706 ;
+C -1 ; WX 611 ; N Egrave ; B 12 0 597 890 ;
+C -1 ; WX 333 ; N racute ; B 5 0 335 678 ;
+C -1 ; WX 500 ; N omacron ; B 29 -10 470 601 ;
+C -1 ; WX 611 ; N Zacute ; B 9 0 597 890 ;
+C -1 ; WX 611 ; N Zcaron ; B 9 0 597 886 ;
+C -1 ; WX 549 ; N greaterequal ; B 26 0 523 666 ;
+C -1 ; WX 722 ; N Eth ; B 16 0 685 662 ;
+C -1 ; WX 667 ; N Ccedilla ; B 28 -215 633 676 ;
+C -1 ; WX 278 ; N lcommaaccent ; B 19 -218 257 683 ;
+C -1 ; WX 326 ; N tcaron ; B 13 -10 318 722 ;
+C -1 ; WX 444 ; N eogonek ; B 25 -165 424 460 ;
+C -1 ; WX 722 ; N Uogonek ; B 14 -165 705 662 ;
+C -1 ; WX 722 ; N Aacute ; B 15 0 706 890 ;
+C -1 ; WX 722 ; N Adieresis ; B 15 0 706 835 ;
+C -1 ; WX 444 ; N egrave ; B 25 -10 424 678 ;
+C -1 ; WX 444 ; N zacute ; B 27 0 418 678 ;
+C -1 ; WX 278 ; N iogonek ; B 16 -165 265 683 ;
+C -1 ; WX 722 ; N Oacute ; B 34 -14 688 890 ;
+C -1 ; WX 500 ; N oacute ; B 29 -10 470 678 ;
+C -1 ; WX 444 ; N amacron ; B 37 -10 442 601 ;
+C -1 ; WX 389 ; N sacute ; B 51 -10 348 678 ;
+C -1 ; WX 278 ; N idieresis ; B -9 0 288 623 ;
+C -1 ; WX 722 ; N Ocircumflex ; B 34 -14 688 886 ;
+C -1 ; WX 722 ; N Ugrave ; B 14 -14 705 890 ;
+C -1 ; WX 612 ; N Delta ; B 6 0 608 688 ;
+C -1 ; WX 500 ; N thorn ; B 5 -217 470 683 ;
+C -1 ; WX 300 ; N twosuperior ; B 1 270 296 676 ;
+C -1 ; WX 722 ; N Odieresis ; B 34 -14 688 835 ;
+C -1 ; WX 500 ; N mu ; B 36 -218 512 450 ;
+C -1 ; WX 278 ; N igrave ; B -8 0 253 678 ;
+C -1 ; WX 500 ; N ohungarumlaut ; B 29 -10 491 678 ;
+C -1 ; WX 611 ; N Eogonek ; B 12 -165 597 662 ;
+C -1 ; WX 500 ; N dcroat ; B 27 -10 500 683 ;
+C -1 ; WX 750 ; N threequarters ; B 15 -14 718 676 ;
+C -1 ; WX 556 ; N Scedilla ; B 42 -215 491 676 ;
+C -1 ; WX 344 ; N lcaron ; B 19 0 347 695 ;
+C -1 ; WX 722 ; N Kcommaaccent ; B 34 -198 723 662 ;
+C -1 ; WX 611 ; N Lacute ; B 12 0 598 890 ;
+C -1 ; WX 980 ; N trademark ; B 30 256 957 662 ;
+C -1 ; WX 444 ; N edotaccent ; B 25 -10 424 623 ;
+C -1 ; WX 333 ; N Igrave ; B 18 0 315 890 ;
+C -1 ; WX 333 ; N Imacron ; B 11 0 322 813 ;
+C -1 ; WX 611 ; N Lcaron ; B 12 0 598 676 ;
+C -1 ; WX 750 ; N onehalf ; B 31 -14 746 676 ;
+C -1 ; WX 549 ; N lessequal ; B 26 0 523 666 ;
+C -1 ; WX 500 ; N ocircumflex ; B 29 -10 470 674 ;
+C -1 ; WX 500 ; N ntilde ; B 16 0 485 638 ;
+C -1 ; WX 722 ; N Uhungarumlaut ; B 14 -14 705 890 ;
+C -1 ; WX 611 ; N Eacute ; B 12 0 597 890 ;
+C -1 ; WX 444 ; N emacron ; B 25 -10 424 601 ;
+C -1 ; WX 500 ; N gbreve ; B 28 -218 470 664 ;
+C -1 ; WX 750 ; N onequarter ; B 37 -14 718 676 ;
+C -1 ; WX 556 ; N Scaron ; B 42 -14 491 886 ;
+C -1 ; WX 556 ; N Scommaaccent ; B 42 -218 491 676 ;
+C -1 ; WX 722 ; N Ohungarumlaut ; B 34 -14 688 890 ;
+C -1 ; WX 400 ; N degree ; B 57 390 343 676 ;
+C -1 ; WX 500 ; N ograve ; B 29 -10 470 678 ;
+C -1 ; WX 667 ; N Ccaron ; B 28 -14 633 886 ;
+C -1 ; WX 500 ; N ugrave ; B 9 -10 479 678 ;
+C -1 ; WX 453 ; N radical ; B 2 -60 452 768 ;
+C -1 ; WX 722 ; N Dcaron ; B 16 0 685 886 ;
+C -1 ; WX 333 ; N rcommaaccent ; B 5 -218 335 460 ;
+C -1 ; WX 722 ; N Ntilde ; B 12 -11 707 850 ;
+C -1 ; WX 500 ; N otilde ; B 29 -10 470 638 ;
+C -1 ; WX 667 ; N Rcommaaccent ; B 17 -198 659 662 ;
+C -1 ; WX 611 ; N Lcommaaccent ; B 12 -218 598 662 ;
+C -1 ; WX 722 ; N Atilde ; B 15 0 706 850 ;
+C -1 ; WX 722 ; N Aogonek ; B 15 -165 738 674 ;
+C -1 ; WX 722 ; N Aring ; B 15 0 706 898 ;
+C -1 ; WX 722 ; N Otilde ; B 34 -14 688 850 ;
+C -1 ; WX 444 ; N zdotaccent ; B 27 0 418 623 ;
+C -1 ; WX 611 ; N Ecaron ; B 12 0 597 886 ;
+C -1 ; WX 333 ; N Iogonek ; B 18 -165 315 662 ;
+C -1 ; WX 500 ; N kcommaaccent ; B 7 -218 505 683 ;
+C -1 ; WX 564 ; N minus ; B 30 220 534 286 ;
+C -1 ; WX 333 ; N Icircumflex ; B 11 0 322 886 ;
+C -1 ; WX 500 ; N ncaron ; B 16 0 485 674 ;
+C -1 ; WX 278 ; N tcommaaccent ; B 13 -218 279 579 ;
+C -1 ; WX 564 ; N logicalnot ; B 30 108 534 386 ;
+C -1 ; WX 500 ; N odieresis ; B 29 -10 470 623 ;
+C -1 ; WX 500 ; N udieresis ; B 9 -10 479 623 ;
+C -1 ; WX 549 ; N notequal ; B 12 -31 537 547 ;
+C -1 ; WX 500 ; N gcommaaccent ; B 28 -218 470 749 ;
+C -1 ; WX 500 ; N eth ; B 29 -10 471 686 ;
+C -1 ; WX 444 ; N zcaron ; B 27 0 418 674 ;
+C -1 ; WX 500 ; N ncommaaccent ; B 16 -218 485 460 ;
+C -1 ; WX 300 ; N onesuperior ; B 57 270 248 676 ;
+C -1 ; WX 278 ; N imacron ; B 6 0 271 601 ;
+C -1 ; WX 500 ; N Euro ; B 0 0 0 0 ;
+EndCharMetrics
+StartKernData
+StartKernPairs 2073
+KPX A C -40
+KPX A Cacute -40
+KPX A Ccaron -40
+KPX A Ccedilla -40
+KPX A G -40
+KPX A Gbreve -40
+KPX A Gcommaaccent -40
+KPX A O -55
+KPX A Oacute -55
+KPX A Ocircumflex -55
+KPX A Odieresis -55
+KPX A Ograve -55
+KPX A Ohungarumlaut -55
+KPX A Omacron -55
+KPX A Oslash -55
+KPX A Otilde -55
+KPX A Q -55
+KPX A T -111
+KPX A Tcaron -111
+KPX A Tcommaaccent -111
+KPX A U -55
+KPX A Uacute -55
+KPX A Ucircumflex -55
+KPX A Udieresis -55
+KPX A Ugrave -55
+KPX A Uhungarumlaut -55
+KPX A Umacron -55
+KPX A Uogonek -55
+KPX A Uring -55
+KPX A V -135
+KPX A W -90
+KPX A Y -105
+KPX A Yacute -105
+KPX A Ydieresis -105
+KPX A quoteright -111
+KPX A v -74
+KPX A w -92
+KPX A y -92
+KPX A yacute -92
+KPX A ydieresis -92
+KPX Aacute C -40
+KPX Aacute Cacute -40
+KPX Aacute Ccaron -40
+KPX Aacute Ccedilla -40
+KPX Aacute G -40
+KPX Aacute Gbreve -40
+KPX Aacute Gcommaaccent -40
+KPX Aacute O -55
+KPX Aacute Oacute -55
+KPX Aacute Ocircumflex -55
+KPX Aacute Odieresis -55
+KPX Aacute Ograve -55
+KPX Aacute Ohungarumlaut -55
+KPX Aacute Omacron -55
+KPX Aacute Oslash -55
+KPX Aacute Otilde -55
+KPX Aacute Q -55
+KPX Aacute T -111
+KPX Aacute Tcaron -111
+KPX Aacute Tcommaaccent -111
+KPX Aacute U -55
+KPX Aacute Uacute -55
+KPX Aacute Ucircumflex -55
+KPX Aacute Udieresis -55
+KPX Aacute Ugrave -55
+KPX Aacute Uhungarumlaut -55
+KPX Aacute Umacron -55
+KPX Aacute Uogonek -55
+KPX Aacute Uring -55
+KPX Aacute V -135
+KPX Aacute W -90
+KPX Aacute Y -105
+KPX Aacute Yacute -105
+KPX Aacute Ydieresis -105
+KPX Aacute quoteright -111
+KPX Aacute v -74
+KPX Aacute w -92
+KPX Aacute y -92
+KPX Aacute yacute -92
+KPX Aacute ydieresis -92
+KPX Abreve C -40
+KPX Abreve Cacute -40
+KPX Abreve Ccaron -40
+KPX Abreve Ccedilla -40
+KPX Abreve G -40
+KPX Abreve Gbreve -40
+KPX Abreve Gcommaaccent -40
+KPX Abreve O -55
+KPX Abreve Oacute -55
+KPX Abreve Ocircumflex -55
+KPX Abreve Odieresis -55
+KPX Abreve Ograve -55
+KPX Abreve Ohungarumlaut -55
+KPX Abreve Omacron -55
+KPX Abreve Oslash -55
+KPX Abreve Otilde -55
+KPX Abreve Q -55
+KPX Abreve T -111
+KPX Abreve Tcaron -111
+KPX Abreve Tcommaaccent -111
+KPX Abreve U -55
+KPX Abreve Uacute -55
+KPX Abreve Ucircumflex -55
+KPX Abreve Udieresis -55
+KPX Abreve Ugrave -55
+KPX Abreve Uhungarumlaut -55
+KPX Abreve Umacron -55
+KPX Abreve Uogonek -55
+KPX Abreve Uring -55
+KPX Abreve V -135
+KPX Abreve W -90
+KPX Abreve Y -105
+KPX Abreve Yacute -105
+KPX Abreve Ydieresis -105
+KPX Abreve quoteright -111
+KPX Abreve v -74
+KPX Abreve w -92
+KPX Abreve y -92
+KPX Abreve yacute -92
+KPX Abreve ydieresis -92
+KPX Acircumflex C -40
+KPX Acircumflex Cacute -40
+KPX Acircumflex Ccaron -40
+KPX Acircumflex Ccedilla -40
+KPX Acircumflex G -40
+KPX Acircumflex Gbreve -40
+KPX Acircumflex Gcommaaccent -40
+KPX Acircumflex O -55
+KPX Acircumflex Oacute -55
+KPX Acircumflex Ocircumflex -55
+KPX Acircumflex Odieresis -55
+KPX Acircumflex Ograve -55
+KPX Acircumflex Ohungarumlaut -55
+KPX Acircumflex Omacron -55
+KPX Acircumflex Oslash -55
+KPX Acircumflex Otilde -55
+KPX Acircumflex Q -55
+KPX Acircumflex T -111
+KPX Acircumflex Tcaron -111
+KPX Acircumflex Tcommaaccent -111
+KPX Acircumflex U -55
+KPX Acircumflex Uacute -55
+KPX Acircumflex Ucircumflex -55
+KPX Acircumflex Udieresis -55
+KPX Acircumflex Ugrave -55
+KPX Acircumflex Uhungarumlaut -55
+KPX Acircumflex Umacron -55
+KPX Acircumflex Uogonek -55
+KPX Acircumflex Uring -55
+KPX Acircumflex V -135
+KPX Acircumflex W -90
+KPX Acircumflex Y -105
+KPX Acircumflex Yacute -105
+KPX Acircumflex Ydieresis -105
+KPX Acircumflex quoteright -111
+KPX Acircumflex v -74
+KPX Acircumflex w -92
+KPX Acircumflex y -92
+KPX Acircumflex yacute -92
+KPX Acircumflex ydieresis -92
+KPX Adieresis C -40
+KPX Adieresis Cacute -40
+KPX Adieresis Ccaron -40
+KPX Adieresis Ccedilla -40
+KPX Adieresis G -40
+KPX Adieresis Gbreve -40
+KPX Adieresis Gcommaaccent -40
+KPX Adieresis O -55
+KPX Adieresis Oacute -55
+KPX Adieresis Ocircumflex -55
+KPX Adieresis Odieresis -55
+KPX Adieresis Ograve -55
+KPX Adieresis Ohungarumlaut -55
+KPX Adieresis Omacron -55
+KPX Adieresis Oslash -55
+KPX Adieresis Otilde -55
+KPX Adieresis Q -55
+KPX Adieresis T -111
+KPX Adieresis Tcaron -111
+KPX Adieresis Tcommaaccent -111
+KPX Adieresis U -55
+KPX Adieresis Uacute -55
+KPX Adieresis Ucircumflex -55
+KPX Adieresis Udieresis -55
+KPX Adieresis Ugrave -55
+KPX Adieresis Uhungarumlaut -55
+KPX Adieresis Umacron -55
+KPX Adieresis Uogonek -55
+KPX Adieresis Uring -55
+KPX Adieresis V -135
+KPX Adieresis W -90
+KPX Adieresis Y -105
+KPX Adieresis Yacute -105
+KPX Adieresis Ydieresis -105
+KPX Adieresis quoteright -111
+KPX Adieresis v -74
+KPX Adieresis w -92
+KPX Adieresis y -92
+KPX Adieresis yacute -92
+KPX Adieresis ydieresis -92
+KPX Agrave C -40
+KPX Agrave Cacute -40
+KPX Agrave Ccaron -40
+KPX Agrave Ccedilla -40
+KPX Agrave G -40
+KPX Agrave Gbreve -40
+KPX Agrave Gcommaaccent -40
+KPX Agrave O -55
+KPX Agrave Oacute -55
+KPX Agrave Ocircumflex -55
+KPX Agrave Odieresis -55
+KPX Agrave Ograve -55
+KPX Agrave Ohungarumlaut -55
+KPX Agrave Omacron -55
+KPX Agrave Oslash -55
+KPX Agrave Otilde -55
+KPX Agrave Q -55
+KPX Agrave T -111
+KPX Agrave Tcaron -111
+KPX Agrave Tcommaaccent -111
+KPX Agrave U -55
+KPX Agrave Uacute -55
+KPX Agrave Ucircumflex -55
+KPX Agrave Udieresis -55
+KPX Agrave Ugrave -55
+KPX Agrave Uhungarumlaut -55
+KPX Agrave Umacron -55
+KPX Agrave Uogonek -55
+KPX Agrave Uring -55
+KPX Agrave V -135
+KPX Agrave W -90
+KPX Agrave Y -105
+KPX Agrave Yacute -105
+KPX Agrave Ydieresis -105
+KPX Agrave quoteright -111
+KPX Agrave v -74
+KPX Agrave w -92
+KPX Agrave y -92
+KPX Agrave yacute -92
+KPX Agrave ydieresis -92
+KPX Amacron C -40
+KPX Amacron Cacute -40
+KPX Amacron Ccaron -40
+KPX Amacron Ccedilla -40
+KPX Amacron G -40
+KPX Amacron Gbreve -40
+KPX Amacron Gcommaaccent -40
+KPX Amacron O -55
+KPX Amacron Oacute -55
+KPX Amacron Ocircumflex -55
+KPX Amacron Odieresis -55
+KPX Amacron Ograve -55
+KPX Amacron Ohungarumlaut -55
+KPX Amacron Omacron -55
+KPX Amacron Oslash -55
+KPX Amacron Otilde -55
+KPX Amacron Q -55
+KPX Amacron T -111
+KPX Amacron Tcaron -111
+KPX Amacron Tcommaaccent -111
+KPX Amacron U -55
+KPX Amacron Uacute -55
+KPX Amacron Ucircumflex -55
+KPX Amacron Udieresis -55
+KPX Amacron Ugrave -55
+KPX Amacron Uhungarumlaut -55
+KPX Amacron Umacron -55
+KPX Amacron Uogonek -55
+KPX Amacron Uring -55
+KPX Amacron V -135
+KPX Amacron W -90
+KPX Amacron Y -105
+KPX Amacron Yacute -105
+KPX Amacron Ydieresis -105
+KPX Amacron quoteright -111
+KPX Amacron v -74
+KPX Amacron w -92
+KPX Amacron y -92
+KPX Amacron yacute -92
+KPX Amacron ydieresis -92
+KPX Aogonek C -40
+KPX Aogonek Cacute -40
+KPX Aogonek Ccaron -40
+KPX Aogonek Ccedilla -40
+KPX Aogonek G -40
+KPX Aogonek Gbreve -40
+KPX Aogonek Gcommaaccent -40
+KPX Aogonek O -55
+KPX Aogonek Oacute -55
+KPX Aogonek Ocircumflex -55
+KPX Aogonek Odieresis -55
+KPX Aogonek Ograve -55
+KPX Aogonek Ohungarumlaut -55
+KPX Aogonek Omacron -55
+KPX Aogonek Oslash -55
+KPX Aogonek Otilde -55
+KPX Aogonek Q -55
+KPX Aogonek T -111
+KPX Aogonek Tcaron -111
+KPX Aogonek Tcommaaccent -111
+KPX Aogonek U -55
+KPX Aogonek Uacute -55
+KPX Aogonek Ucircumflex -55
+KPX Aogonek Udieresis -55
+KPX Aogonek Ugrave -55
+KPX Aogonek Uhungarumlaut -55
+KPX Aogonek Umacron -55
+KPX Aogonek Uogonek -55
+KPX Aogonek Uring -55
+KPX Aogonek V -135
+KPX Aogonek W -90
+KPX Aogonek Y -105
+KPX Aogonek Yacute -105
+KPX Aogonek Ydieresis -105
+KPX Aogonek quoteright -111
+KPX Aogonek v -74
+KPX Aogonek w -52
+KPX Aogonek y -52
+KPX Aogonek yacute -52
+KPX Aogonek ydieresis -52
+KPX Aring C -40
+KPX Aring Cacute -40
+KPX Aring Ccaron -40
+KPX Aring Ccedilla -40
+KPX Aring G -40
+KPX Aring Gbreve -40
+KPX Aring Gcommaaccent -40
+KPX Aring O -55
+KPX Aring Oacute -55
+KPX Aring Ocircumflex -55
+KPX Aring Odieresis -55
+KPX Aring Ograve -55
+KPX Aring Ohungarumlaut -55
+KPX Aring Omacron -55
+KPX Aring Oslash -55
+KPX Aring Otilde -55
+KPX Aring Q -55
+KPX Aring T -111
+KPX Aring Tcaron -111
+KPX Aring Tcommaaccent -111
+KPX Aring U -55
+KPX Aring Uacute -55
+KPX Aring Ucircumflex -55
+KPX Aring Udieresis -55
+KPX Aring Ugrave -55
+KPX Aring Uhungarumlaut -55
+KPX Aring Umacron -55
+KPX Aring Uogonek -55
+KPX Aring Uring -55
+KPX Aring V -135
+KPX Aring W -90
+KPX Aring Y -105
+KPX Aring Yacute -105
+KPX Aring Ydieresis -105
+KPX Aring quoteright -111
+KPX Aring v -74
+KPX Aring w -92
+KPX Aring y -92
+KPX Aring yacute -92
+KPX Aring ydieresis -92
+KPX Atilde C -40
+KPX Atilde Cacute -40
+KPX Atilde Ccaron -40
+KPX Atilde Ccedilla -40
+KPX Atilde G -40
+KPX Atilde Gbreve -40
+KPX Atilde Gcommaaccent -40
+KPX Atilde O -55
+KPX Atilde Oacute -55
+KPX Atilde Ocircumflex -55
+KPX Atilde Odieresis -55
+KPX Atilde Ograve -55
+KPX Atilde Ohungarumlaut -55
+KPX Atilde Omacron -55
+KPX Atilde Oslash -55
+KPX Atilde Otilde -55
+KPX Atilde Q -55
+KPX Atilde T -111
+KPX Atilde Tcaron -111
+KPX Atilde Tcommaaccent -111
+KPX Atilde U -55
+KPX Atilde Uacute -55
+KPX Atilde Ucircumflex -55
+KPX Atilde Udieresis -55
+KPX Atilde Ugrave -55
+KPX Atilde Uhungarumlaut -55
+KPX Atilde Umacron -55
+KPX Atilde Uogonek -55
+KPX Atilde Uring -55
+KPX Atilde V -135
+KPX Atilde W -90
+KPX Atilde Y -105
+KPX Atilde Yacute -105
+KPX Atilde Ydieresis -105
+KPX Atilde quoteright -111
+KPX Atilde v -74
+KPX Atilde w -92
+KPX Atilde y -92
+KPX Atilde yacute -92
+KPX Atilde ydieresis -92
+KPX B A -35
+KPX B Aacute -35
+KPX B Abreve -35
+KPX B Acircumflex -35
+KPX B Adieresis -35
+KPX B Agrave -35
+KPX B Amacron -35
+KPX B Aogonek -35
+KPX B Aring -35
+KPX B Atilde -35
+KPX B U -10
+KPX B Uacute -10
+KPX B Ucircumflex -10
+KPX B Udieresis -10
+KPX B Ugrave -10
+KPX B Uhungarumlaut -10
+KPX B Umacron -10
+KPX B Uogonek -10
+KPX B Uring -10
+KPX D A -40
+KPX D Aacute -40
+KPX D Abreve -40
+KPX D Acircumflex -40
+KPX D Adieresis -40
+KPX D Agrave -40
+KPX D Amacron -40
+KPX D Aogonek -40
+KPX D Aring -40
+KPX D Atilde -40
+KPX D V -40
+KPX D W -30
+KPX D Y -55
+KPX D Yacute -55
+KPX D Ydieresis -55
+KPX Dcaron A -40
+KPX Dcaron Aacute -40
+KPX Dcaron Abreve -40
+KPX Dcaron Acircumflex -40
+KPX Dcaron Adieresis -40
+KPX Dcaron Agrave -40
+KPX Dcaron Amacron -40
+KPX Dcaron Aogonek -40
+KPX Dcaron Aring -40
+KPX Dcaron Atilde -40
+KPX Dcaron V -40
+KPX Dcaron W -30
+KPX Dcaron Y -55
+KPX Dcaron Yacute -55
+KPX Dcaron Ydieresis -55
+KPX Dcroat A -40
+KPX Dcroat Aacute -40
+KPX Dcroat Abreve -40
+KPX Dcroat Acircumflex -40
+KPX Dcroat Adieresis -40
+KPX Dcroat Agrave -40
+KPX Dcroat Amacron -40
+KPX Dcroat Aogonek -40
+KPX Dcroat Aring -40
+KPX Dcroat Atilde -40
+KPX Dcroat V -40
+KPX Dcroat W -30
+KPX Dcroat Y -55
+KPX Dcroat Yacute -55
+KPX Dcroat Ydieresis -55
+KPX F A -74
+KPX F Aacute -74
+KPX F Abreve -74
+KPX F Acircumflex -74
+KPX F Adieresis -74
+KPX F Agrave -74
+KPX F Amacron -74
+KPX F Aogonek -74
+KPX F Aring -74
+KPX F Atilde -74
+KPX F a -15
+KPX F aacute -15
+KPX F abreve -15
+KPX F acircumflex -15
+KPX F adieresis -15
+KPX F agrave -15
+KPX F amacron -15
+KPX F aogonek -15
+KPX F aring -15
+KPX F atilde -15
+KPX F comma -80
+KPX F o -15
+KPX F oacute -15
+KPX F ocircumflex -15
+KPX F odieresis -15
+KPX F ograve -15
+KPX F ohungarumlaut -15
+KPX F omacron -15
+KPX F oslash -15
+KPX F otilde -15
+KPX F period -80
+KPX J A -60
+KPX J Aacute -60
+KPX J Abreve -60
+KPX J Acircumflex -60
+KPX J Adieresis -60
+KPX J Agrave -60
+KPX J Amacron -60
+KPX J Aogonek -60
+KPX J Aring -60
+KPX J Atilde -60
+KPX K O -30
+KPX K Oacute -30
+KPX K Ocircumflex -30
+KPX K Odieresis -30
+KPX K Ograve -30
+KPX K Ohungarumlaut -30
+KPX K Omacron -30
+KPX K Oslash -30
+KPX K Otilde -30
+KPX K e -25
+KPX K eacute -25
+KPX K ecaron -25
+KPX K ecircumflex -25
+KPX K edieresis -25
+KPX K edotaccent -25
+KPX K egrave -25
+KPX K emacron -25
+KPX K eogonek -25
+KPX K o -35
+KPX K oacute -35
+KPX K ocircumflex -35
+KPX K odieresis -35
+KPX K ograve -35
+KPX K ohungarumlaut -35
+KPX K omacron -35
+KPX K oslash -35
+KPX K otilde -35
+KPX K u -15
+KPX K uacute -15
+KPX K ucircumflex -15
+KPX K udieresis -15
+KPX K ugrave -15
+KPX K uhungarumlaut -15
+KPX K umacron -15
+KPX K uogonek -15
+KPX K uring -15
+KPX K y -25
+KPX K yacute -25
+KPX K ydieresis -25
+KPX Kcommaaccent O -30
+KPX Kcommaaccent Oacute -30
+KPX Kcommaaccent Ocircumflex -30
+KPX Kcommaaccent Odieresis -30
+KPX Kcommaaccent Ograve -30
+KPX Kcommaaccent Ohungarumlaut -30
+KPX Kcommaaccent Omacron -30
+KPX Kcommaaccent Oslash -30
+KPX Kcommaaccent Otilde -30
+KPX Kcommaaccent e -25
+KPX Kcommaaccent eacute -25
+KPX Kcommaaccent ecaron -25
+KPX Kcommaaccent ecircumflex -25
+KPX Kcommaaccent edieresis -25
+KPX Kcommaaccent edotaccent -25
+KPX Kcommaaccent egrave -25
+KPX Kcommaaccent emacron -25
+KPX Kcommaaccent eogonek -25
+KPX Kcommaaccent o -35
+KPX Kcommaaccent oacute -35
+KPX Kcommaaccent ocircumflex -35
+KPX Kcommaaccent odieresis -35
+KPX Kcommaaccent ograve -35
+KPX Kcommaaccent ohungarumlaut -35
+KPX Kcommaaccent omacron -35
+KPX Kcommaaccent oslash -35
+KPX Kcommaaccent otilde -35
+KPX Kcommaaccent u -15
+KPX Kcommaaccent uacute -15
+KPX Kcommaaccent ucircumflex -15
+KPX Kcommaaccent udieresis -15
+KPX Kcommaaccent ugrave -15
+KPX Kcommaaccent uhungarumlaut -15
+KPX Kcommaaccent umacron -15
+KPX Kcommaaccent uogonek -15
+KPX Kcommaaccent uring -15
+KPX Kcommaaccent y -25
+KPX Kcommaaccent yacute -25
+KPX Kcommaaccent ydieresis -25
+KPX L T -92
+KPX L Tcaron -92
+KPX L Tcommaaccent -92
+KPX L V -100
+KPX L W -74
+KPX L Y -100
+KPX L Yacute -100
+KPX L Ydieresis -100
+KPX L quoteright -92
+KPX L y -55
+KPX L yacute -55
+KPX L ydieresis -55
+KPX Lacute T -92
+KPX Lacute Tcaron -92
+KPX Lacute Tcommaaccent -92
+KPX Lacute V -100
+KPX Lacute W -74
+KPX Lacute Y -100
+KPX Lacute Yacute -100
+KPX Lacute Ydieresis -100
+KPX Lacute quoteright -92
+KPX Lacute y -55
+KPX Lacute yacute -55
+KPX Lacute ydieresis -55
+KPX Lcaron quoteright -92
+KPX Lcaron y -55
+KPX Lcaron yacute -55
+KPX Lcaron ydieresis -55
+KPX Lcommaaccent T -92
+KPX Lcommaaccent Tcaron -92
+KPX Lcommaaccent Tcommaaccent -92
+KPX Lcommaaccent V -100
+KPX Lcommaaccent W -74
+KPX Lcommaaccent Y -100
+KPX Lcommaaccent Yacute -100
+KPX Lcommaaccent Ydieresis -100
+KPX Lcommaaccent quoteright -92
+KPX Lcommaaccent y -55
+KPX Lcommaaccent yacute -55
+KPX Lcommaaccent ydieresis -55
+KPX Lslash T -92
+KPX Lslash Tcaron -92
+KPX Lslash Tcommaaccent -92
+KPX Lslash V -100
+KPX Lslash W -74
+KPX Lslash Y -100
+KPX Lslash Yacute -100
+KPX Lslash Ydieresis -100
+KPX Lslash quoteright -92
+KPX Lslash y -55
+KPX Lslash yacute -55
+KPX Lslash ydieresis -55
+KPX N A -35
+KPX N Aacute -35
+KPX N Abreve -35
+KPX N Acircumflex -35
+KPX N Adieresis -35
+KPX N Agrave -35
+KPX N Amacron -35
+KPX N Aogonek -35
+KPX N Aring -35
+KPX N Atilde -35
+KPX Nacute A -35
+KPX Nacute Aacute -35
+KPX Nacute Abreve -35
+KPX Nacute Acircumflex -35
+KPX Nacute Adieresis -35
+KPX Nacute Agrave -35
+KPX Nacute Amacron -35
+KPX Nacute Aogonek -35
+KPX Nacute Aring -35
+KPX Nacute Atilde -35
+KPX Ncaron A -35
+KPX Ncaron Aacute -35
+KPX Ncaron Abreve -35
+KPX Ncaron Acircumflex -35
+KPX Ncaron Adieresis -35
+KPX Ncaron Agrave -35
+KPX Ncaron Amacron -35
+KPX Ncaron Aogonek -35
+KPX Ncaron Aring -35
+KPX Ncaron Atilde -35
+KPX Ncommaaccent A -35
+KPX Ncommaaccent Aacute -35
+KPX Ncommaaccent Abreve -35
+KPX Ncommaaccent Acircumflex -35
+KPX Ncommaaccent Adieresis -35
+KPX Ncommaaccent Agrave -35
+KPX Ncommaaccent Amacron -35
+KPX Ncommaaccent Aogonek -35
+KPX Ncommaaccent Aring -35
+KPX Ncommaaccent Atilde -35
+KPX Ntilde A -35
+KPX Ntilde Aacute -35
+KPX Ntilde Abreve -35
+KPX Ntilde Acircumflex -35
+KPX Ntilde Adieresis -35
+KPX Ntilde Agrave -35
+KPX Ntilde Amacron -35
+KPX Ntilde Aogonek -35
+KPX Ntilde Aring -35
+KPX Ntilde Atilde -35
+KPX O A -35
+KPX O Aacute -35
+KPX O Abreve -35
+KPX O Acircumflex -35
+KPX O Adieresis -35
+KPX O Agrave -35
+KPX O Amacron -35
+KPX O Aogonek -35
+KPX O Aring -35
+KPX O Atilde -35
+KPX O T -40
+KPX O Tcaron -40
+KPX O Tcommaaccent -40
+KPX O V -50
+KPX O W -35
+KPX O X -40
+KPX O Y -50
+KPX O Yacute -50
+KPX O Ydieresis -50
+KPX Oacute A -35
+KPX Oacute Aacute -35
+KPX Oacute Abreve -35
+KPX Oacute Acircumflex -35
+KPX Oacute Adieresis -35
+KPX Oacute Agrave -35
+KPX Oacute Amacron -35
+KPX Oacute Aogonek -35
+KPX Oacute Aring -35
+KPX Oacute Atilde -35
+KPX Oacute T -40
+KPX Oacute Tcaron -40
+KPX Oacute Tcommaaccent -40
+KPX Oacute V -50
+KPX Oacute W -35
+KPX Oacute X -40
+KPX Oacute Y -50
+KPX Oacute Yacute -50
+KPX Oacute Ydieresis -50
+KPX Ocircumflex A -35
+KPX Ocircumflex Aacute -35
+KPX Ocircumflex Abreve -35
+KPX Ocircumflex Acircumflex -35
+KPX Ocircumflex Adieresis -35
+KPX Ocircumflex Agrave -35
+KPX Ocircumflex Amacron -35
+KPX Ocircumflex Aogonek -35
+KPX Ocircumflex Aring -35
+KPX Ocircumflex Atilde -35
+KPX Ocircumflex T -40
+KPX Ocircumflex Tcaron -40
+KPX Ocircumflex Tcommaaccent -40
+KPX Ocircumflex V -50
+KPX Ocircumflex W -35
+KPX Ocircumflex X -40
+KPX Ocircumflex Y -50
+KPX Ocircumflex Yacute -50
+KPX Ocircumflex Ydieresis -50
+KPX Odieresis A -35
+KPX Odieresis Aacute -35
+KPX Odieresis Abreve -35
+KPX Odieresis Acircumflex -35
+KPX Odieresis Adieresis -35
+KPX Odieresis Agrave -35
+KPX Odieresis Amacron -35
+KPX Odieresis Aogonek -35
+KPX Odieresis Aring -35
+KPX Odieresis Atilde -35
+KPX Odieresis T -40
+KPX Odieresis Tcaron -40
+KPX Odieresis Tcommaaccent -40
+KPX Odieresis V -50
+KPX Odieresis W -35
+KPX Odieresis X -40
+KPX Odieresis Y -50
+KPX Odieresis Yacute -50
+KPX Odieresis Ydieresis -50
+KPX Ograve A -35
+KPX Ograve Aacute -35
+KPX Ograve Abreve -35
+KPX Ograve Acircumflex -35
+KPX Ograve Adieresis -35
+KPX Ograve Agrave -35
+KPX Ograve Amacron -35
+KPX Ograve Aogonek -35
+KPX Ograve Aring -35
+KPX Ograve Atilde -35
+KPX Ograve T -40
+KPX Ograve Tcaron -40
+KPX Ograve Tcommaaccent -40
+KPX Ograve V -50
+KPX Ograve W -35
+KPX Ograve X -40
+KPX Ograve Y -50
+KPX Ograve Yacute -50
+KPX Ograve Ydieresis -50
+KPX Ohungarumlaut A -35
+KPX Ohungarumlaut Aacute -35
+KPX Ohungarumlaut Abreve -35
+KPX Ohungarumlaut Acircumflex -35
+KPX Ohungarumlaut Adieresis -35
+KPX Ohungarumlaut Agrave -35
+KPX Ohungarumlaut Amacron -35
+KPX Ohungarumlaut Aogonek -35
+KPX Ohungarumlaut Aring -35
+KPX Ohungarumlaut Atilde -35
+KPX Ohungarumlaut T -40
+KPX Ohungarumlaut Tcaron -40
+KPX Ohungarumlaut Tcommaaccent -40
+KPX Ohungarumlaut V -50
+KPX Ohungarumlaut W -35
+KPX Ohungarumlaut X -40
+KPX Ohungarumlaut Y -50
+KPX Ohungarumlaut Yacute -50
+KPX Ohungarumlaut Ydieresis -50
+KPX Omacron A -35
+KPX Omacron Aacute -35
+KPX Omacron Abreve -35
+KPX Omacron Acircumflex -35
+KPX Omacron Adieresis -35
+KPX Omacron Agrave -35
+KPX Omacron Amacron -35
+KPX Omacron Aogonek -35
+KPX Omacron Aring -35
+KPX Omacron Atilde -35
+KPX Omacron T -40
+KPX Omacron Tcaron -40
+KPX Omacron Tcommaaccent -40
+KPX Omacron V -50
+KPX Omacron W -35
+KPX Omacron X -40
+KPX Omacron Y -50
+KPX Omacron Yacute -50
+KPX Omacron Ydieresis -50
+KPX Oslash A -35
+KPX Oslash Aacute -35
+KPX Oslash Abreve -35
+KPX Oslash Acircumflex -35
+KPX Oslash Adieresis -35
+KPX Oslash Agrave -35
+KPX Oslash Amacron -35
+KPX Oslash Aogonek -35
+KPX Oslash Aring -35
+KPX Oslash Atilde -35
+KPX Oslash T -40
+KPX Oslash Tcaron -40
+KPX Oslash Tcommaaccent -40
+KPX Oslash V -50
+KPX Oslash W -35
+KPX Oslash X -40
+KPX Oslash Y -50
+KPX Oslash Yacute -50
+KPX Oslash Ydieresis -50
+KPX Otilde A -35
+KPX Otilde Aacute -35
+KPX Otilde Abreve -35
+KPX Otilde Acircumflex -35
+KPX Otilde Adieresis -35
+KPX Otilde Agrave -35
+KPX Otilde Amacron -35
+KPX Otilde Aogonek -35
+KPX Otilde Aring -35
+KPX Otilde Atilde -35
+KPX Otilde T -40
+KPX Otilde Tcaron -40
+KPX Otilde Tcommaaccent -40
+KPX Otilde V -50
+KPX Otilde W -35
+KPX Otilde X -40
+KPX Otilde Y -50
+KPX Otilde Yacute -50
+KPX Otilde Ydieresis -50
+KPX P A -92
+KPX P Aacute -92
+KPX P Abreve -92
+KPX P Acircumflex -92
+KPX P Adieresis -92
+KPX P Agrave -92
+KPX P Amacron -92
+KPX P Aogonek -92
+KPX P Aring -92
+KPX P Atilde -92
+KPX P a -15
+KPX P aacute -15
+KPX P abreve -15
+KPX P acircumflex -15
+KPX P adieresis -15
+KPX P agrave -15
+KPX P amacron -15
+KPX P aogonek -15
+KPX P aring -15
+KPX P atilde -15
+KPX P comma -111
+KPX P period -111
+KPX Q U -10
+KPX Q Uacute -10
+KPX Q Ucircumflex -10
+KPX Q Udieresis -10
+KPX Q Ugrave -10
+KPX Q Uhungarumlaut -10
+KPX Q Umacron -10
+KPX Q Uogonek -10
+KPX Q Uring -10
+KPX R O -40
+KPX R Oacute -40
+KPX R Ocircumflex -40
+KPX R Odieresis -40
+KPX R Ograve -40
+KPX R Ohungarumlaut -40
+KPX R Omacron -40
+KPX R Oslash -40
+KPX R Otilde -40
+KPX R T -60
+KPX R Tcaron -60
+KPX R Tcommaaccent -60
+KPX R U -40
+KPX R Uacute -40
+KPX R Ucircumflex -40
+KPX R Udieresis -40
+KPX R Ugrave -40
+KPX R Uhungarumlaut -40
+KPX R Umacron -40
+KPX R Uogonek -40
+KPX R Uring -40
+KPX R V -80
+KPX R W -55
+KPX R Y -65
+KPX R Yacute -65
+KPX R Ydieresis -65
+KPX Racute O -40
+KPX Racute Oacute -40
+KPX Racute Ocircumflex -40
+KPX Racute Odieresis -40
+KPX Racute Ograve -40
+KPX Racute Ohungarumlaut -40
+KPX Racute Omacron -40
+KPX Racute Oslash -40
+KPX Racute Otilde -40
+KPX Racute T -60
+KPX Racute Tcaron -60
+KPX Racute Tcommaaccent -60
+KPX Racute U -40
+KPX Racute Uacute -40
+KPX Racute Ucircumflex -40
+KPX Racute Udieresis -40
+KPX Racute Ugrave -40
+KPX Racute Uhungarumlaut -40
+KPX Racute Umacron -40
+KPX Racute Uogonek -40
+KPX Racute Uring -40
+KPX Racute V -80
+KPX Racute W -55
+KPX Racute Y -65
+KPX Racute Yacute -65
+KPX Racute Ydieresis -65
+KPX Rcaron O -40
+KPX Rcaron Oacute -40
+KPX Rcaron Ocircumflex -40
+KPX Rcaron Odieresis -40
+KPX Rcaron Ograve -40
+KPX Rcaron Ohungarumlaut -40
+KPX Rcaron Omacron -40
+KPX Rcaron Oslash -40
+KPX Rcaron Otilde -40
+KPX Rcaron T -60
+KPX Rcaron Tcaron -60
+KPX Rcaron Tcommaaccent -60
+KPX Rcaron U -40
+KPX Rcaron Uacute -40
+KPX Rcaron Ucircumflex -40
+KPX Rcaron Udieresis -40
+KPX Rcaron Ugrave -40
+KPX Rcaron Uhungarumlaut -40
+KPX Rcaron Umacron -40
+KPX Rcaron Uogonek -40
+KPX Rcaron Uring -40
+KPX Rcaron V -80
+KPX Rcaron W -55
+KPX Rcaron Y -65
+KPX Rcaron Yacute -65
+KPX Rcaron Ydieresis -65
+KPX Rcommaaccent O -40
+KPX Rcommaaccent Oacute -40
+KPX Rcommaaccent Ocircumflex -40
+KPX Rcommaaccent Odieresis -40
+KPX Rcommaaccent Ograve -40
+KPX Rcommaaccent Ohungarumlaut -40
+KPX Rcommaaccent Omacron -40
+KPX Rcommaaccent Oslash -40
+KPX Rcommaaccent Otilde -40
+KPX Rcommaaccent T -60
+KPX Rcommaaccent Tcaron -60
+KPX Rcommaaccent Tcommaaccent -60
+KPX Rcommaaccent U -40
+KPX Rcommaaccent Uacute -40
+KPX Rcommaaccent Ucircumflex -40
+KPX Rcommaaccent Udieresis -40
+KPX Rcommaaccent Ugrave -40
+KPX Rcommaaccent Uhungarumlaut -40
+KPX Rcommaaccent Umacron -40
+KPX Rcommaaccent Uogonek -40
+KPX Rcommaaccent Uring -40
+KPX Rcommaaccent V -80
+KPX Rcommaaccent W -55
+KPX Rcommaaccent Y -65
+KPX Rcommaaccent Yacute -65
+KPX Rcommaaccent Ydieresis -65
+KPX T A -93
+KPX T Aacute -93
+KPX T Abreve -93
+KPX T Acircumflex -93
+KPX T Adieresis -93
+KPX T Agrave -93
+KPX T Amacron -93
+KPX T Aogonek -93
+KPX T Aring -93
+KPX T Atilde -93
+KPX T O -18
+KPX T Oacute -18
+KPX T Ocircumflex -18
+KPX T Odieresis -18
+KPX T Ograve -18
+KPX T Ohungarumlaut -18
+KPX T Omacron -18
+KPX T Oslash -18
+KPX T Otilde -18
+KPX T a -80
+KPX T aacute -80
+KPX T abreve -80
+KPX T acircumflex -80
+KPX T adieresis -40
+KPX T agrave -40
+KPX T amacron -40
+KPX T aogonek -80
+KPX T aring -80
+KPX T atilde -40
+KPX T colon -50
+KPX T comma -74
+KPX T e -70
+KPX T eacute -70
+KPX T ecaron -70
+KPX T ecircumflex -70
+KPX T edieresis -30
+KPX T edotaccent -70
+KPX T egrave -70
+KPX T emacron -30
+KPX T eogonek -70
+KPX T hyphen -92
+KPX T i -35
+KPX T iacute -35
+KPX T iogonek -35
+KPX T o -80
+KPX T oacute -80
+KPX T ocircumflex -80
+KPX T odieresis -80
+KPX T ograve -80
+KPX T ohungarumlaut -80
+KPX T omacron -80
+KPX T oslash -80
+KPX T otilde -80
+KPX T period -74
+KPX T r -35
+KPX T racute -35
+KPX T rcaron -35
+KPX T rcommaaccent -35
+KPX T semicolon -55
+KPX T u -45
+KPX T uacute -45
+KPX T ucircumflex -45
+KPX T udieresis -45
+KPX T ugrave -45
+KPX T uhungarumlaut -45
+KPX T umacron -45
+KPX T uogonek -45
+KPX T uring -45
+KPX T w -80
+KPX T y -80
+KPX T yacute -80
+KPX T ydieresis -80
+KPX Tcaron A -93
+KPX Tcaron Aacute -93
+KPX Tcaron Abreve -93
+KPX Tcaron Acircumflex -93
+KPX Tcaron Adieresis -93
+KPX Tcaron Agrave -93
+KPX Tcaron Amacron -93
+KPX Tcaron Aogonek -93
+KPX Tcaron Aring -93
+KPX Tcaron Atilde -93
+KPX Tcaron O -18
+KPX Tcaron Oacute -18
+KPX Tcaron Ocircumflex -18
+KPX Tcaron Odieresis -18
+KPX Tcaron Ograve -18
+KPX Tcaron Ohungarumlaut -18
+KPX Tcaron Omacron -18
+KPX Tcaron Oslash -18
+KPX Tcaron Otilde -18
+KPX Tcaron a -80
+KPX Tcaron aacute -80
+KPX Tcaron abreve -80
+KPX Tcaron acircumflex -80
+KPX Tcaron adieresis -40
+KPX Tcaron agrave -40
+KPX Tcaron amacron -40
+KPX Tcaron aogonek -80
+KPX Tcaron aring -80
+KPX Tcaron atilde -40
+KPX Tcaron colon -50
+KPX Tcaron comma -74
+KPX Tcaron e -70
+KPX Tcaron eacute -70
+KPX Tcaron ecaron -70
+KPX Tcaron ecircumflex -30
+KPX Tcaron edieresis -30
+KPX Tcaron edotaccent -70
+KPX Tcaron egrave -70
+KPX Tcaron emacron -30
+KPX Tcaron eogonek -70
+KPX Tcaron hyphen -92
+KPX Tcaron i -35
+KPX Tcaron iacute -35
+KPX Tcaron iogonek -35
+KPX Tcaron o -80
+KPX Tcaron oacute -80
+KPX Tcaron ocircumflex -80
+KPX Tcaron odieresis -80
+KPX Tcaron ograve -80
+KPX Tcaron ohungarumlaut -80
+KPX Tcaron omacron -80
+KPX Tcaron oslash -80
+KPX Tcaron otilde -80
+KPX Tcaron period -74
+KPX Tcaron r -35
+KPX Tcaron racute -35
+KPX Tcaron rcaron -35
+KPX Tcaron rcommaaccent -35
+KPX Tcaron semicolon -55
+KPX Tcaron u -45
+KPX Tcaron uacute -45
+KPX Tcaron ucircumflex -45
+KPX Tcaron udieresis -45
+KPX Tcaron ugrave -45
+KPX Tcaron uhungarumlaut -45
+KPX Tcaron umacron -45
+KPX Tcaron uogonek -45
+KPX Tcaron uring -45
+KPX Tcaron w -80
+KPX Tcaron y -80
+KPX Tcaron yacute -80
+KPX Tcaron ydieresis -80
+KPX Tcommaaccent A -93
+KPX Tcommaaccent Aacute -93
+KPX Tcommaaccent Abreve -93
+KPX Tcommaaccent Acircumflex -93
+KPX Tcommaaccent Adieresis -93
+KPX Tcommaaccent Agrave -93
+KPX Tcommaaccent Amacron -93
+KPX Tcommaaccent Aogonek -93
+KPX Tcommaaccent Aring -93
+KPX Tcommaaccent Atilde -93
+KPX Tcommaaccent O -18
+KPX Tcommaaccent Oacute -18
+KPX Tcommaaccent Ocircumflex -18
+KPX Tcommaaccent Odieresis -18
+KPX Tcommaaccent Ograve -18
+KPX Tcommaaccent Ohungarumlaut -18
+KPX Tcommaaccent Omacron -18
+KPX Tcommaaccent Oslash -18
+KPX Tcommaaccent Otilde -18
+KPX Tcommaaccent a -80
+KPX Tcommaaccent aacute -80
+KPX Tcommaaccent abreve -80
+KPX Tcommaaccent acircumflex -80
+KPX Tcommaaccent adieresis -40
+KPX Tcommaaccent agrave -40
+KPX Tcommaaccent amacron -40
+KPX Tcommaaccent aogonek -80
+KPX Tcommaaccent aring -80
+KPX Tcommaaccent atilde -40
+KPX Tcommaaccent colon -50
+KPX Tcommaaccent comma -74
+KPX Tcommaaccent e -70
+KPX Tcommaaccent eacute -70
+KPX Tcommaaccent ecaron -70
+KPX Tcommaaccent ecircumflex -30
+KPX Tcommaaccent edieresis -30
+KPX Tcommaaccent edotaccent -70
+KPX Tcommaaccent egrave -30
+KPX Tcommaaccent emacron -70
+KPX Tcommaaccent eogonek -70
+KPX Tcommaaccent hyphen -92
+KPX Tcommaaccent i -35
+KPX Tcommaaccent iacute -35
+KPX Tcommaaccent iogonek -35
+KPX Tcommaaccent o -80
+KPX Tcommaaccent oacute -80
+KPX Tcommaaccent ocircumflex -80
+KPX Tcommaaccent odieresis -80
+KPX Tcommaaccent ograve -80
+KPX Tcommaaccent ohungarumlaut -80
+KPX Tcommaaccent omacron -80
+KPX Tcommaaccent oslash -80
+KPX Tcommaaccent otilde -80
+KPX Tcommaaccent period -74
+KPX Tcommaaccent r -35
+KPX Tcommaaccent racute -35
+KPX Tcommaaccent rcaron -35
+KPX Tcommaaccent rcommaaccent -35
+KPX Tcommaaccent semicolon -55
+KPX Tcommaaccent u -45
+KPX Tcommaaccent uacute -45
+KPX Tcommaaccent ucircumflex -45
+KPX Tcommaaccent udieresis -45
+KPX Tcommaaccent ugrave -45
+KPX Tcommaaccent uhungarumlaut -45
+KPX Tcommaaccent umacron -45
+KPX Tcommaaccent uogonek -45
+KPX Tcommaaccent uring -45
+KPX Tcommaaccent w -80
+KPX Tcommaaccent y -80
+KPX Tcommaaccent yacute -80
+KPX Tcommaaccent ydieresis -80
+KPX U A -40
+KPX U Aacute -40
+KPX U Abreve -40
+KPX U Acircumflex -40
+KPX U Adieresis -40
+KPX U Agrave -40
+KPX U Amacron -40
+KPX U Aogonek -40
+KPX U Aring -40
+KPX U Atilde -40
+KPX Uacute A -40
+KPX Uacute Aacute -40
+KPX Uacute Abreve -40
+KPX Uacute Acircumflex -40
+KPX Uacute Adieresis -40
+KPX Uacute Agrave -40
+KPX Uacute Amacron -40
+KPX Uacute Aogonek -40
+KPX Uacute Aring -40
+KPX Uacute Atilde -40
+KPX Ucircumflex A -40
+KPX Ucircumflex Aacute -40
+KPX Ucircumflex Abreve -40
+KPX Ucircumflex Acircumflex -40
+KPX Ucircumflex Adieresis -40
+KPX Ucircumflex Agrave -40
+KPX Ucircumflex Amacron -40
+KPX Ucircumflex Aogonek -40
+KPX Ucircumflex Aring -40
+KPX Ucircumflex Atilde -40
+KPX Udieresis A -40
+KPX Udieresis Aacute -40
+KPX Udieresis Abreve -40
+KPX Udieresis Acircumflex -40
+KPX Udieresis Adieresis -40
+KPX Udieresis Agrave -40
+KPX Udieresis Amacron -40
+KPX Udieresis Aogonek -40
+KPX Udieresis Aring -40
+KPX Udieresis Atilde -40
+KPX Ugrave A -40
+KPX Ugrave Aacute -40
+KPX Ugrave Abreve -40
+KPX Ugrave Acircumflex -40
+KPX Ugrave Adieresis -40
+KPX Ugrave Agrave -40
+KPX Ugrave Amacron -40
+KPX Ugrave Aogonek -40
+KPX Ugrave Aring -40
+KPX Ugrave Atilde -40
+KPX Uhungarumlaut A -40
+KPX Uhungarumlaut Aacute -40
+KPX Uhungarumlaut Abreve -40
+KPX Uhungarumlaut Acircumflex -40
+KPX Uhungarumlaut Adieresis -40
+KPX Uhungarumlaut Agrave -40
+KPX Uhungarumlaut Amacron -40
+KPX Uhungarumlaut Aogonek -40
+KPX Uhungarumlaut Aring -40
+KPX Uhungarumlaut Atilde -40
+KPX Umacron A -40
+KPX Umacron Aacute -40
+KPX Umacron Abreve -40
+KPX Umacron Acircumflex -40
+KPX Umacron Adieresis -40
+KPX Umacron Agrave -40
+KPX Umacron Amacron -40
+KPX Umacron Aogonek -40
+KPX Umacron Aring -40
+KPX Umacron Atilde -40
+KPX Uogonek A -40
+KPX Uogonek Aacute -40
+KPX Uogonek Abreve -40
+KPX Uogonek Acircumflex -40
+KPX Uogonek Adieresis -40
+KPX Uogonek Agrave -40
+KPX Uogonek Amacron -40
+KPX Uogonek Aogonek -40
+KPX Uogonek Aring -40
+KPX Uogonek Atilde -40
+KPX Uring A -40
+KPX Uring Aacute -40
+KPX Uring Abreve -40
+KPX Uring Acircumflex -40
+KPX Uring Adieresis -40
+KPX Uring Agrave -40
+KPX Uring Amacron -40
+KPX Uring Aogonek -40
+KPX Uring Aring -40
+KPX Uring Atilde -40
+KPX V A -135
+KPX V Aacute -135
+KPX V Abreve -135
+KPX V Acircumflex -135
+KPX V Adieresis -135
+KPX V Agrave -135
+KPX V Amacron -135
+KPX V Aogonek -135
+KPX V Aring -135
+KPX V Atilde -135
+KPX V G -15
+KPX V Gbreve -15
+KPX V Gcommaaccent -15
+KPX V O -40
+KPX V Oacute -40
+KPX V Ocircumflex -40
+KPX V Odieresis -40
+KPX V Ograve -40
+KPX V Ohungarumlaut -40
+KPX V Omacron -40
+KPX V Oslash -40
+KPX V Otilde -40
+KPX V a -111
+KPX V aacute -111
+KPX V abreve -111
+KPX V acircumflex -71
+KPX V adieresis -71
+KPX V agrave -71
+KPX V amacron -71
+KPX V aogonek -111
+KPX V aring -111
+KPX V atilde -71
+KPX V colon -74
+KPX V comma -129
+KPX V e -111
+KPX V eacute -111
+KPX V ecaron -71
+KPX V ecircumflex -71
+KPX V edieresis -71
+KPX V edotaccent -111
+KPX V egrave -71
+KPX V emacron -71
+KPX V eogonek -111
+KPX V hyphen -100
+KPX V i -60
+KPX V iacute -60
+KPX V icircumflex -20
+KPX V idieresis -20
+KPX V igrave -20
+KPX V imacron -20
+KPX V iogonek -60
+KPX V o -129
+KPX V oacute -129
+KPX V ocircumflex -129
+KPX V odieresis -89
+KPX V ograve -89
+KPX V ohungarumlaut -129
+KPX V omacron -89
+KPX V oslash -129
+KPX V otilde -89
+KPX V period -129
+KPX V semicolon -74
+KPX V u -75
+KPX V uacute -75
+KPX V ucircumflex -75
+KPX V udieresis -75
+KPX V ugrave -75
+KPX V uhungarumlaut -75
+KPX V umacron -75
+KPX V uogonek -75
+KPX V uring -75
+KPX W A -120
+KPX W Aacute -120
+KPX W Abreve -120
+KPX W Acircumflex -120
+KPX W Adieresis -120
+KPX W Agrave -120
+KPX W Amacron -120
+KPX W Aogonek -120
+KPX W Aring -120
+KPX W Atilde -120
+KPX W O -10
+KPX W Oacute -10
+KPX W Ocircumflex -10
+KPX W Odieresis -10
+KPX W Ograve -10
+KPX W Ohungarumlaut -10
+KPX W Omacron -10
+KPX W Oslash -10
+KPX W Otilde -10
+KPX W a -80
+KPX W aacute -80
+KPX W abreve -80
+KPX W acircumflex -80
+KPX W adieresis -80
+KPX W agrave -80
+KPX W amacron -80
+KPX W aogonek -80
+KPX W aring -80
+KPX W atilde -80
+KPX W colon -37
+KPX W comma -92
+KPX W e -80
+KPX W eacute -80
+KPX W ecaron -80
+KPX W ecircumflex -80
+KPX W edieresis -40
+KPX W edotaccent -80
+KPX W egrave -40
+KPX W emacron -40
+KPX W eogonek -80
+KPX W hyphen -65
+KPX W i -40
+KPX W iacute -40
+KPX W iogonek -40
+KPX W o -80
+KPX W oacute -80
+KPX W ocircumflex -80
+KPX W odieresis -80
+KPX W ograve -80
+KPX W ohungarumlaut -80
+KPX W omacron -80
+KPX W oslash -80
+KPX W otilde -80
+KPX W period -92
+KPX W semicolon -37
+KPX W u -50
+KPX W uacute -50
+KPX W ucircumflex -50
+KPX W udieresis -50
+KPX W ugrave -50
+KPX W uhungarumlaut -50
+KPX W umacron -50
+KPX W uogonek -50
+KPX W uring -50
+KPX W y -73
+KPX W yacute -73
+KPX W ydieresis -73
+KPX Y A -120
+KPX Y Aacute -120
+KPX Y Abreve -120
+KPX Y Acircumflex -120
+KPX Y Adieresis -120
+KPX Y Agrave -120
+KPX Y Amacron -120
+KPX Y Aogonek -120
+KPX Y Aring -120
+KPX Y Atilde -120
+KPX Y O -30
+KPX Y Oacute -30
+KPX Y Ocircumflex -30
+KPX Y Odieresis -30
+KPX Y Ograve -30
+KPX Y Ohungarumlaut -30
+KPX Y Omacron -30
+KPX Y Oslash -30
+KPX Y Otilde -30
+KPX Y a -100
+KPX Y aacute -100
+KPX Y abreve -100
+KPX Y acircumflex -100
+KPX Y adieresis -60
+KPX Y agrave -60
+KPX Y amacron -60
+KPX Y aogonek -100
+KPX Y aring -100
+KPX Y atilde -60
+KPX Y colon -92
+KPX Y comma -129
+KPX Y e -100
+KPX Y eacute -100
+KPX Y ecaron -100
+KPX Y ecircumflex -100
+KPX Y edieresis -60
+KPX Y edotaccent -100
+KPX Y egrave -60
+KPX Y emacron -60
+KPX Y eogonek -100
+KPX Y hyphen -111
+KPX Y i -55
+KPX Y iacute -55
+KPX Y iogonek -55
+KPX Y o -110
+KPX Y oacute -110
+KPX Y ocircumflex -110
+KPX Y odieresis -70
+KPX Y ograve -70
+KPX Y ohungarumlaut -110
+KPX Y omacron -70
+KPX Y oslash -110
+KPX Y otilde -70
+KPX Y period -129
+KPX Y semicolon -92
+KPX Y u -111
+KPX Y uacute -111
+KPX Y ucircumflex -111
+KPX Y udieresis -71
+KPX Y ugrave -71
+KPX Y uhungarumlaut -111
+KPX Y umacron -71
+KPX Y uogonek -111
+KPX Y uring -111
+KPX Yacute A -120
+KPX Yacute Aacute -120
+KPX Yacute Abreve -120
+KPX Yacute Acircumflex -120
+KPX Yacute Adieresis -120
+KPX Yacute Agrave -120
+KPX Yacute Amacron -120
+KPX Yacute Aogonek -120
+KPX Yacute Aring -120
+KPX Yacute Atilde -120
+KPX Yacute O -30
+KPX Yacute Oacute -30
+KPX Yacute Ocircumflex -30
+KPX Yacute Odieresis -30
+KPX Yacute Ograve -30
+KPX Yacute Ohungarumlaut -30
+KPX Yacute Omacron -30
+KPX Yacute Oslash -30
+KPX Yacute Otilde -30
+KPX Yacute a -100
+KPX Yacute aacute -100
+KPX Yacute abreve -100
+KPX Yacute acircumflex -100
+KPX Yacute adieresis -60
+KPX Yacute agrave -60
+KPX Yacute amacron -60
+KPX Yacute aogonek -100
+KPX Yacute aring -100
+KPX Yacute atilde -60
+KPX Yacute colon -92
+KPX Yacute comma -129
+KPX Yacute e -100
+KPX Yacute eacute -100
+KPX Yacute ecaron -100
+KPX Yacute ecircumflex -100
+KPX Yacute edieresis -60
+KPX Yacute edotaccent -100
+KPX Yacute egrave -60
+KPX Yacute emacron -60
+KPX Yacute eogonek -100
+KPX Yacute hyphen -111
+KPX Yacute i -55
+KPX Yacute iacute -55
+KPX Yacute iogonek -55
+KPX Yacute o -110
+KPX Yacute oacute -110
+KPX Yacute ocircumflex -110
+KPX Yacute odieresis -70
+KPX Yacute ograve -70
+KPX Yacute ohungarumlaut -110
+KPX Yacute omacron -70
+KPX Yacute oslash -110
+KPX Yacute otilde -70
+KPX Yacute period -129
+KPX Yacute semicolon -92
+KPX Yacute u -111
+KPX Yacute uacute -111
+KPX Yacute ucircumflex -111
+KPX Yacute udieresis -71
+KPX Yacute ugrave -71
+KPX Yacute uhungarumlaut -111
+KPX Yacute umacron -71
+KPX Yacute uogonek -111
+KPX Yacute uring -111
+KPX Ydieresis A -120
+KPX Ydieresis Aacute -120
+KPX Ydieresis Abreve -120
+KPX Ydieresis Acircumflex -120
+KPX Ydieresis Adieresis -120
+KPX Ydieresis Agrave -120
+KPX Ydieresis Amacron -120
+KPX Ydieresis Aogonek -120
+KPX Ydieresis Aring -120
+KPX Ydieresis Atilde -120
+KPX Ydieresis O -30
+KPX Ydieresis Oacute -30
+KPX Ydieresis Ocircumflex -30
+KPX Ydieresis Odieresis -30
+KPX Ydieresis Ograve -30
+KPX Ydieresis Ohungarumlaut -30
+KPX Ydieresis Omacron -30
+KPX Ydieresis Oslash -30
+KPX Ydieresis Otilde -30
+KPX Ydieresis a -100
+KPX Ydieresis aacute -100
+KPX Ydieresis abreve -100
+KPX Ydieresis acircumflex -100
+KPX Ydieresis adieresis -60
+KPX Ydieresis agrave -60
+KPX Ydieresis amacron -60
+KPX Ydieresis aogonek -100
+KPX Ydieresis aring -100
+KPX Ydieresis atilde -100
+KPX Ydieresis colon -92
+KPX Ydieresis comma -129
+KPX Ydieresis e -100
+KPX Ydieresis eacute -100
+KPX Ydieresis ecaron -100
+KPX Ydieresis ecircumflex -100
+KPX Ydieresis edieresis -60
+KPX Ydieresis edotaccent -100
+KPX Ydieresis egrave -60
+KPX Ydieresis emacron -60
+KPX Ydieresis eogonek -100
+KPX Ydieresis hyphen -111
+KPX Ydieresis i -55
+KPX Ydieresis iacute -55
+KPX Ydieresis iogonek -55
+KPX Ydieresis o -110
+KPX Ydieresis oacute -110
+KPX Ydieresis ocircumflex -110
+KPX Ydieresis odieresis -70
+KPX Ydieresis ograve -70
+KPX Ydieresis ohungarumlaut -110
+KPX Ydieresis omacron -70
+KPX Ydieresis oslash -110
+KPX Ydieresis otilde -70
+KPX Ydieresis period -129
+KPX Ydieresis semicolon -92
+KPX Ydieresis u -111
+KPX Ydieresis uacute -111
+KPX Ydieresis ucircumflex -111
+KPX Ydieresis udieresis -71
+KPX Ydieresis ugrave -71
+KPX Ydieresis uhungarumlaut -111
+KPX Ydieresis umacron -71
+KPX Ydieresis uogonek -111
+KPX Ydieresis uring -111
+KPX a v -20
+KPX a w -15
+KPX aacute v -20
+KPX aacute w -15
+KPX abreve v -20
+KPX abreve w -15
+KPX acircumflex v -20
+KPX acircumflex w -15
+KPX adieresis v -20
+KPX adieresis w -15
+KPX agrave v -20
+KPX agrave w -15
+KPX amacron v -20
+KPX amacron w -15
+KPX aogonek v -20
+KPX aogonek w -15
+KPX aring v -20
+KPX aring w -15
+KPX atilde v -20
+KPX atilde w -15
+KPX b period -40
+KPX b u -20
+KPX b uacute -20
+KPX b ucircumflex -20
+KPX b udieresis -20
+KPX b ugrave -20
+KPX b uhungarumlaut -20
+KPX b umacron -20
+KPX b uogonek -20
+KPX b uring -20
+KPX b v -15
+KPX c y -15
+KPX c yacute -15
+KPX c ydieresis -15
+KPX cacute y -15
+KPX cacute yacute -15
+KPX cacute ydieresis -15
+KPX ccaron y -15
+KPX ccaron yacute -15
+KPX ccaron ydieresis -15
+KPX ccedilla y -15
+KPX ccedilla yacute -15
+KPX ccedilla ydieresis -15
+KPX comma quotedblright -70
+KPX comma quoteright -70
+KPX e g -15
+KPX e gbreve -15
+KPX e gcommaaccent -15
+KPX e v -25
+KPX e w -25
+KPX e x -15
+KPX e y -15
+KPX e yacute -15
+KPX e ydieresis -15
+KPX eacute g -15
+KPX eacute gbreve -15
+KPX eacute gcommaaccent -15
+KPX eacute v -25
+KPX eacute w -25
+KPX eacute x -15
+KPX eacute y -15
+KPX eacute yacute -15
+KPX eacute ydieresis -15
+KPX ecaron g -15
+KPX ecaron gbreve -15
+KPX ecaron gcommaaccent -15
+KPX ecaron v -25
+KPX ecaron w -25
+KPX ecaron x -15
+KPX ecaron y -15
+KPX ecaron yacute -15
+KPX ecaron ydieresis -15
+KPX ecircumflex g -15
+KPX ecircumflex gbreve -15
+KPX ecircumflex gcommaaccent -15
+KPX ecircumflex v -25
+KPX ecircumflex w -25
+KPX ecircumflex x -15
+KPX ecircumflex y -15
+KPX ecircumflex yacute -15
+KPX ecircumflex ydieresis -15
+KPX edieresis g -15
+KPX edieresis gbreve -15
+KPX edieresis gcommaaccent -15
+KPX edieresis v -25
+KPX edieresis w -25
+KPX edieresis x -15
+KPX edieresis y -15
+KPX edieresis yacute -15
+KPX edieresis ydieresis -15
+KPX edotaccent g -15
+KPX edotaccent gbreve -15
+KPX edotaccent gcommaaccent -15
+KPX edotaccent v -25
+KPX edotaccent w -25
+KPX edotaccent x -15
+KPX edotaccent y -15
+KPX edotaccent yacute -15
+KPX edotaccent ydieresis -15
+KPX egrave g -15
+KPX egrave gbreve -15
+KPX egrave gcommaaccent -15
+KPX egrave v -25
+KPX egrave w -25
+KPX egrave x -15
+KPX egrave y -15
+KPX egrave yacute -15
+KPX egrave ydieresis -15
+KPX emacron g -15
+KPX emacron gbreve -15
+KPX emacron gcommaaccent -15
+KPX emacron v -25
+KPX emacron w -25
+KPX emacron x -15
+KPX emacron y -15
+KPX emacron yacute -15
+KPX emacron ydieresis -15
+KPX eogonek g -15
+KPX eogonek gbreve -15
+KPX eogonek gcommaaccent -15
+KPX eogonek v -25
+KPX eogonek w -25
+KPX eogonek x -15
+KPX eogonek y -15
+KPX eogonek yacute -15
+KPX eogonek ydieresis -15
+KPX f a -10
+KPX f aacute -10
+KPX f abreve -10
+KPX f acircumflex -10
+KPX f adieresis -10
+KPX f agrave -10
+KPX f amacron -10
+KPX f aogonek -10
+KPX f aring -10
+KPX f atilde -10
+KPX f dotlessi -50
+KPX f f -25
+KPX f i -20
+KPX f iacute -20
+KPX f quoteright 55
+KPX g a -5
+KPX g aacute -5
+KPX g abreve -5
+KPX g acircumflex -5
+KPX g adieresis -5
+KPX g agrave -5
+KPX g amacron -5
+KPX g aogonek -5
+KPX g aring -5
+KPX g atilde -5
+KPX gbreve a -5
+KPX gbreve aacute -5
+KPX gbreve abreve -5
+KPX gbreve acircumflex -5
+KPX gbreve adieresis -5
+KPX gbreve agrave -5
+KPX gbreve amacron -5
+KPX gbreve aogonek -5
+KPX gbreve aring -5
+KPX gbreve atilde -5
+KPX gcommaaccent a -5
+KPX gcommaaccent aacute -5
+KPX gcommaaccent abreve -5
+KPX gcommaaccent acircumflex -5
+KPX gcommaaccent adieresis -5
+KPX gcommaaccent agrave -5
+KPX gcommaaccent amacron -5
+KPX gcommaaccent aogonek -5
+KPX gcommaaccent aring -5
+KPX gcommaaccent atilde -5
+KPX h y -5
+KPX h yacute -5
+KPX h ydieresis -5
+KPX i v -25
+KPX iacute v -25
+KPX icircumflex v -25
+KPX idieresis v -25
+KPX igrave v -25
+KPX imacron v -25
+KPX iogonek v -25
+KPX k e -10
+KPX k eacute -10
+KPX k ecaron -10
+KPX k ecircumflex -10
+KPX k edieresis -10
+KPX k edotaccent -10
+KPX k egrave -10
+KPX k emacron -10
+KPX k eogonek -10
+KPX k o -10
+KPX k oacute -10
+KPX k ocircumflex -10
+KPX k odieresis -10
+KPX k ograve -10
+KPX k ohungarumlaut -10
+KPX k omacron -10
+KPX k oslash -10
+KPX k otilde -10
+KPX k y -15
+KPX k yacute -15
+KPX k ydieresis -15
+KPX kcommaaccent e -10
+KPX kcommaaccent eacute -10
+KPX kcommaaccent ecaron -10
+KPX kcommaaccent ecircumflex -10
+KPX kcommaaccent edieresis -10
+KPX kcommaaccent edotaccent -10
+KPX kcommaaccent egrave -10
+KPX kcommaaccent emacron -10
+KPX kcommaaccent eogonek -10
+KPX kcommaaccent o -10
+KPX kcommaaccent oacute -10
+KPX kcommaaccent ocircumflex -10
+KPX kcommaaccent odieresis -10
+KPX kcommaaccent ograve -10
+KPX kcommaaccent ohungarumlaut -10
+KPX kcommaaccent omacron -10
+KPX kcommaaccent oslash -10
+KPX kcommaaccent otilde -10
+KPX kcommaaccent y -15
+KPX kcommaaccent yacute -15
+KPX kcommaaccent ydieresis -15
+KPX l w -10
+KPX lacute w -10
+KPX lcommaaccent w -10
+KPX lslash w -10
+KPX n v -40
+KPX n y -15
+KPX n yacute -15
+KPX n ydieresis -15
+KPX nacute v -40
+KPX nacute y -15
+KPX nacute yacute -15
+KPX nacute ydieresis -15
+KPX ncaron v -40
+KPX ncaron y -15
+KPX ncaron yacute -15
+KPX ncaron ydieresis -15
+KPX ncommaaccent v -40
+KPX ncommaaccent y -15
+KPX ncommaaccent yacute -15
+KPX ncommaaccent ydieresis -15
+KPX ntilde v -40
+KPX ntilde y -15
+KPX ntilde yacute -15
+KPX ntilde ydieresis -15
+KPX o v -15
+KPX o w -25
+KPX o y -10
+KPX o yacute -10
+KPX o ydieresis -10
+KPX oacute v -15
+KPX oacute w -25
+KPX oacute y -10
+KPX oacute yacute -10
+KPX oacute ydieresis -10
+KPX ocircumflex v -15
+KPX ocircumflex w -25
+KPX ocircumflex y -10
+KPX ocircumflex yacute -10
+KPX ocircumflex ydieresis -10
+KPX odieresis v -15
+KPX odieresis w -25
+KPX odieresis y -10
+KPX odieresis yacute -10
+KPX odieresis ydieresis -10
+KPX ograve v -15
+KPX ograve w -25
+KPX ograve y -10
+KPX ograve yacute -10
+KPX ograve ydieresis -10
+KPX ohungarumlaut v -15
+KPX ohungarumlaut w -25
+KPX ohungarumlaut y -10
+KPX ohungarumlaut yacute -10
+KPX ohungarumlaut ydieresis -10
+KPX omacron v -15
+KPX omacron w -25
+KPX omacron y -10
+KPX omacron yacute -10
+KPX omacron ydieresis -10
+KPX oslash v -15
+KPX oslash w -25
+KPX oslash y -10
+KPX oslash yacute -10
+KPX oslash ydieresis -10
+KPX otilde v -15
+KPX otilde w -25
+KPX otilde y -10
+KPX otilde yacute -10
+KPX otilde ydieresis -10
+KPX p y -10
+KPX p yacute -10
+KPX p ydieresis -10
+KPX period quotedblright -70
+KPX period quoteright -70
+KPX quotedblleft A -80
+KPX quotedblleft Aacute -80
+KPX quotedblleft Abreve -80
+KPX quotedblleft Acircumflex -80
+KPX quotedblleft Adieresis -80
+KPX quotedblleft Agrave -80
+KPX quotedblleft Amacron -80
+KPX quotedblleft Aogonek -80
+KPX quotedblleft Aring -80
+KPX quotedblleft Atilde -80
+KPX quoteleft A -80
+KPX quoteleft Aacute -80
+KPX quoteleft Abreve -80
+KPX quoteleft Acircumflex -80
+KPX quoteleft Adieresis -80
+KPX quoteleft Agrave -80
+KPX quoteleft Amacron -80
+KPX quoteleft Aogonek -80
+KPX quoteleft Aring -80
+KPX quoteleft Atilde -80
+KPX quoteleft quoteleft -74
+KPX quoteright d -50
+KPX quoteright dcroat -50
+KPX quoteright l -10
+KPX quoteright lacute -10
+KPX quoteright lcommaaccent -10
+KPX quoteright lslash -10
+KPX quoteright quoteright -74
+KPX quoteright r -50
+KPX quoteright racute -50
+KPX quoteright rcaron -50
+KPX quoteright rcommaaccent -50
+KPX quoteright s -55
+KPX quoteright sacute -55
+KPX quoteright scaron -55
+KPX quoteright scedilla -55
+KPX quoteright scommaaccent -55
+KPX quoteright space -74
+KPX quoteright t -18
+KPX quoteright tcommaaccent -18
+KPX quoteright v -50
+KPX r comma -40
+KPX r g -18
+KPX r gbreve -18
+KPX r gcommaaccent -18
+KPX r hyphen -20
+KPX r period -55
+KPX racute comma -40
+KPX racute g -18
+KPX racute gbreve -18
+KPX racute gcommaaccent -18
+KPX racute hyphen -20
+KPX racute period -55
+KPX rcaron comma -40
+KPX rcaron g -18
+KPX rcaron gbreve -18
+KPX rcaron gcommaaccent -18
+KPX rcaron hyphen -20
+KPX rcaron period -55
+KPX rcommaaccent comma -40
+KPX rcommaaccent g -18
+KPX rcommaaccent gbreve -18
+KPX rcommaaccent gcommaaccent -18
+KPX rcommaaccent hyphen -20
+KPX rcommaaccent period -55
+KPX space A -55
+KPX space Aacute -55
+KPX space Abreve -55
+KPX space Acircumflex -55
+KPX space Adieresis -55
+KPX space Agrave -55
+KPX space Amacron -55
+KPX space Aogonek -55
+KPX space Aring -55
+KPX space Atilde -55
+KPX space T -18
+KPX space Tcaron -18
+KPX space Tcommaaccent -18
+KPX space V -50
+KPX space W -30
+KPX space Y -90
+KPX space Yacute -90
+KPX space Ydieresis -90
+KPX v a -25
+KPX v aacute -25
+KPX v abreve -25
+KPX v acircumflex -25
+KPX v adieresis -25
+KPX v agrave -25
+KPX v amacron -25
+KPX v aogonek -25
+KPX v aring -25
+KPX v atilde -25
+KPX v comma -65
+KPX v e -15
+KPX v eacute -15
+KPX v ecaron -15
+KPX v ecircumflex -15
+KPX v edieresis -15
+KPX v edotaccent -15
+KPX v egrave -15
+KPX v emacron -15
+KPX v eogonek -15
+KPX v o -20
+KPX v oacute -20
+KPX v ocircumflex -20
+KPX v odieresis -20
+KPX v ograve -20
+KPX v ohungarumlaut -20
+KPX v omacron -20
+KPX v oslash -20
+KPX v otilde -20
+KPX v period -65
+KPX w a -10
+KPX w aacute -10
+KPX w abreve -10
+KPX w acircumflex -10
+KPX w adieresis -10
+KPX w agrave -10
+KPX w amacron -10
+KPX w aogonek -10
+KPX w aring -10
+KPX w atilde -10
+KPX w comma -65
+KPX w o -10
+KPX w oacute -10
+KPX w ocircumflex -10
+KPX w odieresis -10
+KPX w ograve -10
+KPX w ohungarumlaut -10
+KPX w omacron -10
+KPX w oslash -10
+KPX w otilde -10
+KPX w period -65
+KPX x e -15
+KPX x eacute -15
+KPX x ecaron -15
+KPX x ecircumflex -15
+KPX x edieresis -15
+KPX x edotaccent -15
+KPX x egrave -15
+KPX x emacron -15
+KPX x eogonek -15
+KPX y comma -65
+KPX y period -65
+KPX yacute comma -65
+KPX yacute period -65
+KPX ydieresis comma -65
+KPX ydieresis period -65
+EndKernPairs
+EndKernData
+EndFontMetrics
diff --git a/program/libraries/afm/ZapfDingbats.afm b/program/libraries/afm/ZapfDingbats.afm
--- /dev/null
@@ -0,0 +1,225 @@
+StartFontMetrics 4.1
+Comment Copyright (c) 1985, 1987, 1988, 1989, 1997 Adobe Systems Incorporated. All Rights Reserved.
+Comment Creation Date: Thu May 1 15:14:13 1997
+Comment UniqueID 43082
+Comment VMusage 45775 55535
+FontName ZapfDingbats
+FullName ITC Zapf Dingbats
+FamilyName ZapfDingbats
+Weight Medium
+ItalicAngle 0
+IsFixedPitch false
+CharacterSet Special
+FontBBox -1 -143 981 820
+UnderlinePosition -100
+UnderlineThickness 50
+Version 002.000
+Notice Copyright (c) 1985, 1987, 1988, 1989, 1997 Adobe Systems Incorporated. All Rights Reserved.ITC Zapf Dingbats is a registered trademark of International Typeface Corporation.
+EncodingScheme FontSpecific
+StdHW 28
+StdVW 90
+StartCharMetrics 202
+C 32 ; WX 278 ; N space ; B 0 0 0 0 ;
+C 33 ; WX 974 ; N a1 ; B 35 72 939 621 ;
+C 34 ; WX 961 ; N a2 ; B 35 81 927 611 ;
+C 35 ; WX 974 ; N a202 ; B 35 72 939 621 ;
+C 36 ; WX 980 ; N a3 ; B 35 0 945 692 ;
+C 37 ; WX 719 ; N a4 ; B 34 139 685 566 ;
+C 38 ; WX 789 ; N a5 ; B 35 -14 755 705 ;
+C 39 ; WX 790 ; N a119 ; B 35 -14 755 705 ;
+C 40 ; WX 791 ; N a118 ; B 35 -13 761 705 ;
+C 41 ; WX 690 ; N a117 ; B 34 138 655 553 ;
+C 42 ; WX 960 ; N a11 ; B 35 123 925 568 ;
+C 43 ; WX 939 ; N a12 ; B 35 134 904 559 ;
+C 44 ; WX 549 ; N a13 ; B 29 -11 516 705 ;
+C 45 ; WX 855 ; N a14 ; B 34 59 820 632 ;
+C 46 ; WX 911 ; N a15 ; B 35 50 876 642 ;
+C 47 ; WX 933 ; N a16 ; B 35 139 899 550 ;
+C 48 ; WX 911 ; N a105 ; B 35 50 876 642 ;
+C 49 ; WX 945 ; N a17 ; B 35 139 909 553 ;
+C 50 ; WX 974 ; N a18 ; B 35 104 938 587 ;
+C 51 ; WX 755 ; N a19 ; B 34 -13 721 705 ;
+C 52 ; WX 846 ; N a20 ; B 36 -14 811 705 ;
+C 53 ; WX 762 ; N a21 ; B 35 0 727 692 ;
+C 54 ; WX 761 ; N a22 ; B 35 0 727 692 ;
+C 55 ; WX 571 ; N a23 ; B -1 -68 571 661 ;
+C 56 ; WX 677 ; N a24 ; B 36 -13 642 705 ;
+C 57 ; WX 763 ; N a25 ; B 35 0 728 692 ;
+C 58 ; WX 760 ; N a26 ; B 35 0 726 692 ;
+C 59 ; WX 759 ; N a27 ; B 35 0 725 692 ;
+C 60 ; WX 754 ; N a28 ; B 35 0 720 692 ;
+C 61 ; WX 494 ; N a6 ; B 35 0 460 692 ;
+C 62 ; WX 552 ; N a7 ; B 35 0 517 692 ;
+C 63 ; WX 537 ; N a8 ; B 35 0 503 692 ;
+C 64 ; WX 577 ; N a9 ; B 35 96 542 596 ;
+C 65 ; WX 692 ; N a10 ; B 35 -14 657 705 ;
+C 66 ; WX 786 ; N a29 ; B 35 -14 751 705 ;
+C 67 ; WX 788 ; N a30 ; B 35 -14 752 705 ;
+C 68 ; WX 788 ; N a31 ; B 35 -14 753 705 ;
+C 69 ; WX 790 ; N a32 ; B 35 -14 756 705 ;
+C 70 ; WX 793 ; N a33 ; B 35 -13 759 705 ;
+C 71 ; WX 794 ; N a34 ; B 35 -13 759 705 ;
+C 72 ; WX 816 ; N a35 ; B 35 -14 782 705 ;
+C 73 ; WX 823 ; N a36 ; B 35 -14 787 705 ;
+C 74 ; WX 789 ; N a37 ; B 35 -14 754 705 ;
+C 75 ; WX 841 ; N a38 ; B 35 -14 807 705 ;
+C 76 ; WX 823 ; N a39 ; B 35 -14 789 705 ;
+C 77 ; WX 833 ; N a40 ; B 35 -14 798 705 ;
+C 78 ; WX 816 ; N a41 ; B 35 -13 782 705 ;
+C 79 ; WX 831 ; N a42 ; B 35 -14 796 705 ;
+C 80 ; WX 923 ; N a43 ; B 35 -14 888 705 ;
+C 81 ; WX 744 ; N a44 ; B 35 0 710 692 ;
+C 82 ; WX 723 ; N a45 ; B 35 0 688 692 ;
+C 83 ; WX 749 ; N a46 ; B 35 0 714 692 ;
+C 84 ; WX 790 ; N a47 ; B 34 -14 756 705 ;
+C 85 ; WX 792 ; N a48 ; B 35 -14 758 705 ;
+C 86 ; WX 695 ; N a49 ; B 35 -14 661 706 ;
+C 87 ; WX 776 ; N a50 ; B 35 -6 741 699 ;
+C 88 ; WX 768 ; N a51 ; B 35 -7 734 699 ;
+C 89 ; WX 792 ; N a52 ; B 35 -14 757 705 ;
+C 90 ; WX 759 ; N a53 ; B 35 0 725 692 ;
+C 91 ; WX 707 ; N a54 ; B 35 -13 672 704 ;
+C 92 ; WX 708 ; N a55 ; B 35 -14 672 705 ;
+C 93 ; WX 682 ; N a56 ; B 35 -14 647 705 ;
+C 94 ; WX 701 ; N a57 ; B 35 -14 666 705 ;
+C 95 ; WX 826 ; N a58 ; B 35 -14 791 705 ;
+C 96 ; WX 815 ; N a59 ; B 35 -14 780 705 ;
+C 97 ; WX 789 ; N a60 ; B 35 -14 754 705 ;
+C 98 ; WX 789 ; N a61 ; B 35 -14 754 705 ;
+C 99 ; WX 707 ; N a62 ; B 34 -14 673 705 ;
+C 100 ; WX 687 ; N a63 ; B 36 0 651 692 ;
+C 101 ; WX 696 ; N a64 ; B 35 0 661 691 ;
+C 102 ; WX 689 ; N a65 ; B 35 0 655 692 ;
+C 103 ; WX 786 ; N a66 ; B 34 -14 751 705 ;
+C 104 ; WX 787 ; N a67 ; B 35 -14 752 705 ;
+C 105 ; WX 713 ; N a68 ; B 35 -14 678 705 ;
+C 106 ; WX 791 ; N a69 ; B 35 -14 756 705 ;
+C 107 ; WX 785 ; N a70 ; B 36 -14 751 705 ;
+C 108 ; WX 791 ; N a71 ; B 35 -14 757 705 ;
+C 109 ; WX 873 ; N a72 ; B 35 -14 838 705 ;
+C 110 ; WX 761 ; N a73 ; B 35 0 726 692 ;
+C 111 ; WX 762 ; N a74 ; B 35 0 727 692 ;
+C 112 ; WX 762 ; N a203 ; B 35 0 727 692 ;
+C 113 ; WX 759 ; N a75 ; B 35 0 725 692 ;
+C 114 ; WX 759 ; N a204 ; B 35 0 725 692 ;
+C 115 ; WX 892 ; N a76 ; B 35 0 858 705 ;
+C 116 ; WX 892 ; N a77 ; B 35 -14 858 692 ;
+C 117 ; WX 788 ; N a78 ; B 35 -14 754 705 ;
+C 118 ; WX 784 ; N a79 ; B 35 -14 749 705 ;
+C 119 ; WX 438 ; N a81 ; B 35 -14 403 705 ;
+C 120 ; WX 138 ; N a82 ; B 35 0 104 692 ;
+C 121 ; WX 277 ; N a83 ; B 35 0 242 692 ;
+C 122 ; WX 415 ; N a84 ; B 35 0 380 692 ;
+C 123 ; WX 392 ; N a97 ; B 35 263 357 705 ;
+C 124 ; WX 392 ; N a98 ; B 34 263 357 705 ;
+C 125 ; WX 668 ; N a99 ; B 35 263 633 705 ;
+C 126 ; WX 668 ; N a100 ; B 36 263 634 705 ;
+C 128 ; WX 390 ; N a89 ; B 35 -14 356 705 ;
+C 129 ; WX 390 ; N a90 ; B 35 -14 355 705 ;
+C 130 ; WX 317 ; N a93 ; B 35 0 283 692 ;
+C 131 ; WX 317 ; N a94 ; B 35 0 283 692 ;
+C 132 ; WX 276 ; N a91 ; B 35 0 242 692 ;
+C 133 ; WX 276 ; N a92 ; B 35 0 242 692 ;
+C 134 ; WX 509 ; N a205 ; B 35 0 475 692 ;
+C 135 ; WX 509 ; N a85 ; B 35 0 475 692 ;
+C 136 ; WX 410 ; N a206 ; B 35 0 375 692 ;
+C 137 ; WX 410 ; N a86 ; B 35 0 375 692 ;
+C 138 ; WX 234 ; N a87 ; B 35 -14 199 705 ;
+C 139 ; WX 234 ; N a88 ; B 35 -14 199 705 ;
+C 140 ; WX 334 ; N a95 ; B 35 0 299 692 ;
+C 141 ; WX 334 ; N a96 ; B 35 0 299 692 ;
+C 161 ; WX 732 ; N a101 ; B 35 -143 697 806 ;
+C 162 ; WX 544 ; N a102 ; B 56 -14 488 706 ;
+C 163 ; WX 544 ; N a103 ; B 34 -14 508 705 ;
+C 164 ; WX 910 ; N a104 ; B 35 40 875 651 ;
+C 165 ; WX 667 ; N a106 ; B 35 -14 633 705 ;
+C 166 ; WX 760 ; N a107 ; B 35 -14 726 705 ;
+C 167 ; WX 760 ; N a108 ; B 0 121 758 569 ;
+C 168 ; WX 776 ; N a112 ; B 35 0 741 705 ;
+C 169 ; WX 595 ; N a111 ; B 34 -14 560 705 ;
+C 170 ; WX 694 ; N a110 ; B 35 -14 659 705 ;
+C 171 ; WX 626 ; N a109 ; B 34 0 591 705 ;
+C 172 ; WX 788 ; N a120 ; B 35 -14 754 705 ;
+C 173 ; WX 788 ; N a121 ; B 35 -14 754 705 ;
+C 174 ; WX 788 ; N a122 ; B 35 -14 754 705 ;
+C 175 ; WX 788 ; N a123 ; B 35 -14 754 705 ;
+C 176 ; WX 788 ; N a124 ; B 35 -14 754 705 ;
+C 177 ; WX 788 ; N a125 ; B 35 -14 754 705 ;
+C 178 ; WX 788 ; N a126 ; B 35 -14 754 705 ;
+C 179 ; WX 788 ; N a127 ; B 35 -14 754 705 ;
+C 180 ; WX 788 ; N a128 ; B 35 -14 754 705 ;
+C 181 ; WX 788 ; N a129 ; B 35 -14 754 705 ;
+C 182 ; WX 788 ; N a130 ; B 35 -14 754 705 ;
+C 183 ; WX 788 ; N a131 ; B 35 -14 754 705 ;
+C 184 ; WX 788 ; N a132 ; B 35 -14 754 705 ;
+C 185 ; WX 788 ; N a133 ; B 35 -14 754 705 ;
+C 186 ; WX 788 ; N a134 ; B 35 -14 754 705 ;
+C 187 ; WX 788 ; N a135 ; B 35 -14 754 705 ;
+C 188 ; WX 788 ; N a136 ; B 35 -14 754 705 ;
+C 189 ; WX 788 ; N a137 ; B 35 -14 754 705 ;
+C 190 ; WX 788 ; N a138 ; B 35 -14 754 705 ;
+C 191 ; WX 788 ; N a139 ; B 35 -14 754 705 ;
+C 192 ; WX 788 ; N a140 ; B 35 -14 754 705 ;
+C 193 ; WX 788 ; N a141 ; B 35 -14 754 705 ;
+C 194 ; WX 788 ; N a142 ; B 35 -14 754 705 ;
+C 195 ; WX 788 ; N a143 ; B 35 -14 754 705 ;
+C 196 ; WX 788 ; N a144 ; B 35 -14 754 705 ;
+C 197 ; WX 788 ; N a145 ; B 35 -14 754 705 ;
+C 198 ; WX 788 ; N a146 ; B 35 -14 754 705 ;
+C 199 ; WX 788 ; N a147 ; B 35 -14 754 705 ;
+C 200 ; WX 788 ; N a148 ; B 35 -14 754 705 ;
+C 201 ; WX 788 ; N a149 ; B 35 -14 754 705 ;
+C 202 ; WX 788 ; N a150 ; B 35 -14 754 705 ;
+C 203 ; WX 788 ; N a151 ; B 35 -14 754 705 ;
+C 204 ; WX 788 ; N a152 ; B 35 -14 754 705 ;
+C 205 ; WX 788 ; N a153 ; B 35 -14 754 705 ;
+C 206 ; WX 788 ; N a154 ; B 35 -14 754 705 ;
+C 207 ; WX 788 ; N a155 ; B 35 -14 754 705 ;
+C 208 ; WX 788 ; N a156 ; B 35 -14 754 705 ;
+C 209 ; WX 788 ; N a157 ; B 35 -14 754 705 ;
+C 210 ; WX 788 ; N a158 ; B 35 -14 754 705 ;
+C 211 ; WX 788 ; N a159 ; B 35 -14 754 705 ;
+C 212 ; WX 894 ; N a160 ; B 35 58 860 634 ;
+C 213 ; WX 838 ; N a161 ; B 35 152 803 540 ;
+C 214 ; WX 1016 ; N a163 ; B 34 152 981 540 ;
+C 215 ; WX 458 ; N a164 ; B 35 -127 422 820 ;
+C 216 ; WX 748 ; N a196 ; B 35 94 698 597 ;
+C 217 ; WX 924 ; N a165 ; B 35 140 890 552 ;
+C 218 ; WX 748 ; N a192 ; B 35 94 698 597 ;
+C 219 ; WX 918 ; N a166 ; B 35 166 884 526 ;
+C 220 ; WX 927 ; N a167 ; B 35 32 892 660 ;
+C 221 ; WX 928 ; N a168 ; B 35 129 891 562 ;
+C 222 ; WX 928 ; N a169 ; B 35 128 893 563 ;
+C 223 ; WX 834 ; N a170 ; B 35 155 799 537 ;
+C 224 ; WX 873 ; N a171 ; B 35 93 838 599 ;
+C 225 ; WX 828 ; N a172 ; B 35 104 791 588 ;
+C 226 ; WX 924 ; N a173 ; B 35 98 889 594 ;
+C 227 ; WX 924 ; N a162 ; B 35 98 889 594 ;
+C 228 ; WX 917 ; N a174 ; B 35 0 882 692 ;
+C 229 ; WX 930 ; N a175 ; B 35 84 896 608 ;
+C 230 ; WX 931 ; N a176 ; B 35 84 896 608 ;
+C 231 ; WX 463 ; N a177 ; B 35 -99 429 791 ;
+C 232 ; WX 883 ; N a178 ; B 35 71 848 623 ;
+C 233 ; WX 836 ; N a179 ; B 35 44 802 648 ;
+C 234 ; WX 836 ; N a193 ; B 35 44 802 648 ;
+C 235 ; WX 867 ; N a180 ; B 35 101 832 591 ;
+C 236 ; WX 867 ; N a199 ; B 35 101 832 591 ;
+C 237 ; WX 696 ; N a181 ; B 35 44 661 648 ;
+C 238 ; WX 696 ; N a200 ; B 35 44 661 648 ;
+C 239 ; WX 874 ; N a182 ; B 35 77 840 619 ;
+C 241 ; WX 874 ; N a201 ; B 35 73 840 615 ;
+C 242 ; WX 760 ; N a183 ; B 35 0 725 692 ;
+C 243 ; WX 946 ; N a184 ; B 35 160 911 533 ;
+C 244 ; WX 771 ; N a197 ; B 34 37 736 655 ;
+C 245 ; WX 865 ; N a185 ; B 35 207 830 481 ;
+C 246 ; WX 771 ; N a194 ; B 34 37 736 655 ;
+C 247 ; WX 888 ; N a198 ; B 34 -19 853 712 ;
+C 248 ; WX 967 ; N a186 ; B 35 124 932 568 ;
+C 249 ; WX 888 ; N a195 ; B 34 -19 853 712 ;
+C 250 ; WX 831 ; N a187 ; B 35 113 796 579 ;
+C 251 ; WX 873 ; N a188 ; B 36 118 838 578 ;
+C 252 ; WX 927 ; N a189 ; B 35 150 891 542 ;
+C 253 ; WX 970 ; N a190 ; B 35 76 931 616 ;
+C 254 ; WX 918 ; N a191 ; B 34 99 884 593 ;
+EndCharMetrics
+EndFontMetrics
diff --git a/program/libraries/afm/compile_afm.pl b/program/libraries/afm/compile_afm.pl
--- /dev/null
@@ -0,0 +1,486 @@
+#!/usr/bin/perl -w
+
+require 5.005;
+use strict;
+
+# The glyps list can be downloaded from
+# http://partners.adobe.com/asn/developer/type/glyphlist.txt
+# This URL is from this page:
+# http://partners.adobe.com/asn/developer/type/unicodegn.html
+# which is refered from
+# http://partners.adobe.com/asn/developer/technotes/fonts.html
+
+my $onlyHelvetica = 0;
+
+my %globalName2Unicode;
+my %font_code = ();
+
+my $indent0 = "";
+my $indent1 = " ";
+my $indent2 = $indent1 x 3;
+
+my $q = 0;
+my $qU = 0;
+
+sub read_glyphlist
+{
+ my $fn ="glyphlist.txt";
+ open(FH, $fn)
+ || die "Can't read $fn\n";
+ my %seen = ();
+ while (<FH>) {
+ next if /^\s*#/;
+ next unless /^([0-9A-F]{4});(\w+);/;
+ my $unicode = 0 + hex($1);
+ my $name = $2;
+ next if ($globalName2Unicode{$name});
+ $globalName2Unicode{$name} = $unicode;
+ }
+ close(FH);
+}
+
+sub process_all_fonts
+{
+ my $dir = ".";
+ my $wc = "*.afm";
+ $wc = "Helvetica.afm" if $onlyHelvetica;
+ $wc = "ZapfDin.afm" if 0;
+ $wc = "Helve*.afm" if 0;
+ $wc = "Times-BoldItalic.afm" if 0;
+ foreach my $fn (glob("$dir/$wc")) {
+ process_font($fn);
+ }
+}
+
+sub process_font
+{
+ my ($fn) = @_;
+ print STDERR "Compiling afm file: $fn\n";
+ my %fi = (); # font info
+ my $c = "";
+ $fi{C} = \$c;
+ $fi{ligaturesR} = {};
+ $fi{FontSpecificUnicodeNameToChar} = {};
+ $fi{filename} = $fn;
+ $fi{filename} =~ s/.*\///;
+ $fi{Ascender} = 0;
+ $fi{Descender} = 0;
+ open(FH, $fn) || die "Can't open $fn\n";
+ print STDERR "Reads global font info\n" if $q;
+ while (<FH>) {
+ chomp;
+ next if /^\s*$/ || /^\s*#/;
+ $fi{Ascender} = $1 if /^Ascender\s+(-?\d+)/;
+ $fi{Descender} = $1 if /^Descender\s+(-?\d+)/;
+ last if /^StartCharMetrics/;
+ next unless (/^(\S+)\s+(\S(.*\S)?)/);
+ my $id = $1;
+ my $value = $2;
+ $value =~ s/\s+/ /g;
+ $fi{"Afm$id"} = $value;
+ }
+ my $fontName = $fi{AfmFontName};
+ $c .= "\n\n/* ". ("-" x 66) . "*/\n";
+ $c .= "/* FontName: $fontName */\n";
+ $c .= "/* FullName: $fi{AfmFullName} */\n";
+ $c .= "/* FamilyName: $fi{AfmFamilyName} */\n";
+ $fi{cName} = $fontName;
+ $fi{cName} =~ s/\W/_/g;
+ my %charMetrics = ();
+ my %kerning = ();
+ read_charmetrics(\%fi, \%charMetrics);
+ while (<FH>) {
+ read_kerning(\%fi, \%kerning) if /^StartKernPairs/;
+ }
+ if (0) {
+ my @names = keys %charMetrics;
+ print STDERR "Did read ", ($#names + 1), " font metrics\n";
+ }
+ write_font(\%fi, \%charMetrics, \%kerning);
+}
+
+sub read_charmetrics
+{
+ my ($fiR, $charMetricsR) = @_;
+ print STDERR "Reads char metric info\n" if $q;
+ my $isZapfDingbats = $$fiR{AfmFontName} eq "ZapfDingbats";
+ my $ligaturesR = $$fiR{ligaturesR};
+ my %ligatures = ();
+ my %seenUnicodes = ();
+ while (<FH>) {
+ chomp;
+ next if /^\s*$/ || /^\s*#/;
+ last if /^EndCharMetrics/;
+#next unless /N S / || /N comma /;
+#next unless /N ([sfil]|fi) /;
+#print "$_\n";
+ my $line = $_;
+# C 102 ; WX 333 ; N f ; B -169 -205 446 698 ; L i fi ; L l fl ;
+ my ($width, $unicode, $name, @charLigatures);
+ foreach (split/\s*;\s*/, $line) {
+ if (/^C\s+(-?\d+)/) {
+ $unicode = 0 + $1;
+ } elsif (/^N\s+(\w+)/) {
+ $name = $1;
+ } elsif (/^WX?\s+(-?\d+)/) {
+ $width = normalize_width($1, 0);
+ } elsif (/^L\s+(\w+)\s+(\w+)/) {
+ push(@charLigatures, $1, $2);
+ }
+ }
+ if ($unicode < 0) {
+ unless (defined $name) {
+ print STDERR "Glyph missing name and code: $_\n";
+ next;
+ }
+ $unicode = name2uni($fiR, $name);
+ print STDERR "name2uni: $name -> $unicode\n" if $qU && 0;
+ } elsif (defined $name) {
+ my $std = $globalName2Unicode{$name};
+ if (!defined $std) {
+ print STDERR "Adds unicode mapping: ",
+ "$name -> $unicode\n" if $qU;
+ ${$$fiR{FontSpecificUnicodeNameToChar}}{$name} = $unicode;
+ } else {
+ $unicode = $std;
+ }
+ }
+ if (!defined($unicode) || $unicode <= 0) {
+ next if $isZapfDingbats && $name =~ /^a(\d+)$/;
+ next if $$fiR{AfmFontName} eq "Symbol" && $name eq "apple";
+ print STDERR "Glyph '$name' has unknown unicode: $_\n";
+ next;
+ }
+ unless (defined $width) {
+ print STDERR "Glyph '$name' missing width: $_\n";
+ next;
+ }
+ if ($seenUnicodes{$unicode}) {
+ print STDERR "Duplicate character: unicode = $unicode, ",
+ "$name and ", $seenUnicodes{$unicode},
+ " (might be due to Adobe charset remapping)\n";
+ next;
+ }
+ $seenUnicodes{$unicode} = $name;
+ my %c = ();
+ $c{name} = $name;
+ $c{unicode} = $unicode;
+ $c{width} = $width;
+ $$charMetricsR{$unicode} = \%c;
+ $ligatures{$unicode} = \@charLigatures if $#charLigatures >= 0;
+ }
+ foreach my $unicode (keys %ligatures) {
+ my $aR = $ligatures{$unicode};
+ my $unicode2 = name2uni($fiR, $$aR[0]);
+ my $unicode3 = name2uni($fiR, $$aR[1]);
+ unless ($unicode2) {
+ print STDERR "Missing ligature char 1: $$aR[0]\n";
+ next;
+ }
+ unless ($unicode3) {
+ print STDERR "Missing ligature char 2: $$aR[1]\n";
+ next;
+ }
+ my $key = sprintf("%04d;%04d", $unicode, $unicode2);
+ $$ligaturesR{$key} = $unicode3;
+ }
+}
+
+sub name2uni
+{
+ my ($fiR, $name) = @_;
+ my $fontMapR = $$fiR{FontSpecificUnicodeNameToChar};
+ return $globalName2Unicode{$name} || $$fontMapR{$name};
+}
+
+sub read_kerning
+{
+ my ($fiR, $kerningR) = @_;
+ print STDERR "Reads kerning info\n" if $q;
+ while (<FH>) {
+ chomp;
+ next if /^\s*$/ || /^\s*#/;
+ last if /^EndKernPairs/;
+ unless (/^KPX\s+(\w+)\s+(\w+)\s+(-?\d+)\s*$/) {
+ print STDERR "Can't parse kern spec: $_\n";
+ next;
+ }
+ my $name1 = $1;
+ my $name2 = $2;
+ my $delta = normalize_width($3, 1);
+ next unless $delta;
+ my $unicode1 = name2uni($fiR, $name1);
+ my $unicode2 = name2uni($fiR, $name2);
+ unless ($unicode1 && $unicode2) {
+ print "Unknown kern pair: $name1 and $name2\n";
+ next;
+ }
+ my $charR = $$kerningR{$unicode1};
+ unless (defined $charR) {
+ my %c = ();
+ $charR = \%c;
+ $$kerningR{$unicode1} = $charR;
+ }
+ $$charR{$unicode2} = $delta;
+ }
+}
+
+sub write_font
+{
+ my ($fiR, $charMetricsR, $kerningR) = @_;
+ print STDERR "Writes font\n" if $q;
+ my $cR = $$fiR{C};
+ $$fiR{widthsA} = make_array();
+ $$fiR{kerning_indexA} = make_array();
+ $$fiR{kerning_dataA} = make_array();
+ $$fiR{highchars_indexA} = make_array();
+ $$fiR{ligaturesA} = make_array();
+ write_font_metrics($fiR, $charMetricsR, $kerningR);
+ write_ligatures($fiR);
+ my $widths_count = array_size($$fiR{widthsA});
+ my $kerning_index_count = array_size($$fiR{kerning_indexA});
+ my $kerning_data_count = array_size($$fiR{kerning_dataA});
+ my $highchars_count = array_size($$fiR{highchars_indexA});
+ my $ligatures_count = array_size($$fiR{ligaturesA}) / 3;
+ my $info_code = "";
+ my $i2 = $indent2;
+ my $packedSize = $widths_count + 2 * $kerning_index_count +
+ $kerning_data_count + 2 * $highchars_count +
+ 3 * 2 * $ligatures_count;
+ $info_code .= $indent1 . "{ /* $$fiR{filename} $packedSize bytes */\n";
+ $info_code .= $i2 . "\"$$fiR{AfmFontName}\", \"$$fiR{AfmFullName}\",\n";
+ $info_code .= $i2 . $$fiR{Ascender} . ", " . $$fiR{Descender} . ",\n";
+ $info_code .= $i2 . $$fiR{widthsACName} . ",\n";
+ $info_code .= $i2 . $$fiR{kerning_indexACName} . ",\n";
+ $info_code .= $i2 . $$fiR{kerning_dataACName} . ",\n";
+ $info_code .= $i2 . $$fiR{highchars_indexACName} . ", ";
+ $info_code .= $highchars_count . ",\n";
+ $info_code .= $i2 . $$fiR{ligaturesACName} . ", ";
+ $info_code .= $ligatures_count;
+ $info_code .= "},\n";
+ $font_code{$$fiR{AfmFullName}} = { TABLES => $$cR, INFO => $info_code};
+}
+
+sub write_font_metrics
+{
+ my ($fiR, $charMetricsR, $kerningR) = @_;
+ print STDERR "Writes font metrics\n" if $q;
+ my $lastUnicode = 31;
+ my $cR = $$fiR{C};
+ my $widthsA = $$fiR{widthsA};
+ my $kerning_indexA = $$fiR{kerning_indexA};
+ my $kerning_dataA = $$fiR{kerning_dataA};
+ my $highchars_indexA = $$fiR{highchars_indexA};
+ my @uniArray = sort { $a <=> $b } keys %$charMetricsR;
+ my $highchars_count = 0;
+ my $had_kerning = 0;
+ while (1) {
+ my $fill = 0;
+ if ($#uniArray < 0) {
+ last if $lastUnicode > 126;
+ $fill = 1;
+ } elsif ($lastUnicode < 126 && $uniArray[0] > $lastUnicode + 1) {
+ $fill = 1;
+ }
+ if ($fill) {
+ $lastUnicode++;
+#print STDERR "fill for $lastUnicode, $#uniArray, $uniArray[0]\n";
+ append_to_array($widthsA, 0);
+ append_to_array($kerning_indexA, 0);
+ next;
+ }
+ my $unicode = shift @uniArray;
+ next if $unicode < 32;
+ $lastUnicode = $unicode;
+ my $metricsR = $$charMetricsR{$unicode};
+ if ($unicode > 126) {
+ append_to_array($highchars_indexA, $unicode);
+ $highchars_count++;
+ }
+ my $m = $$metricsR{width};
+ $m = "/* ".array_size($widthsA)."=$unicode */". $m if 0;
+ append_to_array($widthsA, $m);
+ my $kerningInfoR = $$kerningR{$unicode};
+ my $kerning_index = 0;
+ if (defined $kerningInfoR) {
+ my @kerns = ();
+ my $numKernings = 0;
+ foreach my $unicode2 (sort { $a <=> $b } keys %$kerningInfoR) {
+ my $delta = $$kerningInfoR{$unicode2};
+ $numKernings++;
+ append_escaped_16bit_int(\@kerns, $unicode2);
+ push(@kerns, $delta);
+ $had_kerning = 1;
+ }
+ $kerning_index = append_8bit_subarray($kerning_dataA, $numKernings, @kerns);
+ }
+ append_to_array($kerning_indexA, $kerning_index);
+ }
+ $$fiR{kerning_indexA} = make_array() if !$had_kerning;
+ write_array($fiR, "widths", "afm_cuint8");
+ write_array($fiR, "kerning_index", "afm_sint16");
+ write_array($fiR, "kerning_data", "afm_cuint8");
+ write_array($fiR, "highchars_index", "afm_cuint16");
+}
+
+sub write_ligatures
+{
+ my ($fiR) = @_;
+ print STDERR "Writes font ligatures\n" if $q;
+ my $ligaturesA = $$fiR{ligaturesA};
+ my $ligaturesR = $$fiR{ligaturesR};
+ foreach (sort keys %$ligaturesR) {
+ unless (/^(\w{4});(\w{4})$/) {
+ die "Invalid ligature key: $_";
+ }
+ append_to_array($ligaturesA, $1 + 0, $2 + 0, $$ligaturesR{$_});
+ }
+ write_array($fiR, "ligatures", "afm_cunicode");
+}
+
+sub indent
+{
+ my ($num) = @_;
+ return " " x $num;
+}
+
+sub make_array
+{
+ my @a = ();
+ return \@a;
+}
+
+sub append_to_array
+{
+ my ($aR, @newElements) = @_;
+ my $z1 = array_size($aR);
+ push(@$aR, @newElements);
+ my $z2 = array_size($aR);
+ my $zz = $#newElements +1;
+}
+
+sub append_8bit_subarray
+{
+ my ($aR, $numItems, @newElements) = @_;
+ push(@$aR, 42) if !array_size($aR); # initial dummy value
+ die if $numItems > $#newElements + 1;
+ my $idx = $#{$aR} + 1;
+#print "append_8bit_subarray ", ($#newElements+1), " = (", join(", ", @newElements), ") -> $idx\n";
+ append_escaped_16bit_int($aR, $numItems);
+ push(@$aR, @newElements);
+ die "Can't handle that big sub array, sorry...\n" if $idx > 50000;
+ return $idx;
+}
+
+sub append_escaped_16bit_int
+{
+ my ($aR, $count) = @_;
+ die "Invalid count = 0\n" unless $count;
+ if ($count >= 510) {
+ push(@$aR, 1, int($count / 256), int($count % 256));
+ print STDERR "full: $count\n" if 0;
+ } elsif ($count >= 254) {
+ push(@$aR, 0, $count - 254);
+ print STDERR "semi: $count\n" if 0;
+ } else {
+ push(@$aR, $count + 1);
+ }
+}
+
+sub array_size
+{
+ my ($aR) = @_;
+ return $#{$aR} + 1;
+}
+
+sub write_array
+{
+ my ($fiR, $name, $type) = @_;
+ my $aR = $$fiR{$name."A"};
+ my $cName = $$fiR{cName};
+ my $num = $#{$aR} + 1;
+ my $array_name_key = $name."ACName";
+ if ($num == 0) {
+ $$fiR{$array_name_key} = "NULL";
+ return;
+ }
+ my $cR = $$fiR{C};
+ my $array_name = "afm_" . $cName . "_" . $name;
+ $$fiR{$array_name_key} = $array_name;
+ $$cR .= "static $type $array_name" . "[] = { /* $num */\n";
+ my $line = $indent1;
+ for (my $i = 0; $i < $num; $i++) {
+ $line .= "," if $i > 0;
+ if (length($line) > 65) {
+ $line .= "\n";
+ $$cR .= $line;
+ $line = $indent1;
+ }
+ $line .= $$aR[$i];
+ }
+ $line .= "\n";
+ $$cR .= $line;
+ $$cR .= "};\n";
+}
+
+sub normalize_width
+{
+ my ($w, $signed) = @_;
+ my $n = int(($w + 3) / 6);
+ if ($signed) {
+ $n = -128 if $n < -128;
+ $n = 127 if $n > 127;
+ $n = 256 + $n if $n < 0; # make unsigned.
+ } else {
+ $n = 0 if $n < 0;
+ $n = 255 if $n > 255;
+ }
+ return $n;
+}
+
+sub main
+{
+ my $cfn = "../../src/rrd_afm_data.c";
+ read_glyphlist();
+ process_all_fonts();
+ my @fonts = sort keys %font_code;
+ unless ($#fonts >= 0) {
+ die "You must have at least 1 font.\n";
+ }
+ open(CFILE, ">$cfn") || die "Can't create $cfn\n";
+ print CFILE header($cfn);
+ print CFILE ${$font_code{$_}}{TABLES} foreach @fonts;
+ print CFILE "const afm_fontinfo afm_fontinfolist[] = {\n";
+ print CFILE ${$font_code{$_}}{INFO} foreach @fonts;
+ print CFILE $indent1 . "{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }\n";
+ print CFILE $indent0 . "};\n";
+ print CFILE $indent0 . "const int afm_fontinfo_count = ",
+ ($#fonts + 1), ";\n";
+ close(CFILE);
+ print STDERR "Compiled ", ($#fonts+1), " fonts.\n";
+}
+
+sub header
+{
+ my ($fn) = @_;
+ $fn =~ s/.*\///;
+ my $h = $fn;
+ $h =~ s/\.c$/.h/;
+ return <<"END";
+/****************************************************************************
+ * RRDtool 1.1.x Copyright Tobias Oetiker, 1997 - 2002
+ ****************************************************************************
+ * $fn Encoded afm (Adobe Font Metrics) for selected fonts.
+ ****************************************************************************
+ *
+ * THIS FILE IS AUTOGENERATED BY PERL. DO NOT EDIT.
+ *
+ ****************************************************************************/
+
+#include "$h"
+#include <stdlib.h>
+
+END
+}
+
+main();
diff --git a/program/libraries/afm/glyphlist.txt b/program/libraries/afm/glyphlist.txt
--- /dev/null
@@ -0,0 +1,1151 @@
+#
+# Name: Adobe Glyph List
+# Table version: 1.2
+# Date: 22 Oct 1998
+#
+# Description:
+#
+# The Adobe Glyph List (AGL) list relates Unicode values (UVs) to glyph
+# names, and should be used only as described in the document "Unicode and
+# Glyph Names," at
+# http://partners.adobe.com/asn/developer/typeforum/unicodegn.html .
+#
+# The glyph name to UV relation is one to many. 12 glyph names are mapped to
+# two UVs each; each UV has a separate entry. All other glyph names are
+# mapped to one UV each.
+#
+# The Unicode Standard version 2.1 is used for all UVs outside of the Private
+# Use area, except for 4 entries (see Revision History for 1.2 below).
+#
+# There are 1051 entries in this list, 171 of which are in the Corporate Use
+# subarea (CUS). Refer to the document "Unicode Corporate Use Subarea as used
+# by Adobe Systems," at
+# http://partners.adobe.com/asn/developer/typeforum/corporateuse.txt
+# for compatibility decompositions for these characters, and to the document
+# "Unicode and Glyph Names" for more information the CUS.
+#
+# Format: Semicolon-delimited fields:
+#
+# (1) Standard UV or CUS UV. (4 uppercase hexadecimal digits)
+#
+# (2) Glyph name. (upper- and lowercase letters, digits)
+#
+# (3) Character names: Unicode character names for standard UVs, and
+# descriptive names for CUS UVs. (uppercase letters, hyphen, space)
+#
+# (4) [optional] Comment. A comment of "Duplicate" indicates one of two
+# UVs of a double-mapping. It is the UV that may be given a uni<CODE>
+# override, or the UV that is in the CUS, as described in the document
+# "Unicode and Glyph Names."
+#
+# The entries are sorted by glyph name in increasing ASCII order; entries
+# with the same glyph name are sorted in decreasing priority order.
+#
+# Lines starting with "#" are comments; blank lines should be ignored.
+#
+# Revision History:
+#
+# 1.2 [22 Oct 1998]
+#
+# Some Central European glyph names were remapped and the glyph "dotlessj"
+# was added. Some entries in the table below have not changed but are
+# included to provide a complete context for other glyphs that have been
+# remapped or double-mapped. "-" means that the entry for that UV does not
+# exist in the AGL.
+#
+# -------- ---------------------- ---------------- --------------
+# UV Character name AGL 1.1 AGL 1.2
+# (shortened) glyph name glyph name
+# -------- ---------------------- ---------------- --------------
+# 015E/F S/s with cedilla S/scommaaccent S/scedilla
+# 0162/3 T/t with cedilla T/tcommaaccent T/tcommaaccent
+# 0218/9 S/s with comma below - S/scommaaccent
+# 021A/B T/t with comma below - T/tcommaaccent
+# 1E9E/F S/s with comma below S/scedilla -
+# F6C1/2 S/s with cedilla S/scedilla S/scedilla
+# F6BE dotless j - dotlessj
+# -------- ---------------------- ---------------- --------------
+#
+# The characters at U+1E9E/F in AGL 1.1, LATIN CAPITAL/SMALL LETTER S WITH
+# COMMA BELOW, which are proposed new characters (see (b) in the notes for
+# AGL 1.1 below), have since been reassigned by the Unicode Standard to new
+# proposed values of U+0218/9. These characters, as well as U+021A/B, LATIN
+# CAPITAL/SMALL LETTER T WITH COMMA BELOW, are not in the Unicode Standard
+# 2.1.
+#
+# Entries with the same glyph name are now sorted in decreasing priority
+# order instead of in increasing UV order.
+#
+# 1.1 [24 Nov 1997]
+#
+# a. The "Euro" glyph's UV assignment is changed from U+20A0 (EURO-CURRENCY
+# SIGN) to U+20AC (EURO SIGN). While U+20AC is not defined in the
+# Unicode Standard 2.0, it has been accepted by the Unicode Technical
+# Committee for the next version of the Standard; it has not yet passed
+# the ISO approval process as of 7 November '97.
+#
+# b. Glyphs "Scedilla" and "scedilla", which were assigned in the Corporate
+# Use Subarea in AGL 1.0, are now additionally mapped to U+1E9E and
+# U+1E9F respectively. These two UVs share the same Unicode approval
+# status as the Euro glyph (see a. above).
+#
+# c. The "fraction" glyph is now additionally mapped to U+2215, to match
+# Windows Glyph List 4.
+#
+# d. The descriptive name for glyph "onefitted", in the Corporate Use
+# subarea, is changed from "TABULAR DIGIT ONE" to "PROPORTIONAL DIGIT
+# ONE".
+#
+# 1.0 [17 Jul 1997] Original version
+#
+0041;A;LATIN CAPITAL LETTER A
+00C6;AE;LATIN CAPITAL LETTER AE
+01FC;AEacute;LATIN CAPITAL LETTER AE WITH ACUTE
+F7E6;AEsmall;LATIN SMALL CAPITAL LETTER AE
+00C1;Aacute;LATIN CAPITAL LETTER A WITH ACUTE
+F7E1;Aacutesmall;LATIN SMALL CAPITAL LETTER A WITH ACUTE
+0102;Abreve;LATIN CAPITAL LETTER A WITH BREVE
+00C2;Acircumflex;LATIN CAPITAL LETTER A WITH CIRCUMFLEX
+F7E2;Acircumflexsmall;LATIN SMALL CAPITAL LETTER A WITH CIRCUMFLEX
+F6C9;Acute;CAPITAL ACUTE ACCENT
+F7B4;Acutesmall;SMALL CAPITAL ACUTE ACCENT
+00C4;Adieresis;LATIN CAPITAL LETTER A WITH DIAERESIS
+F7E4;Adieresissmall;LATIN SMALL CAPITAL LETTER A WITH DIAERESIS
+00C0;Agrave;LATIN CAPITAL LETTER A WITH GRAVE
+F7E0;Agravesmall;LATIN SMALL CAPITAL LETTER A WITH GRAVE
+0391;Alpha;GREEK CAPITAL LETTER ALPHA
+0386;Alphatonos;GREEK CAPITAL LETTER ALPHA WITH TONOS
+0100;Amacron;LATIN CAPITAL LETTER A WITH MACRON
+0104;Aogonek;LATIN CAPITAL LETTER A WITH OGONEK
+00C5;Aring;LATIN CAPITAL LETTER A WITH RING ABOVE
+01FA;Aringacute;LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE
+F7E5;Aringsmall;LATIN SMALL CAPITAL LETTER A WITH RING ABOVE
+F761;Asmall;LATIN SMALL CAPITAL LETTER A
+00C3;Atilde;LATIN CAPITAL LETTER A WITH TILDE
+F7E3;Atildesmall;LATIN SMALL CAPITAL LETTER A WITH TILDE
+0042;B;LATIN CAPITAL LETTER B
+0392;Beta;GREEK CAPITAL LETTER BETA
+F6F4;Brevesmall;SMALL CAPITAL BREVE
+F762;Bsmall;LATIN SMALL CAPITAL LETTER B
+0043;C;LATIN CAPITAL LETTER C
+0106;Cacute;LATIN CAPITAL LETTER C WITH ACUTE
+F6CA;Caron;CAPITAL CARON
+F6F5;Caronsmall;SMALL CAPITAL CARON
+010C;Ccaron;LATIN CAPITAL LETTER C WITH CARON
+00C7;Ccedilla;LATIN CAPITAL LETTER C WITH CEDILLA
+F7E7;Ccedillasmall;LATIN SMALL CAPITAL LETTER C WITH CEDILLA
+0108;Ccircumflex;LATIN CAPITAL LETTER C WITH CIRCUMFLEX
+010A;Cdotaccent;LATIN CAPITAL LETTER C WITH DOT ABOVE
+F7B8;Cedillasmall;SMALL CAPITAL CEDILLA
+03A7;Chi;GREEK CAPITAL LETTER CHI
+F6F6;Circumflexsmall;SMALL CAPITAL MODIFIER LETTER CIRCUMFLEX ACCENT
+F763;Csmall;LATIN SMALL CAPITAL LETTER C
+0044;D;LATIN CAPITAL LETTER D
+010E;Dcaron;LATIN CAPITAL LETTER D WITH CARON
+0110;Dcroat;LATIN CAPITAL LETTER D WITH STROKE
+2206;Delta;INCREMENT
+0394;Delta;GREEK CAPITAL LETTER DELTA;Duplicate
+F6CB;Dieresis;CAPITAL DIAERESIS
+F6CC;DieresisAcute;CAPITAL DIAERESIS ACUTE ACCENT
+F6CD;DieresisGrave;CAPITAL DIAERESIS GRAVE ACCENT
+F7A8;Dieresissmall;SMALL CAPITAL DIAERESIS
+F6F7;Dotaccentsmall;SMALL CAPITAL DOT ABOVE
+F764;Dsmall;LATIN SMALL CAPITAL LETTER D
+0045;E;LATIN CAPITAL LETTER E
+00C9;Eacute;LATIN CAPITAL LETTER E WITH ACUTE
+F7E9;Eacutesmall;LATIN SMALL CAPITAL LETTER E WITH ACUTE
+0114;Ebreve;LATIN CAPITAL LETTER E WITH BREVE
+011A;Ecaron;LATIN CAPITAL LETTER E WITH CARON
+00CA;Ecircumflex;LATIN CAPITAL LETTER E WITH CIRCUMFLEX
+F7EA;Ecircumflexsmall;LATIN SMALL CAPITAL LETTER E WITH CIRCUMFLEX
+00CB;Edieresis;LATIN CAPITAL LETTER E WITH DIAERESIS
+F7EB;Edieresissmall;LATIN SMALL CAPITAL LETTER E WITH DIAERESIS
+0116;Edotaccent;LATIN CAPITAL LETTER E WITH DOT ABOVE
+00C8;Egrave;LATIN CAPITAL LETTER E WITH GRAVE
+F7E8;Egravesmall;LATIN SMALL CAPITAL LETTER E WITH GRAVE
+0112;Emacron;LATIN CAPITAL LETTER E WITH MACRON
+014A;Eng;LATIN CAPITAL LETTER ENG
+0118;Eogonek;LATIN CAPITAL LETTER E WITH OGONEK
+0395;Epsilon;GREEK CAPITAL LETTER EPSILON
+0388;Epsilontonos;GREEK CAPITAL LETTER EPSILON WITH TONOS
+F765;Esmall;LATIN SMALL CAPITAL LETTER E
+0397;Eta;GREEK CAPITAL LETTER ETA
+0389;Etatonos;GREEK CAPITAL LETTER ETA WITH TONOS
+00D0;Eth;LATIN CAPITAL LETTER ETH
+F7F0;Ethsmall;LATIN SMALL CAPITAL LETTER ETH
+20AC;Euro;EURO SIGN
+0046;F;LATIN CAPITAL LETTER F
+F766;Fsmall;LATIN SMALL CAPITAL LETTER F
+0047;G;LATIN CAPITAL LETTER G
+0393;Gamma;GREEK CAPITAL LETTER GAMMA
+011E;Gbreve;LATIN CAPITAL LETTER G WITH BREVE
+01E6;Gcaron;LATIN CAPITAL LETTER G WITH CARON
+011C;Gcircumflex;LATIN CAPITAL LETTER G WITH CIRCUMFLEX
+0122;Gcommaaccent;LATIN CAPITAL LETTER G WITH CEDILLA
+0120;Gdotaccent;LATIN CAPITAL LETTER G WITH DOT ABOVE
+F6CE;Grave;CAPITAL GRAVE ACCENT
+F760;Gravesmall;SMALL CAPITAL GRAVE ACCENT
+F767;Gsmall;LATIN SMALL CAPITAL LETTER G
+0048;H;LATIN CAPITAL LETTER H
+25CF;H18533;BLACK CIRCLE
+25AA;H18543;BLACK SMALL SQUARE
+25AB;H18551;WHITE SMALL SQUARE
+25A1;H22073;WHITE SQUARE
+0126;Hbar;LATIN CAPITAL LETTER H WITH STROKE
+0124;Hcircumflex;LATIN CAPITAL LETTER H WITH CIRCUMFLEX
+F768;Hsmall;LATIN SMALL CAPITAL LETTER H
+F6CF;Hungarumlaut;CAPITAL DOUBLE ACUTE ACCENT
+F6F8;Hungarumlautsmall;SMALL CAPITAL DOUBLE ACUTE ACCENT
+0049;I;LATIN CAPITAL LETTER I
+0132;IJ;LATIN CAPITAL LIGATURE IJ
+00CD;Iacute;LATIN CAPITAL LETTER I WITH ACUTE
+F7ED;Iacutesmall;LATIN SMALL CAPITAL LETTER I WITH ACUTE
+012C;Ibreve;LATIN CAPITAL LETTER I WITH BREVE
+00CE;Icircumflex;LATIN CAPITAL LETTER I WITH CIRCUMFLEX
+F7EE;Icircumflexsmall;LATIN SMALL CAPITAL LETTER I WITH CIRCUMFLEX
+00CF;Idieresis;LATIN CAPITAL LETTER I WITH DIAERESIS
+F7EF;Idieresissmall;LATIN SMALL CAPITAL LETTER I WITH DIAERESIS
+0130;Idotaccent;LATIN CAPITAL LETTER I WITH DOT ABOVE
+2111;Ifraktur;BLACK-LETTER CAPITAL I
+00CC;Igrave;LATIN CAPITAL LETTER I WITH GRAVE
+F7EC;Igravesmall;LATIN SMALL CAPITAL LETTER I WITH GRAVE
+012A;Imacron;LATIN CAPITAL LETTER I WITH MACRON
+012E;Iogonek;LATIN CAPITAL LETTER I WITH OGONEK
+0399;Iota;GREEK CAPITAL LETTER IOTA
+03AA;Iotadieresis;GREEK CAPITAL LETTER IOTA WITH DIALYTIKA
+038A;Iotatonos;GREEK CAPITAL LETTER IOTA WITH TONOS
+F769;Ismall;LATIN SMALL CAPITAL LETTER I
+0128;Itilde;LATIN CAPITAL LETTER I WITH TILDE
+004A;J;LATIN CAPITAL LETTER J
+0134;Jcircumflex;LATIN CAPITAL LETTER J WITH CIRCUMFLEX
+F76A;Jsmall;LATIN SMALL CAPITAL LETTER J
+004B;K;LATIN CAPITAL LETTER K
+039A;Kappa;GREEK CAPITAL LETTER KAPPA
+0136;Kcommaaccent;LATIN CAPITAL LETTER K WITH CEDILLA
+F76B;Ksmall;LATIN SMALL CAPITAL LETTER K
+004C;L;LATIN CAPITAL LETTER L
+F6BF;LL;LATIN CAPITAL LETTER LL
+0139;Lacute;LATIN CAPITAL LETTER L WITH ACUTE
+039B;Lambda;GREEK CAPITAL LETTER LAMDA
+013D;Lcaron;LATIN CAPITAL LETTER L WITH CARON
+013B;Lcommaaccent;LATIN CAPITAL LETTER L WITH CEDILLA
+013F;Ldot;LATIN CAPITAL LETTER L WITH MIDDLE DOT
+0141;Lslash;LATIN CAPITAL LETTER L WITH STROKE
+F6F9;Lslashsmall;LATIN SMALL CAPITAL LETTER L WITH STROKE
+F76C;Lsmall;LATIN SMALL CAPITAL LETTER L
+004D;M;LATIN CAPITAL LETTER M
+F6D0;Macron;CAPITAL MACRON
+F7AF;Macronsmall;SMALL CAPITAL MACRON
+F76D;Msmall;LATIN SMALL CAPITAL LETTER M
+039C;Mu;GREEK CAPITAL LETTER MU
+004E;N;LATIN CAPITAL LETTER N
+0143;Nacute;LATIN CAPITAL LETTER N WITH ACUTE
+0147;Ncaron;LATIN CAPITAL LETTER N WITH CARON
+0145;Ncommaaccent;LATIN CAPITAL LETTER N WITH CEDILLA
+F76E;Nsmall;LATIN SMALL CAPITAL LETTER N
+00D1;Ntilde;LATIN CAPITAL LETTER N WITH TILDE
+F7F1;Ntildesmall;LATIN SMALL CAPITAL LETTER N WITH TILDE
+039D;Nu;GREEK CAPITAL LETTER NU
+004F;O;LATIN CAPITAL LETTER O
+0152;OE;LATIN CAPITAL LIGATURE OE
+F6FA;OEsmall;LATIN SMALL CAPITAL LIGATURE OE
+00D3;Oacute;LATIN CAPITAL LETTER O WITH ACUTE
+F7F3;Oacutesmall;LATIN SMALL CAPITAL LETTER O WITH ACUTE
+014E;Obreve;LATIN CAPITAL LETTER O WITH BREVE
+00D4;Ocircumflex;LATIN CAPITAL LETTER O WITH CIRCUMFLEX
+F7F4;Ocircumflexsmall;LATIN SMALL CAPITAL LETTER O WITH CIRCUMFLEX
+00D6;Odieresis;LATIN CAPITAL LETTER O WITH DIAERESIS
+F7F6;Odieresissmall;LATIN SMALL CAPITAL LETTER O WITH DIAERESIS
+F6FB;Ogoneksmall;SMALL CAPITAL OGONEK
+00D2;Ograve;LATIN CAPITAL LETTER O WITH GRAVE
+F7F2;Ogravesmall;LATIN SMALL CAPITAL LETTER O WITH GRAVE
+01A0;Ohorn;LATIN CAPITAL LETTER O WITH HORN
+0150;Ohungarumlaut;LATIN CAPITAL LETTER O WITH DOUBLE ACUTE
+014C;Omacron;LATIN CAPITAL LETTER O WITH MACRON
+2126;Omega;OHM SIGN
+03A9;Omega;GREEK CAPITAL LETTER OMEGA;Duplicate
+038F;Omegatonos;GREEK CAPITAL LETTER OMEGA WITH TONOS
+039F;Omicron;GREEK CAPITAL LETTER OMICRON
+038C;Omicrontonos;GREEK CAPITAL LETTER OMICRON WITH TONOS
+00D8;Oslash;LATIN CAPITAL LETTER O WITH STROKE
+01FE;Oslashacute;LATIN CAPITAL LETTER O WITH STROKE AND ACUTE
+F7F8;Oslashsmall;LATIN SMALL CAPITAL LETTER O WITH STROKE
+F76F;Osmall;LATIN SMALL CAPITAL LETTER O
+00D5;Otilde;LATIN CAPITAL LETTER O WITH TILDE
+F7F5;Otildesmall;LATIN SMALL CAPITAL LETTER O WITH TILDE
+0050;P;LATIN CAPITAL LETTER P
+03A6;Phi;GREEK CAPITAL LETTER PHI
+03A0;Pi;GREEK CAPITAL LETTER PI
+03A8;Psi;GREEK CAPITAL LETTER PSI
+F770;Psmall;LATIN SMALL CAPITAL LETTER P
+0051;Q;LATIN CAPITAL LETTER Q
+F771;Qsmall;LATIN SMALL CAPITAL LETTER Q
+0052;R;LATIN CAPITAL LETTER R
+0154;Racute;LATIN CAPITAL LETTER R WITH ACUTE
+0158;Rcaron;LATIN CAPITAL LETTER R WITH CARON
+0156;Rcommaaccent;LATIN CAPITAL LETTER R WITH CEDILLA
+211C;Rfraktur;BLACK-LETTER CAPITAL R
+03A1;Rho;GREEK CAPITAL LETTER RHO
+F6FC;Ringsmall;SMALL CAPITAL RING ABOVE
+F772;Rsmall;LATIN SMALL CAPITAL LETTER R
+0053;S;LATIN CAPITAL LETTER S
+250C;SF010000;BOX DRAWINGS LIGHT DOWN AND RIGHT
+2514;SF020000;BOX DRAWINGS LIGHT UP AND RIGHT
+2510;SF030000;BOX DRAWINGS LIGHT DOWN AND LEFT
+2518;SF040000;BOX DRAWINGS LIGHT UP AND LEFT
+253C;SF050000;BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+252C;SF060000;BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+2534;SF070000;BOX DRAWINGS LIGHT UP AND HORIZONTAL
+251C;SF080000;BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+2524;SF090000;BOX DRAWINGS LIGHT VERTICAL AND LEFT
+2500;SF100000;BOX DRAWINGS LIGHT HORIZONTAL
+2502;SF110000;BOX DRAWINGS LIGHT VERTICAL
+2561;SF190000;BOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE
+2562;SF200000;BOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE
+2556;SF210000;BOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE
+2555;SF220000;BOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE
+2563;SF230000;BOX DRAWINGS DOUBLE VERTICAL AND LEFT
+2551;SF240000;BOX DRAWINGS DOUBLE VERTICAL
+2557;SF250000;BOX DRAWINGS DOUBLE DOWN AND LEFT
+255D;SF260000;BOX DRAWINGS DOUBLE UP AND LEFT
+255C;SF270000;BOX DRAWINGS UP DOUBLE AND LEFT SINGLE
+255B;SF280000;BOX DRAWINGS UP SINGLE AND LEFT DOUBLE
+255E;SF360000;BOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE
+255F;SF370000;BOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE
+255A;SF380000;BOX DRAWINGS DOUBLE UP AND RIGHT
+2554;SF390000;BOX DRAWINGS DOUBLE DOWN AND RIGHT
+2569;SF400000;BOX DRAWINGS DOUBLE UP AND HORIZONTAL
+2566;SF410000;BOX DRAWINGS DOUBLE DOWN AND HORIZONTAL
+2560;SF420000;BOX DRAWINGS DOUBLE VERTICAL AND RIGHT
+2550;SF430000;BOX DRAWINGS DOUBLE HORIZONTAL
+256C;SF440000;BOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL
+2567;SF450000;BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE
+2568;SF460000;BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE
+2564;SF470000;BOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE
+2565;SF480000;BOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE
+2559;SF490000;BOX DRAWINGS UP DOUBLE AND RIGHT SINGLE
+2558;SF500000;BOX DRAWINGS UP SINGLE AND RIGHT DOUBLE
+2552;SF510000;BOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE
+2553;SF520000;BOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE
+256B;SF530000;BOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE
+256A;SF540000;BOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE
+015A;Sacute;LATIN CAPITAL LETTER S WITH ACUTE
+0160;Scaron;LATIN CAPITAL LETTER S WITH CARON
+F6FD;Scaronsmall;LATIN SMALL CAPITAL LETTER S WITH CARON
+015E;Scedilla;LATIN CAPITAL LETTER S WITH CEDILLA
+F6C1;Scedilla;LATIN CAPITAL LETTER S WITH CEDILLA;Duplicate
+015C;Scircumflex;LATIN CAPITAL LETTER S WITH CIRCUMFLEX
+0218;Scommaaccent;LATIN CAPITAL LETTER S WITH COMMA BELOW
+03A3;Sigma;GREEK CAPITAL LETTER SIGMA
+F773;Ssmall;LATIN SMALL CAPITAL LETTER S
+0054;T;LATIN CAPITAL LETTER T
+03A4;Tau;GREEK CAPITAL LETTER TAU
+0166;Tbar;LATIN CAPITAL LETTER T WITH STROKE
+0164;Tcaron;LATIN CAPITAL LETTER T WITH CARON
+0162;Tcommaaccent;LATIN CAPITAL LETTER T WITH CEDILLA
+021A;Tcommaaccent;LATIN CAPITAL LETTER T WITH COMMA BELOW;Duplicate
+0398;Theta;GREEK CAPITAL LETTER THETA
+00DE;Thorn;LATIN CAPITAL LETTER THORN
+F7FE;Thornsmall;LATIN SMALL CAPITAL LETTER THORN
+F6FE;Tildesmall;SMALL CAPITAL SMALL TILDE
+F774;Tsmall;LATIN SMALL CAPITAL LETTER T
+0055;U;LATIN CAPITAL LETTER U
+00DA;Uacute;LATIN CAPITAL LETTER U WITH ACUTE
+F7FA;Uacutesmall;LATIN SMALL CAPITAL LETTER U WITH ACUTE
+016C;Ubreve;LATIN CAPITAL LETTER U WITH BREVE
+00DB;Ucircumflex;LATIN CAPITAL LETTER U WITH CIRCUMFLEX
+F7FB;Ucircumflexsmall;LATIN SMALL CAPITAL LETTER U WITH CIRCUMFLEX
+00DC;Udieresis;LATIN CAPITAL LETTER U WITH DIAERESIS
+F7FC;Udieresissmall;LATIN SMALL CAPITAL LETTER U WITH DIAERESIS
+00D9;Ugrave;LATIN CAPITAL LETTER U WITH GRAVE
+F7F9;Ugravesmall;LATIN SMALL CAPITAL LETTER U WITH GRAVE
+01AF;Uhorn;LATIN CAPITAL LETTER U WITH HORN
+0170;Uhungarumlaut;LATIN CAPITAL LETTER U WITH DOUBLE ACUTE
+016A;Umacron;LATIN CAPITAL LETTER U WITH MACRON
+0172;Uogonek;LATIN CAPITAL LETTER U WITH OGONEK
+03A5;Upsilon;GREEK CAPITAL LETTER UPSILON
+03D2;Upsilon1;GREEK UPSILON WITH HOOK SYMBOL
+03AB;Upsilondieresis;GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA
+038E;Upsilontonos;GREEK CAPITAL LETTER UPSILON WITH TONOS
+016E;Uring;LATIN CAPITAL LETTER U WITH RING ABOVE
+F775;Usmall;LATIN SMALL CAPITAL LETTER U
+0168;Utilde;LATIN CAPITAL LETTER U WITH TILDE
+0056;V;LATIN CAPITAL LETTER V
+F776;Vsmall;LATIN SMALL CAPITAL LETTER V
+0057;W;LATIN CAPITAL LETTER W
+1E82;Wacute;LATIN CAPITAL LETTER W WITH ACUTE
+0174;Wcircumflex;LATIN CAPITAL LETTER W WITH CIRCUMFLEX
+1E84;Wdieresis;LATIN CAPITAL LETTER W WITH DIAERESIS
+1E80;Wgrave;LATIN CAPITAL LETTER W WITH GRAVE
+F777;Wsmall;LATIN SMALL CAPITAL LETTER W
+0058;X;LATIN CAPITAL LETTER X
+039E;Xi;GREEK CAPITAL LETTER XI
+F778;Xsmall;LATIN SMALL CAPITAL LETTER X
+0059;Y;LATIN CAPITAL LETTER Y
+00DD;Yacute;LATIN CAPITAL LETTER Y WITH ACUTE
+F7FD;Yacutesmall;LATIN SMALL CAPITAL LETTER Y WITH ACUTE
+0176;Ycircumflex;LATIN CAPITAL LETTER Y WITH CIRCUMFLEX
+0178;Ydieresis;LATIN CAPITAL LETTER Y WITH DIAERESIS
+F7FF;Ydieresissmall;LATIN SMALL CAPITAL LETTER Y WITH DIAERESIS
+1EF2;Ygrave;LATIN CAPITAL LETTER Y WITH GRAVE
+F779;Ysmall;LATIN SMALL CAPITAL LETTER Y
+005A;Z;LATIN CAPITAL LETTER Z
+0179;Zacute;LATIN CAPITAL LETTER Z WITH ACUTE
+017D;Zcaron;LATIN CAPITAL LETTER Z WITH CARON
+F6FF;Zcaronsmall;LATIN SMALL CAPITAL LETTER Z WITH CARON
+017B;Zdotaccent;LATIN CAPITAL LETTER Z WITH DOT ABOVE
+0396;Zeta;GREEK CAPITAL LETTER ZETA
+F77A;Zsmall;LATIN SMALL CAPITAL LETTER Z
+0061;a;LATIN SMALL LETTER A
+00E1;aacute;LATIN SMALL LETTER A WITH ACUTE
+0103;abreve;LATIN SMALL LETTER A WITH BREVE
+00E2;acircumflex;LATIN SMALL LETTER A WITH CIRCUMFLEX
+00B4;acute;ACUTE ACCENT
+0301;acutecomb;COMBINING ACUTE ACCENT
+00E4;adieresis;LATIN SMALL LETTER A WITH DIAERESIS
+00E6;ae;LATIN SMALL LETTER AE
+01FD;aeacute;LATIN SMALL LETTER AE WITH ACUTE
+2015;afii00208;HORIZONTAL BAR
+0410;afii10017;CYRILLIC CAPITAL LETTER A
+0411;afii10018;CYRILLIC CAPITAL LETTER BE
+0412;afii10019;CYRILLIC CAPITAL LETTER VE
+0413;afii10020;CYRILLIC CAPITAL LETTER GHE
+0414;afii10021;CYRILLIC CAPITAL LETTER DE
+0415;afii10022;CYRILLIC CAPITAL LETTER IE
+0401;afii10023;CYRILLIC CAPITAL LETTER IO
+0416;afii10024;CYRILLIC CAPITAL LETTER ZHE
+0417;afii10025;CYRILLIC CAPITAL LETTER ZE
+0418;afii10026;CYRILLIC CAPITAL LETTER I
+0419;afii10027;CYRILLIC CAPITAL LETTER SHORT I
+041A;afii10028;CYRILLIC CAPITAL LETTER KA
+041B;afii10029;CYRILLIC CAPITAL LETTER EL
+041C;afii10030;CYRILLIC CAPITAL LETTER EM
+041D;afii10031;CYRILLIC CAPITAL LETTER EN
+041E;afii10032;CYRILLIC CAPITAL LETTER O
+041F;afii10033;CYRILLIC CAPITAL LETTER PE
+0420;afii10034;CYRILLIC CAPITAL LETTER ER
+0421;afii10035;CYRILLIC CAPITAL LETTER ES
+0422;afii10036;CYRILLIC CAPITAL LETTER TE
+0423;afii10037;CYRILLIC CAPITAL LETTER U
+0424;afii10038;CYRILLIC CAPITAL LETTER EF
+0425;afii10039;CYRILLIC CAPITAL LETTER HA
+0426;afii10040;CYRILLIC CAPITAL LETTER TSE
+0427;afii10041;CYRILLIC CAPITAL LETTER CHE
+0428;afii10042;CYRILLIC CAPITAL LETTER SHA
+0429;afii10043;CYRILLIC CAPITAL LETTER SHCHA
+042A;afii10044;CYRILLIC CAPITAL LETTER HARD SIGN
+042B;afii10045;CYRILLIC CAPITAL LETTER YERU
+042C;afii10046;CYRILLIC CAPITAL LETTER SOFT SIGN
+042D;afii10047;CYRILLIC CAPITAL LETTER E
+042E;afii10048;CYRILLIC CAPITAL LETTER YU
+042F;afii10049;CYRILLIC CAPITAL LETTER YA
+0490;afii10050;CYRILLIC CAPITAL LETTER GHE WITH UPTURN
+0402;afii10051;CYRILLIC CAPITAL LETTER DJE
+0403;afii10052;CYRILLIC CAPITAL LETTER GJE
+0404;afii10053;CYRILLIC CAPITAL LETTER UKRAINIAN IE
+0405;afii10054;CYRILLIC CAPITAL LETTER DZE
+0406;afii10055;CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+0407;afii10056;CYRILLIC CAPITAL LETTER YI
+0408;afii10057;CYRILLIC CAPITAL LETTER JE
+0409;afii10058;CYRILLIC CAPITAL LETTER LJE
+040A;afii10059;CYRILLIC CAPITAL LETTER NJE
+040B;afii10060;CYRILLIC CAPITAL LETTER TSHE
+040C;afii10061;CYRILLIC CAPITAL LETTER KJE
+040E;afii10062;CYRILLIC CAPITAL LETTER SHORT U
+F6C4;afii10063;CYRILLIC SMALL LETTER GHE VARIANT
+F6C5;afii10064;CYRILLIC SMALL LETTER BE VARIANT
+0430;afii10065;CYRILLIC SMALL LETTER A
+0431;afii10066;CYRILLIC SMALL LETTER BE
+0432;afii10067;CYRILLIC SMALL LETTER VE
+0433;afii10068;CYRILLIC SMALL LETTER GHE
+0434;afii10069;CYRILLIC SMALL LETTER DE
+0435;afii10070;CYRILLIC SMALL LETTER IE
+0451;afii10071;CYRILLIC SMALL LETTER IO
+0436;afii10072;CYRILLIC SMALL LETTER ZHE
+0437;afii10073;CYRILLIC SMALL LETTER ZE
+0438;afii10074;CYRILLIC SMALL LETTER I
+0439;afii10075;CYRILLIC SMALL LETTER SHORT I
+043A;afii10076;CYRILLIC SMALL LETTER KA
+043B;afii10077;CYRILLIC SMALL LETTER EL
+043C;afii10078;CYRILLIC SMALL LETTER EM
+043D;afii10079;CYRILLIC SMALL LETTER EN
+043E;afii10080;CYRILLIC SMALL LETTER O
+043F;afii10081;CYRILLIC SMALL LETTER PE
+0440;afii10082;CYRILLIC SMALL LETTER ER
+0441;afii10083;CYRILLIC SMALL LETTER ES
+0442;afii10084;CYRILLIC SMALL LETTER TE
+0443;afii10085;CYRILLIC SMALL LETTER U
+0444;afii10086;CYRILLIC SMALL LETTER EF
+0445;afii10087;CYRILLIC SMALL LETTER HA
+0446;afii10088;CYRILLIC SMALL LETTER TSE
+0447;afii10089;CYRILLIC SMALL LETTER CHE
+0448;afii10090;CYRILLIC SMALL LETTER SHA
+0449;afii10091;CYRILLIC SMALL LETTER SHCHA
+044A;afii10092;CYRILLIC SMALL LETTER HARD SIGN
+044B;afii10093;CYRILLIC SMALL LETTER YERU
+044C;afii10094;CYRILLIC SMALL LETTER SOFT SIGN
+044D;afii10095;CYRILLIC SMALL LETTER E
+044E;afii10096;CYRILLIC SMALL LETTER YU
+044F;afii10097;CYRILLIC SMALL LETTER YA
+0491;afii10098;CYRILLIC SMALL LETTER GHE WITH UPTURN
+0452;afii10099;CYRILLIC SMALL LETTER DJE
+0453;afii10100;CYRILLIC SMALL LETTER GJE
+0454;afii10101;CYRILLIC SMALL LETTER UKRAINIAN IE
+0455;afii10102;CYRILLIC SMALL LETTER DZE
+0456;afii10103;CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+0457;afii10104;CYRILLIC SMALL LETTER YI
+0458;afii10105;CYRILLIC SMALL LETTER JE
+0459;afii10106;CYRILLIC SMALL LETTER LJE
+045A;afii10107;CYRILLIC SMALL LETTER NJE
+045B;afii10108;CYRILLIC SMALL LETTER TSHE
+045C;afii10109;CYRILLIC SMALL LETTER KJE
+045E;afii10110;CYRILLIC SMALL LETTER SHORT U
+040F;afii10145;CYRILLIC CAPITAL LETTER DZHE
+0462;afii10146;CYRILLIC CAPITAL LETTER YAT
+0472;afii10147;CYRILLIC CAPITAL LETTER FITA
+0474;afii10148;CYRILLIC CAPITAL LETTER IZHITSA
+F6C6;afii10192;CYRILLIC SMALL LETTER DE VARIANT
+045F;afii10193;CYRILLIC SMALL LETTER DZHE
+0463;afii10194;CYRILLIC SMALL LETTER YAT
+0473;afii10195;CYRILLIC SMALL LETTER FITA
+0475;afii10196;CYRILLIC SMALL LETTER IZHITSA
+F6C7;afii10831;CYRILLIC SMALL LETTER PE VARIANT
+F6C8;afii10832;CYRILLIC SMALL LETTER TE VARIANT
+04D9;afii10846;CYRILLIC SMALL LETTER SCHWA
+200E;afii299;LEFT-TO-RIGHT MARK
+200F;afii300;RIGHT-TO-LEFT MARK
+200D;afii301;ZERO WIDTH JOINER
+066A;afii57381;ARABIC PERCENT SIGN
+060C;afii57388;ARABIC COMMA
+0660;afii57392;ARABIC-INDIC DIGIT ZERO
+0661;afii57393;ARABIC-INDIC DIGIT ONE
+0662;afii57394;ARABIC-INDIC DIGIT TWO
+0663;afii57395;ARABIC-INDIC DIGIT THREE
+0664;afii57396;ARABIC-INDIC DIGIT FOUR
+0665;afii57397;ARABIC-INDIC DIGIT FIVE
+0666;afii57398;ARABIC-INDIC DIGIT SIX
+0667;afii57399;ARABIC-INDIC DIGIT SEVEN
+0668;afii57400;ARABIC-INDIC DIGIT EIGHT
+0669;afii57401;ARABIC-INDIC DIGIT NINE
+061B;afii57403;ARABIC SEMICOLON
+061F;afii57407;ARABIC QUESTION MARK
+0621;afii57409;ARABIC LETTER HAMZA
+0622;afii57410;ARABIC LETTER ALEF WITH MADDA ABOVE
+0623;afii57411;ARABIC LETTER ALEF WITH HAMZA ABOVE
+0624;afii57412;ARABIC LETTER WAW WITH HAMZA ABOVE
+0625;afii57413;ARABIC LETTER ALEF WITH HAMZA BELOW
+0626;afii57414;ARABIC LETTER YEH WITH HAMZA ABOVE
+0627;afii57415;ARABIC LETTER ALEF
+0628;afii57416;ARABIC LETTER BEH
+0629;afii57417;ARABIC LETTER TEH MARBUTA
+062A;afii57418;ARABIC LETTER TEH
+062B;afii57419;ARABIC LETTER THEH
+062C;afii57420;ARABIC LETTER JEEM
+062D;afii57421;ARABIC LETTER HAH
+062E;afii57422;ARABIC LETTER KHAH
+062F;afii57423;ARABIC LETTER DAL
+0630;afii57424;ARABIC LETTER THAL
+0631;afii57425;ARABIC LETTER REH
+0632;afii57426;ARABIC LETTER ZAIN
+0633;afii57427;ARABIC LETTER SEEN
+0634;afii57428;ARABIC LETTER SHEEN
+0635;afii57429;ARABIC LETTER SAD
+0636;afii57430;ARABIC LETTER DAD
+0637;afii57431;ARABIC LETTER TAH
+0638;afii57432;ARABIC LETTER ZAH
+0639;afii57433;ARABIC LETTER AIN
+063A;afii57434;ARABIC LETTER GHAIN
+0640;afii57440;ARABIC TATWEEL
+0641;afii57441;ARABIC LETTER FEH
+0642;afii57442;ARABIC LETTER QAF
+0643;afii57443;ARABIC LETTER KAF
+0644;afii57444;ARABIC LETTER LAM
+0645;afii57445;ARABIC LETTER MEEM
+0646;afii57446;ARABIC LETTER NOON
+0648;afii57448;ARABIC LETTER WAW
+0649;afii57449;ARABIC LETTER ALEF MAKSURA
+064A;afii57450;ARABIC LETTER YEH
+064B;afii57451;ARABIC FATHATAN
+064C;afii57452;ARABIC DAMMATAN
+064D;afii57453;ARABIC KASRATAN
+064E;afii57454;ARABIC FATHA
+064F;afii57455;ARABIC DAMMA
+0650;afii57456;ARABIC KASRA
+0651;afii57457;ARABIC SHADDA
+0652;afii57458;ARABIC SUKUN
+0647;afii57470;ARABIC LETTER HEH
+06A4;afii57505;ARABIC LETTER VEH
+067E;afii57506;ARABIC LETTER PEH
+0686;afii57507;ARABIC LETTER TCHEH
+0698;afii57508;ARABIC LETTER JEH
+06AF;afii57509;ARABIC LETTER GAF
+0679;afii57511;ARABIC LETTER TTEH
+0688;afii57512;ARABIC LETTER DDAL
+0691;afii57513;ARABIC LETTER RREH
+06BA;afii57514;ARABIC LETTER NOON GHUNNA
+06D2;afii57519;ARABIC LETTER YEH BARREE
+06D5;afii57534;ARABIC LETTER AE
+20AA;afii57636;NEW SHEQEL SIGN
+05BE;afii57645;HEBREW PUNCTUATION MAQAF
+05C3;afii57658;HEBREW PUNCTUATION SOF PASUQ
+05D0;afii57664;HEBREW LETTER ALEF
+05D1;afii57665;HEBREW LETTER BET
+05D2;afii57666;HEBREW LETTER GIMEL
+05D3;afii57667;HEBREW LETTER DALET
+05D4;afii57668;HEBREW LETTER HE
+05D5;afii57669;HEBREW LETTER VAV
+05D6;afii57670;HEBREW LETTER ZAYIN
+05D7;afii57671;HEBREW LETTER HET
+05D8;afii57672;HEBREW LETTER TET
+05D9;afii57673;HEBREW LETTER YOD
+05DA;afii57674;HEBREW LETTER FINAL KAF
+05DB;afii57675;HEBREW LETTER KAF
+05DC;afii57676;HEBREW LETTER LAMED
+05DD;afii57677;HEBREW LETTER FINAL MEM
+05DE;afii57678;HEBREW LETTER MEM
+05DF;afii57679;HEBREW LETTER FINAL NUN
+05E0;afii57680;HEBREW LETTER NUN
+05E1;afii57681;HEBREW LETTER SAMEKH
+05E2;afii57682;HEBREW LETTER AYIN
+05E3;afii57683;HEBREW LETTER FINAL PE
+05E4;afii57684;HEBREW LETTER PE
+05E5;afii57685;HEBREW LETTER FINAL TSADI
+05E6;afii57686;HEBREW LETTER TSADI
+05E7;afii57687;HEBREW LETTER QOF
+05E8;afii57688;HEBREW LETTER RESH
+05E9;afii57689;HEBREW LETTER SHIN
+05EA;afii57690;HEBREW LETTER TAV
+FB2A;afii57694;HEBREW LETTER SHIN WITH SHIN DOT
+FB2B;afii57695;HEBREW LETTER SHIN WITH SIN DOT
+FB4B;afii57700;HEBREW LETTER VAV WITH HOLAM
+FB1F;afii57705;HEBREW LIGATURE YIDDISH YOD YOD PATAH
+05F0;afii57716;HEBREW LIGATURE YIDDISH DOUBLE VAV
+05F1;afii57717;HEBREW LIGATURE YIDDISH VAV YOD
+05F2;afii57718;HEBREW LIGATURE YIDDISH DOUBLE YOD
+FB35;afii57723;HEBREW LETTER VAV WITH DAGESH
+05B4;afii57793;HEBREW POINT HIRIQ
+05B5;afii57794;HEBREW POINT TSERE
+05B6;afii57795;HEBREW POINT SEGOL
+05BB;afii57796;HEBREW POINT QUBUTS
+05B8;afii57797;HEBREW POINT QAMATS
+05B7;afii57798;HEBREW POINT PATAH
+05B0;afii57799;HEBREW POINT SHEVA
+05B2;afii57800;HEBREW POINT HATAF PATAH
+05B1;afii57801;HEBREW POINT HATAF SEGOL
+05B3;afii57802;HEBREW POINT HATAF QAMATS
+05C2;afii57803;HEBREW POINT SIN DOT
+05C1;afii57804;HEBREW POINT SHIN DOT
+05B9;afii57806;HEBREW POINT HOLAM
+05BC;afii57807;HEBREW POINT DAGESH OR MAPIQ
+05BD;afii57839;HEBREW POINT METEG
+05BF;afii57841;HEBREW POINT RAFE
+05C0;afii57842;HEBREW PUNCTUATION PASEQ
+02BC;afii57929;MODIFIER LETTER APOSTROPHE
+2105;afii61248;CARE OF
+2113;afii61289;SCRIPT SMALL L
+2116;afii61352;NUMERO SIGN
+202C;afii61573;POP DIRECTIONAL FORMATTING
+202D;afii61574;LEFT-TO-RIGHT OVERRIDE
+202E;afii61575;RIGHT-TO-LEFT OVERRIDE
+200C;afii61664;ZERO WIDTH NON-JOINER
+066D;afii63167;ARABIC FIVE POINTED STAR
+02BD;afii64937;MODIFIER LETTER REVERSED COMMA
+00E0;agrave;LATIN SMALL LETTER A WITH GRAVE
+2135;aleph;ALEF SYMBOL
+03B1;alpha;GREEK SMALL LETTER ALPHA
+03AC;alphatonos;GREEK SMALL LETTER ALPHA WITH TONOS
+0101;amacron;LATIN SMALL LETTER A WITH MACRON
+0026;ampersand;AMPERSAND
+F726;ampersandsmall;SMALL CAPITAL AMPERSAND
+2220;angle;ANGLE
+2329;angleleft;LEFT-POINTING ANGLE BRACKET
+232A;angleright;RIGHT-POINTING ANGLE BRACKET
+0387;anoteleia;GREEK ANO TELEIA
+0105;aogonek;LATIN SMALL LETTER A WITH OGONEK
+2248;approxequal;ALMOST EQUAL TO
+00E5;aring;LATIN SMALL LETTER A WITH RING ABOVE
+01FB;aringacute;LATIN SMALL LETTER A WITH RING ABOVE AND ACUTE
+2194;arrowboth;LEFT RIGHT ARROW
+21D4;arrowdblboth;LEFT RIGHT DOUBLE ARROW
+21D3;arrowdbldown;DOWNWARDS DOUBLE ARROW
+21D0;arrowdblleft;LEFTWARDS DOUBLE ARROW
+21D2;arrowdblright;RIGHTWARDS DOUBLE ARROW
+21D1;arrowdblup;UPWARDS DOUBLE ARROW
+2193;arrowdown;DOWNWARDS ARROW
+F8E7;arrowhorizex;HORIZONTAL ARROW EXTENDER
+2190;arrowleft;LEFTWARDS ARROW
+2192;arrowright;RIGHTWARDS ARROW
+2191;arrowup;UPWARDS ARROW
+2195;arrowupdn;UP DOWN ARROW
+21A8;arrowupdnbse;UP DOWN ARROW WITH BASE
+F8E6;arrowvertex;VERTICAL ARROW EXTENDER
+005E;asciicircum;CIRCUMFLEX ACCENT
+007E;asciitilde;TILDE
+002A;asterisk;ASTERISK
+2217;asteriskmath;ASTERISK OPERATOR
+F6E9;asuperior;SUPERSCRIPT LATIN SMALL LETTER A
+0040;at;COMMERCIAL AT
+00E3;atilde;LATIN SMALL LETTER A WITH TILDE
+0062;b;LATIN SMALL LETTER B
+005C;backslash;REVERSE SOLIDUS
+007C;bar;VERTICAL LINE
+03B2;beta;GREEK SMALL LETTER BETA
+2588;block;FULL BLOCK
+F8F4;braceex;CURLY BRACKET EXTENDER
+007B;braceleft;LEFT CURLY BRACKET
+F8F3;braceleftbt;LEFT CURLY BRACKET BOTTOM
+F8F2;braceleftmid;LEFT CURLY BRACKET MID
+F8F1;bracelefttp;LEFT CURLY BRACKET TOP
+007D;braceright;RIGHT CURLY BRACKET
+F8FE;bracerightbt;RIGHT CURLY BRACKET BOTTOM
+F8FD;bracerightmid;RIGHT CURLY BRACKET MID
+F8FC;bracerighttp;RIGHT CURLY BRACKET TOP
+005B;bracketleft;LEFT SQUARE BRACKET
+F8F0;bracketleftbt;LEFT SQUARE BRACKET BOTTOM
+F8EF;bracketleftex;LEFT SQUARE BRACKET EXTENDER
+F8EE;bracketlefttp;LEFT SQUARE BRACKET TOP
+005D;bracketright;RIGHT SQUARE BRACKET
+F8FB;bracketrightbt;RIGHT SQUARE BRACKET BOTTOM
+F8FA;bracketrightex;RIGHT SQUARE BRACKET EXTENDER
+F8F9;bracketrighttp;RIGHT SQUARE BRACKET TOP
+02D8;breve;BREVE
+00A6;brokenbar;BROKEN BAR
+F6EA;bsuperior;SUPERSCRIPT LATIN SMALL LETTER B
+2022;bullet;BULLET
+0063;c;LATIN SMALL LETTER C
+0107;cacute;LATIN SMALL LETTER C WITH ACUTE
+02C7;caron;CARON
+21B5;carriagereturn;DOWNWARDS ARROW WITH CORNER LEFTWARDS
+010D;ccaron;LATIN SMALL LETTER C WITH CARON
+00E7;ccedilla;LATIN SMALL LETTER C WITH CEDILLA
+0109;ccircumflex;LATIN SMALL LETTER C WITH CIRCUMFLEX
+010B;cdotaccent;LATIN SMALL LETTER C WITH DOT ABOVE
+00B8;cedilla;CEDILLA
+00A2;cent;CENT SIGN
+F6DF;centinferior;SUBSCRIPT CENT SIGN
+F7A2;centoldstyle;OLDSTYLE CENT SIGN
+F6E0;centsuperior;SUPERSCRIPT CENT SIGN
+03C7;chi;GREEK SMALL LETTER CHI
+25CB;circle;WHITE CIRCLE
+2297;circlemultiply;CIRCLED TIMES
+2295;circleplus;CIRCLED PLUS
+02C6;circumflex;MODIFIER LETTER CIRCUMFLEX ACCENT
+2663;club;BLACK CLUB SUIT
+003A;colon;COLON
+20A1;colonmonetary;COLON SIGN
+002C;comma;COMMA
+F6C3;commaaccent;COMMA BELOW
+F6E1;commainferior;SUBSCRIPT COMMA
+F6E2;commasuperior;SUPERSCRIPT COMMA
+2245;congruent;APPROXIMATELY EQUAL TO
+00A9;copyright;COPYRIGHT SIGN
+F8E9;copyrightsans;COPYRIGHT SIGN SANS SERIF
+F6D9;copyrightserif;COPYRIGHT SIGN SERIF
+00A4;currency;CURRENCY SIGN
+F6D1;cyrBreve;CAPITAL CYRILLIC BREVE
+F6D2;cyrFlex;CAPITAL CYRILLIC CIRCUMFLEX
+F6D4;cyrbreve;CYRILLIC BREVE
+F6D5;cyrflex;CYRILLIC CIRCUMFLEX
+0064;d;LATIN SMALL LETTER D
+2020;dagger;DAGGER
+2021;daggerdbl;DOUBLE DAGGER
+F6D3;dblGrave;CAPITAL DOUBLE GRAVE ACCENT
+F6D6;dblgrave;DOUBLE GRAVE ACCENT
+010F;dcaron;LATIN SMALL LETTER D WITH CARON
+0111;dcroat;LATIN SMALL LETTER D WITH STROKE
+00B0;degree;DEGREE SIGN
+03B4;delta;GREEK SMALL LETTER DELTA
+2666;diamond;BLACK DIAMOND SUIT
+00A8;dieresis;DIAERESIS
+F6D7;dieresisacute;DIAERESIS ACUTE ACCENT
+F6D8;dieresisgrave;DIAERESIS GRAVE ACCENT
+0385;dieresistonos;GREEK DIALYTIKA TONOS
+00F7;divide;DIVISION SIGN
+2593;dkshade;DARK SHADE
+2584;dnblock;LOWER HALF BLOCK
+0024;dollar;DOLLAR SIGN
+F6E3;dollarinferior;SUBSCRIPT DOLLAR SIGN
+F724;dollaroldstyle;OLDSTYLE DOLLAR SIGN
+F6E4;dollarsuperior;SUPERSCRIPT DOLLAR SIGN
+20AB;dong;DONG SIGN
+02D9;dotaccent;DOT ABOVE
+0323;dotbelowcomb;COMBINING DOT BELOW
+0131;dotlessi;LATIN SMALL LETTER DOTLESS I
+F6BE;dotlessj;LATIN SMALL LETTER DOTLESS J
+22C5;dotmath;DOT OPERATOR
+F6EB;dsuperior;SUPERSCRIPT LATIN SMALL LETTER D
+0065;e;LATIN SMALL LETTER E
+00E9;eacute;LATIN SMALL LETTER E WITH ACUTE
+0115;ebreve;LATIN SMALL LETTER E WITH BREVE
+011B;ecaron;LATIN SMALL LETTER E WITH CARON
+00EA;ecircumflex;LATIN SMALL LETTER E WITH CIRCUMFLEX
+00EB;edieresis;LATIN SMALL LETTER E WITH DIAERESIS
+0117;edotaccent;LATIN SMALL LETTER E WITH DOT ABOVE
+00E8;egrave;LATIN SMALL LETTER E WITH GRAVE
+0038;eight;DIGIT EIGHT
+2088;eightinferior;SUBSCRIPT EIGHT
+F738;eightoldstyle;OLDSTYLE DIGIT EIGHT
+2078;eightsuperior;SUPERSCRIPT EIGHT
+2208;element;ELEMENT OF
+2026;ellipsis;HORIZONTAL ELLIPSIS
+0113;emacron;LATIN SMALL LETTER E WITH MACRON
+2014;emdash;EM DASH
+2205;emptyset;EMPTY SET
+2013;endash;EN DASH
+014B;eng;LATIN SMALL LETTER ENG
+0119;eogonek;LATIN SMALL LETTER E WITH OGONEK
+03B5;epsilon;GREEK SMALL LETTER EPSILON
+03AD;epsilontonos;GREEK SMALL LETTER EPSILON WITH TONOS
+003D;equal;EQUALS SIGN
+2261;equivalence;IDENTICAL TO
+212E;estimated;ESTIMATED SYMBOL
+F6EC;esuperior;SUPERSCRIPT LATIN SMALL LETTER E
+03B7;eta;GREEK SMALL LETTER ETA
+03AE;etatonos;GREEK SMALL LETTER ETA WITH TONOS
+00F0;eth;LATIN SMALL LETTER ETH
+0021;exclam;EXCLAMATION MARK
+203C;exclamdbl;DOUBLE EXCLAMATION MARK
+00A1;exclamdown;INVERTED EXCLAMATION MARK
+F7A1;exclamdownsmall;SMALL CAPITAL INVERTED EXCLAMATION MARK
+F721;exclamsmall;SMALL CAPITAL EXCLAMATION MARK
+2203;existential;THERE EXISTS
+0066;f;LATIN SMALL LETTER F
+2640;female;FEMALE SIGN
+FB00;ff;LATIN SMALL LIGATURE FF
+FB03;ffi;LATIN SMALL LIGATURE FFI
+FB04;ffl;LATIN SMALL LIGATURE FFL
+FB01;fi;LATIN SMALL LIGATURE FI
+2012;figuredash;FIGURE DASH
+25A0;filledbox;BLACK SQUARE
+25AC;filledrect;BLACK RECTANGLE
+0035;five;DIGIT FIVE
+215D;fiveeighths;VULGAR FRACTION FIVE EIGHTHS
+2085;fiveinferior;SUBSCRIPT FIVE
+F735;fiveoldstyle;OLDSTYLE DIGIT FIVE
+2075;fivesuperior;SUPERSCRIPT FIVE
+FB02;fl;LATIN SMALL LIGATURE FL
+0192;florin;LATIN SMALL LETTER F WITH HOOK
+0034;four;DIGIT FOUR
+2084;fourinferior;SUBSCRIPT FOUR
+F734;fouroldstyle;OLDSTYLE DIGIT FOUR
+2074;foursuperior;SUPERSCRIPT FOUR
+2044;fraction;FRACTION SLASH
+2215;fraction;DIVISION SLASH;Duplicate
+20A3;franc;FRENCH FRANC SIGN
+0067;g;LATIN SMALL LETTER G
+03B3;gamma;GREEK SMALL LETTER GAMMA
+011F;gbreve;LATIN SMALL LETTER G WITH BREVE
+01E7;gcaron;LATIN SMALL LETTER G WITH CARON
+011D;gcircumflex;LATIN SMALL LETTER G WITH CIRCUMFLEX
+0123;gcommaaccent;LATIN SMALL LETTER G WITH CEDILLA
+0121;gdotaccent;LATIN SMALL LETTER G WITH DOT ABOVE
+00DF;germandbls;LATIN SMALL LETTER SHARP S
+2207;gradient;NABLA
+0060;grave;GRAVE ACCENT
+0300;gravecomb;COMBINING GRAVE ACCENT
+003E;greater;GREATER-THAN SIGN
+2265;greaterequal;GREATER-THAN OR EQUAL TO
+00AB;guillemotleft;LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+00BB;guillemotright;RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+2039;guilsinglleft;SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+203A;guilsinglright;SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+0068;h;LATIN SMALL LETTER H
+0127;hbar;LATIN SMALL LETTER H WITH STROKE
+0125;hcircumflex;LATIN SMALL LETTER H WITH CIRCUMFLEX
+2665;heart;BLACK HEART SUIT
+0309;hookabovecomb;COMBINING HOOK ABOVE
+2302;house;HOUSE
+02DD;hungarumlaut;DOUBLE ACUTE ACCENT
+002D;hyphen;HYPHEN-MINUS
+00AD;hyphen;SOFT HYPHEN;Duplicate
+F6E5;hypheninferior;SUBSCRIPT HYPHEN-MINUS
+F6E6;hyphensuperior;SUPERSCRIPT HYPHEN-MINUS
+0069;i;LATIN SMALL LETTER I
+00ED;iacute;LATIN SMALL LETTER I WITH ACUTE
+012D;ibreve;LATIN SMALL LETTER I WITH BREVE
+00EE;icircumflex;LATIN SMALL LETTER I WITH CIRCUMFLEX
+00EF;idieresis;LATIN SMALL LETTER I WITH DIAERESIS
+00EC;igrave;LATIN SMALL LETTER I WITH GRAVE
+0133;ij;LATIN SMALL LIGATURE IJ
+012B;imacron;LATIN SMALL LETTER I WITH MACRON
+221E;infinity;INFINITY
+222B;integral;INTEGRAL
+2321;integralbt;BOTTOM HALF INTEGRAL
+F8F5;integralex;INTEGRAL EXTENDER
+2320;integraltp;TOP HALF INTEGRAL
+2229;intersection;INTERSECTION
+25D8;invbullet;INVERSE BULLET
+25D9;invcircle;INVERSE WHITE CIRCLE
+263B;invsmileface;BLACK SMILING FACE
+012F;iogonek;LATIN SMALL LETTER I WITH OGONEK
+03B9;iota;GREEK SMALL LETTER IOTA
+03CA;iotadieresis;GREEK SMALL LETTER IOTA WITH DIALYTIKA
+0390;iotadieresistonos;GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS
+03AF;iotatonos;GREEK SMALL LETTER IOTA WITH TONOS
+F6ED;isuperior;SUPERSCRIPT LATIN SMALL LETTER I
+0129;itilde;LATIN SMALL LETTER I WITH TILDE
+006A;j;LATIN SMALL LETTER J
+0135;jcircumflex;LATIN SMALL LETTER J WITH CIRCUMFLEX
+006B;k;LATIN SMALL LETTER K
+03BA;kappa;GREEK SMALL LETTER KAPPA
+0137;kcommaaccent;LATIN SMALL LETTER K WITH CEDILLA
+0138;kgreenlandic;LATIN SMALL LETTER KRA
+006C;l;LATIN SMALL LETTER L
+013A;lacute;LATIN SMALL LETTER L WITH ACUTE
+03BB;lambda;GREEK SMALL LETTER LAMDA
+013E;lcaron;LATIN SMALL LETTER L WITH CARON
+013C;lcommaaccent;LATIN SMALL LETTER L WITH CEDILLA
+0140;ldot;LATIN SMALL LETTER L WITH MIDDLE DOT
+003C;less;LESS-THAN SIGN
+2264;lessequal;LESS-THAN OR EQUAL TO
+258C;lfblock;LEFT HALF BLOCK
+20A4;lira;LIRA SIGN
+F6C0;ll;LATIN SMALL LETTER LL
+2227;logicaland;LOGICAL AND
+00AC;logicalnot;NOT SIGN
+2228;logicalor;LOGICAL OR
+017F;longs;LATIN SMALL LETTER LONG S
+25CA;lozenge;LOZENGE
+0142;lslash;LATIN SMALL LETTER L WITH STROKE
+F6EE;lsuperior;SUPERSCRIPT LATIN SMALL LETTER L
+2591;ltshade;LIGHT SHADE
+006D;m;LATIN SMALL LETTER M
+00AF;macron;MACRON
+02C9;macron;MODIFIER LETTER MACRON;Duplicate
+2642;male;MALE SIGN
+2212;minus;MINUS SIGN
+2032;minute;PRIME
+F6EF;msuperior;SUPERSCRIPT LATIN SMALL LETTER M
+00B5;mu;MICRO SIGN
+03BC;mu;GREEK SMALL LETTER MU;Duplicate
+00D7;multiply;MULTIPLICATION SIGN
+266A;musicalnote;EIGHTH NOTE
+266B;musicalnotedbl;BEAMED EIGHTH NOTES
+006E;n;LATIN SMALL LETTER N
+0144;nacute;LATIN SMALL LETTER N WITH ACUTE
+0149;napostrophe;LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+0148;ncaron;LATIN SMALL LETTER N WITH CARON
+0146;ncommaaccent;LATIN SMALL LETTER N WITH CEDILLA
+0039;nine;DIGIT NINE
+2089;nineinferior;SUBSCRIPT NINE
+F739;nineoldstyle;OLDSTYLE DIGIT NINE
+2079;ninesuperior;SUPERSCRIPT NINE
+2209;notelement;NOT AN ELEMENT OF
+2260;notequal;NOT EQUAL TO
+2284;notsubset;NOT A SUBSET OF
+207F;nsuperior;SUPERSCRIPT LATIN SMALL LETTER N
+00F1;ntilde;LATIN SMALL LETTER N WITH TILDE
+03BD;nu;GREEK SMALL LETTER NU
+0023;numbersign;NUMBER SIGN
+006F;o;LATIN SMALL LETTER O
+00F3;oacute;LATIN SMALL LETTER O WITH ACUTE
+014F;obreve;LATIN SMALL LETTER O WITH BREVE
+00F4;ocircumflex;LATIN SMALL LETTER O WITH CIRCUMFLEX
+00F6;odieresis;LATIN SMALL LETTER O WITH DIAERESIS
+0153;oe;LATIN SMALL LIGATURE OE
+02DB;ogonek;OGONEK
+00F2;ograve;LATIN SMALL LETTER O WITH GRAVE
+01A1;ohorn;LATIN SMALL LETTER O WITH HORN
+0151;ohungarumlaut;LATIN SMALL LETTER O WITH DOUBLE ACUTE
+014D;omacron;LATIN SMALL LETTER O WITH MACRON
+03C9;omega;GREEK SMALL LETTER OMEGA
+03D6;omega1;GREEK PI SYMBOL
+03CE;omegatonos;GREEK SMALL LETTER OMEGA WITH TONOS
+03BF;omicron;GREEK SMALL LETTER OMICRON
+03CC;omicrontonos;GREEK SMALL LETTER OMICRON WITH TONOS
+0031;one;DIGIT ONE
+2024;onedotenleader;ONE DOT LEADER
+215B;oneeighth;VULGAR FRACTION ONE EIGHTH
+F6DC;onefitted;PROPORTIONAL DIGIT ONE
+00BD;onehalf;VULGAR FRACTION ONE HALF
+2081;oneinferior;SUBSCRIPT ONE
+F731;oneoldstyle;OLDSTYLE DIGIT ONE
+00BC;onequarter;VULGAR FRACTION ONE QUARTER
+00B9;onesuperior;SUPERSCRIPT ONE
+2153;onethird;VULGAR FRACTION ONE THIRD
+25E6;openbullet;WHITE BULLET
+00AA;ordfeminine;FEMININE ORDINAL INDICATOR
+00BA;ordmasculine;MASCULINE ORDINAL INDICATOR
+221F;orthogonal;RIGHT ANGLE
+00F8;oslash;LATIN SMALL LETTER O WITH STROKE
+01FF;oslashacute;LATIN SMALL LETTER O WITH STROKE AND ACUTE
+F6F0;osuperior;SUPERSCRIPT LATIN SMALL LETTER O
+00F5;otilde;LATIN SMALL LETTER O WITH TILDE
+0070;p;LATIN SMALL LETTER P
+00B6;paragraph;PILCROW SIGN
+0028;parenleft;LEFT PARENTHESIS
+F8ED;parenleftbt;LEFT PAREN BOTTOM
+F8EC;parenleftex;LEFT PAREN EXTENDER
+208D;parenleftinferior;SUBSCRIPT LEFT PARENTHESIS
+207D;parenleftsuperior;SUPERSCRIPT LEFT PARENTHESIS
+F8EB;parenlefttp;LEFT PAREN TOP
+0029;parenright;RIGHT PARENTHESIS
+F8F8;parenrightbt;RIGHT PAREN BOTTOM
+F8F7;parenrightex;RIGHT PAREN EXTENDER
+208E;parenrightinferior;SUBSCRIPT RIGHT PARENTHESIS
+207E;parenrightsuperior;SUPERSCRIPT RIGHT PARENTHESIS
+F8F6;parenrighttp;RIGHT PAREN TOP
+2202;partialdiff;PARTIAL DIFFERENTIAL
+0025;percent;PERCENT SIGN
+002E;period;FULL STOP
+00B7;periodcentered;MIDDLE DOT
+2219;periodcentered;BULLET OPERATOR;Duplicate
+F6E7;periodinferior;SUBSCRIPT FULL STOP
+F6E8;periodsuperior;SUPERSCRIPT FULL STOP
+22A5;perpendicular;UP TACK
+2030;perthousand;PER MILLE SIGN
+20A7;peseta;PESETA SIGN
+03C6;phi;GREEK SMALL LETTER PHI
+03D5;phi1;GREEK PHI SYMBOL
+03C0;pi;GREEK SMALL LETTER PI
+002B;plus;PLUS SIGN
+00B1;plusminus;PLUS-MINUS SIGN
+211E;prescription;PRESCRIPTION TAKE
+220F;product;N-ARY PRODUCT
+2282;propersubset;SUBSET OF
+2283;propersuperset;SUPERSET OF
+221D;proportional;PROPORTIONAL TO
+03C8;psi;GREEK SMALL LETTER PSI
+0071;q;LATIN SMALL LETTER Q
+003F;question;QUESTION MARK
+00BF;questiondown;INVERTED QUESTION MARK
+F7BF;questiondownsmall;SMALL CAPITAL INVERTED QUESTION MARK
+F73F;questionsmall;SMALL CAPITAL QUESTION MARK
+0022;quotedbl;QUOTATION MARK
+201E;quotedblbase;DOUBLE LOW-9 QUOTATION MARK
+201C;quotedblleft;LEFT DOUBLE QUOTATION MARK
+201D;quotedblright;RIGHT DOUBLE QUOTATION MARK
+2018;quoteleft;LEFT SINGLE QUOTATION MARK
+201B;quotereversed;SINGLE HIGH-REVERSED-9 QUOTATION MARK
+2019;quoteright;RIGHT SINGLE QUOTATION MARK
+201A;quotesinglbase;SINGLE LOW-9 QUOTATION MARK
+0027;quotesingle;APOSTROPHE
+0072;r;LATIN SMALL LETTER R
+0155;racute;LATIN SMALL LETTER R WITH ACUTE
+221A;radical;SQUARE ROOT
+F8E5;radicalex;RADICAL EXTENDER
+0159;rcaron;LATIN SMALL LETTER R WITH CARON
+0157;rcommaaccent;LATIN SMALL LETTER R WITH CEDILLA
+2286;reflexsubset;SUBSET OF OR EQUAL TO
+2287;reflexsuperset;SUPERSET OF OR EQUAL TO
+00AE;registered;REGISTERED SIGN
+F8E8;registersans;REGISTERED SIGN SANS SERIF
+F6DA;registerserif;REGISTERED SIGN SERIF
+2310;revlogicalnot;REVERSED NOT SIGN
+03C1;rho;GREEK SMALL LETTER RHO
+02DA;ring;RING ABOVE
+F6F1;rsuperior;SUPERSCRIPT LATIN SMALL LETTER R
+2590;rtblock;RIGHT HALF BLOCK
+F6DD;rupiah;RUPIAH SIGN
+0073;s;LATIN SMALL LETTER S
+015B;sacute;LATIN SMALL LETTER S WITH ACUTE
+0161;scaron;LATIN SMALL LETTER S WITH CARON
+015F;scedilla;LATIN SMALL LETTER S WITH CEDILLA
+F6C2;scedilla;LATIN SMALL LETTER S WITH CEDILLA;Duplicate
+015D;scircumflex;LATIN SMALL LETTER S WITH CIRCUMFLEX
+0219;scommaaccent;LATIN SMALL LETTER S WITH COMMA BELOW
+2033;second;DOUBLE PRIME
+00A7;section;SECTION SIGN
+003B;semicolon;SEMICOLON
+0037;seven;DIGIT SEVEN
+215E;seveneighths;VULGAR FRACTION SEVEN EIGHTHS
+2087;seveninferior;SUBSCRIPT SEVEN
+F737;sevenoldstyle;OLDSTYLE DIGIT SEVEN
+2077;sevensuperior;SUPERSCRIPT SEVEN
+2592;shade;MEDIUM SHADE
+03C3;sigma;GREEK SMALL LETTER SIGMA
+03C2;sigma1;GREEK SMALL LETTER FINAL SIGMA
+223C;similar;TILDE OPERATOR
+0036;six;DIGIT SIX
+2086;sixinferior;SUBSCRIPT SIX
+F736;sixoldstyle;OLDSTYLE DIGIT SIX
+2076;sixsuperior;SUPERSCRIPT SIX
+002F;slash;SOLIDUS
+263A;smileface;WHITE SMILING FACE
+0020;space;SPACE
+00A0;space;NO-BREAK SPACE;Duplicate
+2660;spade;BLACK SPADE SUIT
+F6F2;ssuperior;SUPERSCRIPT LATIN SMALL LETTER S
+00A3;sterling;POUND SIGN
+220B;suchthat;CONTAINS AS MEMBER
+2211;summation;N-ARY SUMMATION
+263C;sun;WHITE SUN WITH RAYS
+0074;t;LATIN SMALL LETTER T
+03C4;tau;GREEK SMALL LETTER TAU
+0167;tbar;LATIN SMALL LETTER T WITH STROKE
+0165;tcaron;LATIN SMALL LETTER T WITH CARON
+0163;tcommaaccent;LATIN SMALL LETTER T WITH CEDILLA
+021B;tcommaaccent;LATIN SMALL LETTER T WITH COMMA BELOW;Duplicate
+2234;therefore;THEREFORE
+03B8;theta;GREEK SMALL LETTER THETA
+03D1;theta1;GREEK THETA SYMBOL
+00FE;thorn;LATIN SMALL LETTER THORN
+0033;three;DIGIT THREE
+215C;threeeighths;VULGAR FRACTION THREE EIGHTHS
+2083;threeinferior;SUBSCRIPT THREE
+F733;threeoldstyle;OLDSTYLE DIGIT THREE
+00BE;threequarters;VULGAR FRACTION THREE QUARTERS
+F6DE;threequartersemdash;THREE QUARTERS EM DASH
+00B3;threesuperior;SUPERSCRIPT THREE
+02DC;tilde;SMALL TILDE
+0303;tildecomb;COMBINING TILDE
+0384;tonos;GREEK TONOS
+2122;trademark;TRADE MARK SIGN
+F8EA;trademarksans;TRADE MARK SIGN SANS SERIF
+F6DB;trademarkserif;TRADE MARK SIGN SERIF
+25BC;triagdn;BLACK DOWN-POINTING TRIANGLE
+25C4;triaglf;BLACK LEFT-POINTING POINTER
+25BA;triagrt;BLACK RIGHT-POINTING POINTER
+25B2;triagup;BLACK UP-POINTING TRIANGLE
+F6F3;tsuperior;SUPERSCRIPT LATIN SMALL LETTER T
+0032;two;DIGIT TWO
+2025;twodotenleader;TWO DOT LEADER
+2082;twoinferior;SUBSCRIPT TWO
+F732;twooldstyle;OLDSTYLE DIGIT TWO
+00B2;twosuperior;SUPERSCRIPT TWO
+2154;twothirds;VULGAR FRACTION TWO THIRDS
+0075;u;LATIN SMALL LETTER U
+00FA;uacute;LATIN SMALL LETTER U WITH ACUTE
+016D;ubreve;LATIN SMALL LETTER U WITH BREVE
+00FB;ucircumflex;LATIN SMALL LETTER U WITH CIRCUMFLEX
+00FC;udieresis;LATIN SMALL LETTER U WITH DIAERESIS
+00F9;ugrave;LATIN SMALL LETTER U WITH GRAVE
+01B0;uhorn;LATIN SMALL LETTER U WITH HORN
+0171;uhungarumlaut;LATIN SMALL LETTER U WITH DOUBLE ACUTE
+016B;umacron;LATIN SMALL LETTER U WITH MACRON
+005F;underscore;LOW LINE
+2017;underscoredbl;DOUBLE LOW LINE
+222A;union;UNION
+2200;universal;FOR ALL
+0173;uogonek;LATIN SMALL LETTER U WITH OGONEK
+2580;upblock;UPPER HALF BLOCK
+03C5;upsilon;GREEK SMALL LETTER UPSILON
+03CB;upsilondieresis;GREEK SMALL LETTER UPSILON WITH DIALYTIKA
+03B0;upsilondieresistonos;GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS
+03CD;upsilontonos;GREEK SMALL LETTER UPSILON WITH TONOS
+016F;uring;LATIN SMALL LETTER U WITH RING ABOVE
+0169;utilde;LATIN SMALL LETTER U WITH TILDE
+0076;v;LATIN SMALL LETTER V
+0077;w;LATIN SMALL LETTER W
+1E83;wacute;LATIN SMALL LETTER W WITH ACUTE
+0175;wcircumflex;LATIN SMALL LETTER W WITH CIRCUMFLEX
+1E85;wdieresis;LATIN SMALL LETTER W WITH DIAERESIS
+2118;weierstrass;SCRIPT CAPITAL P
+1E81;wgrave;LATIN SMALL LETTER W WITH GRAVE
+0078;x;LATIN SMALL LETTER X
+03BE;xi;GREEK SMALL LETTER XI
+0079;y;LATIN SMALL LETTER Y
+00FD;yacute;LATIN SMALL LETTER Y WITH ACUTE
+0177;ycircumflex;LATIN SMALL LETTER Y WITH CIRCUMFLEX
+00FF;ydieresis;LATIN SMALL LETTER Y WITH DIAERESIS
+00A5;yen;YEN SIGN
+1EF3;ygrave;LATIN SMALL LETTER Y WITH GRAVE
+007A;z;LATIN SMALL LETTER Z
+017A;zacute;LATIN SMALL LETTER Z WITH ACUTE
+017E;zcaron;LATIN SMALL LETTER Z WITH CARON
+017C;zdotaccent;LATIN SMALL LETTER Z WITH DOT ABOVE
+0030;zero;DIGIT ZERO
+2080;zeroinferior;SUBSCRIPT ZERO
+F730;zerooldstyle;OLDSTYLE DIGIT ZERO
+2070;zerosuperior;SUPERSCRIPT ZERO
+03B6;zeta;GREEK SMALL LETTER ZETA
diff --git a/program/libraries/afm/test-afm.c b/program/libraries/afm/test-afm.c
--- /dev/null
@@ -0,0 +1,108 @@
+#include "rrd_afm.h"
+#include <stdio.h>
+
+FILE *fp;
+
+static void make_tests(void);
+static void print(const char *s);
+double y = 0;
+//static const char *font = "Times-Roman";
+static const char *font = "Times Bold Italic";
+//static const char *font = "Courier";
+//static const char *font = "Courier Bold Oblique";
+
+void make_tests()
+{
+#ifdef __APPLE__
+#define charset_legend "Macintosh charset"
+#define AE "\xAE"
+#define ae "\xBE"
+#define oe "\xBF"
+#define aa "\x8C"
+#define NBSP "\x00CA"
+#else
+#define charset_legend "IsoLatin1 charset"
+#define AE "\xC6"
+#define ae "\xE6"
+#define oe "\xF8"
+#define aa "\xA5"
+#define NBSP "\x00A0"
+#endif
+ print(AE); /* very wide char */
+ print(AE AE AE AE AE AE AE AE AE AE AE AE AE AE AE);
+ print(charset_legend);
+ print("S,");
+ print("sfil");
+ print("Hello, world");
+ print("AVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAV");
+ print("AAAAAAAAAAAAAAAAAAVVVVVVVVVVVVVVVVVV");
+ print("fiffififfififfififfififfififfififfi");
+ print("fi");
+ print("fil");
+ print("fifififififififififififififififififififififififififi");
+ print(AE "bleskiver med gl"oe"gg. " NBSP NBSP NBSP NBSP NBSP NBSP NBSP
+ AE" Fywerhus: 'A "ae" u "aa" "ae" "oe" i "ae" fywer'.");
+ print("Ingef"ae"rp"ae"rer med karamelsauce. R"oe"dgr"oe"d med fl"oe"de.");
+ print("(Optional.) Ligature sequence where successor and ligature are both names. The current character may join ...");
+}
+
+static void vline(double x, double y1, double y2)
+{
+ fprintf(fp, "<line x1=\"%.2f\" y1=\"%.2f\" x2=\"%.2f\" y2=\"%.2f\""
+ " stroke-width=\"1\" stroke=\"#000\"/>\n",
+ x, y1, x, y2);
+}
+
+static void print(const char *s)
+{
+ double size = 12;
+ double x = 10;
+ double width = afm_get_text_width(0, font, size, 4, s);
+ unsigned char *up = (unsigned char*)s;
+ fprintf(stderr, "Width = %f for '%s'\n", width, s);
+ y += 10;
+ vline(x, y, y + 5);
+ fprintf(fp, "<text x=\"%.2f\" y=\"%.2f\" font-size=\"%.2f\">", x, y, size);
+ while (*up) {
+ unsigned char ch = afm_host2unicode(*up);
+ if (ch < 127)
+ putc(ch, fp);
+ else
+ fprintf(fp, "&#%d;", ch);
+ up++;
+ }
+ fputs("</text>\n", fp);
+ vline(x + width, y, y + 5);
+ y += 1.1 * size;
+}
+
+static void header()
+{
+ fprintf(fp,
+ "<?xml version=\"1.0\" standalone=\"no\"?>\n"
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\"\n"
+ " \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"
+ "<svg width=\"650\" height=\"400\" preserveAspectRatio=\"xMidYMid\"\n"
+ " font-family=\"%s\">\n", font);
+ }
+
+
+static void footer()
+{
+ fputs("</svg>\n", fp);
+}
+
+int main()
+{
+ fp = fopen("test.svg", "w");
+ if (fp == NULL) {
+ fprintf(stderr, "Can't create output.\n");
+ exit(1);
+ }
+ header();
+ make_tests();
+ footer();
+ fclose(fp);
+ return 0;
+}
+
diff --git a/program/netware/Makefile b/program/netware/Makefile
--- /dev/null
+++ b/program/netware/Makefile
@@ -0,0 +1,555 @@
+# Gnu Makefile for NetWare target
+# for use with gcc/nlmconv or Metrowerks CodeWarrior compiler
+# use with: make -f Makefile [help|all|clean|dev|devclean|dist|distclean]
+#
+# $id: $
+#
+
+DESCR = Round Robin Database Tool $(RRD_VERSION_STR)
+COPYR = Copyright (c) 1997-2007 by Tobias Oetiker
+WWWURL = http://www.rrdtool.org/
+MTSAFE = YES
+#SCREEN = $(DESCR)
+STACK = 65535
+# Comment the line below if you dont want to load protected automatically.
+#LDRING = 3
+
+# You can set the default font used in graphs.
+# If not set here RRD defaults to DejaVuSansMono-Roman.ttf
+#RRD_DEFAULT_FONT = "sys:/java/nwgfx/lib/x11/fonts/ttf/tt0003m_.ttf"
+#RRD_DEFAULT_FONT = "VeraMono.ttf"
+
+# Vertical label angle: 90.0 (default) or 270.0
+RRDGRAPH_YLEGEND_ANGLE = 90.0
+
+# Set to one if you want to have piecharts.
+WITH_PIECHART = 0
+
+# Set the extension used for rrdcgi.
+ifndef CGIEXT
+CGIEXT = nlm
+endif
+
+# Edit the path below to point to your Novell NDK.
+ifndef NDKBASE
+NDKBASE = c:/novell
+endif
+
+# Base for the lib sources
+ifndef LIBBASE
+LIBBASE = ../..
+endif
+# All library code is statically linked to avoid problems with other lib NLMs.
+# Edit the path below to point to your libpng sources or set environment var.
+ifndef LIBPNG
+LIBPNG = $(LIBBASE)/libpng-1.2.16
+endif
+# Edit the path below to point to your freetype sources or set environment var.
+ifndef LIBFT2
+LIBFT2 = $(LIBBASE)/freetype-2.3.4
+endif
+# Edit the path below to point to your libart sources or set environment var.
+ifndef LIBART
+LIBART = $(LIBBASE)/libart_lgpl-2.3.17
+endif
+# Edit the path below to point to your zlib sources or set environment var.
+ifndef ZLIBSDK
+ZLIBSDK = $(LIBBASE)/zlib-1.2.3
+endif
+
+# Edit the path below to point to your distribution folder.
+ifndef DISTDIR
+DISTDIR = rrdtool-$(RRD_VERSION_STR)-nw
+endif
+DISTARC = $(DISTDIR).zip
+
+# Edit the path below to point to your distribution folder.
+ifndef DEVLDIR
+DEVLDIR = rrdtool-$(RRD_VERSION_STR)-sdk-nw
+endif
+DEVLARC = $(DEVLDIR).zip
+
+# whatever...
+# NO_NULL_REALLOC = 1
+
+# Edit the var below to point to your lib architecture.
+ifndef LIBARCH
+# LIBARCH = CLIB
+LIBARCH = LIBC
+endif
+
+# The following line defines your compiler.
+ifdef METROWERKS
+ CC = mwccnlm
+else
+ CC = gcc
+endif
+# RM = rm -f
+CP = cp -afv
+# if you want to mark the target as MTSAFE you will need a tool for
+# generating the xdc data for the linker; here's a minimal tool:
+# http://www.gknw.net/development/prgtools/mkxdc.zip
+MPKXDC = mkxdc
+# CodeWarrior is too stupid to set the internal name properly when
+# the extension is not a NLM and not a registered type. So we need
+# to fix that after linking (since CGI isnt a known type - argh!):
+# http://www.gknw.net/development/prgtools/fixnlmname.zip
+FIXNLMN = fixnlmname #-q
+# Here you can find a native Win32 binary of the original awk:
+# http://www.gknw.net/development/prgtools/awk.zip
+AWK = awk
+ZIP = zip -qzr9
+MV = mv -fv
+
+# must be equal to DEBUG or NDEBUG
+DB = NDEBUG
+# DB = DEBUG
+# Optimization: -O<n> or debugging: -g
+ifeq ($(DB),NDEBUG)
+ OPT = -O2
+ OBJDIR = release
+else
+ OPT = -g
+ OBJDIR = debug
+endif
+
+# Project root
+PROOT = ..
+
+# Include the version info retrieved from source.
+-include $(OBJDIR)/version.inc
+
+# Global flags for all compilers
+CFLAGS = $(OPT) -D$(DB) -nostdinc -DNETWARE -DN_PLAT_NLM -D_POSIX_SOURCE
+CFLAGS += -DHAVE_CONFIG_H
+
+ifeq ($(CC),mwccnlm)
+LD = mwldnlm
+LDFLAGS = -nostdlib $^ $(PRELUDE) $(LDLIBS) -o $@ -commandfile
+AR = $(LD)
+ARFLAGS = -nostdlib -type library -o
+LIBEXT = lib
+#RANLIB =
+CFLAGS += -gccinc -inline off -opt nointrinsics -proc 586
+CFLAGS += -relax_pointers
+#CFLAGS += -w on,nounused,nounusedexpr # -ansi strict
+ifeq ($(LIBARCH),LIBC)
+ PRELUDE = $(SDK_LIBC)/imports/libcpre.o
+ CFLAGS += -align 4
+else
+ PRELUDE = "$(METROWERKS)/Novell Support/libraries/runtime/prelude.obj"
+ CFLAGS += -include "$(METROWERKS)/Novell Support/headers/nlm_prefix.h"
+ CFLAGS += -align 1
+endif
+else
+LD = nlmconv
+LDFLAGS = -T
+AR = ar
+ARFLAGS = -cq
+LIBEXT = a
+RANLIB = ranlib
+CFLAGS += -fno-builtin -fpcc-struct-return -fno-strict-aliasing
+CFLAGS += -Wall -Wno-unused # -pedantic
+ifeq ($(LIBARCH),LIBC)
+ PRELUDE = $(SDK_LIBC)/imports/libcpre.gcc.o
+else
+ PRELUDE = $(NDK_ROOT)/pre/prelude.o
+ CFLAGS += -include $(NDKBASE)/nlmconv/genlm.h
+endif
+endif
+
+ifeq ($(findstring linux,$(OSTYPE)),linux)
+#include $(NDKBASE)/nlmconv/ncpfs.inc
+DL = '
+DS = /
+else
+DS = \\
+endif
+
+ifeq ($(MTSAFE),YES)
+ XDCOPT = -n
+endif
+ifeq ($(MTSAFE),NO)
+ XDCOPT = -u
+endif
+ifndef DESCR
+ DESCR = $(notdir $(@:.def=)) Command Extension
+endif
+DESCR += ($(LIBARCH)) - $(CC) build
+
+NDK_ROOT = $(NDKBASE)/ndk
+SDK_CLIB = $(NDK_ROOT)/nwsdk
+SDK_LIBC = $(NDK_ROOT)/libc
+
+INCLUDES += -I$(PROOT) -I$(PROOT)/src -I$(LIBPNG) -I$(LIBFT2)/include -I$(LIBART) -I$(ZLIBSDK)
+
+ifeq ($(LIBARCH),LIBC)
+ INCLUDES += -I$(SDK_LIBC)/include -I$(SDK_LIBC)/include/nks
+else
+ INCLUDES += -I$(SDK_CLIB)/include/nlm -I$(SDK_CLIB)/include
+endif
+
+CFLAGS += $(INCLUDES)
+
+vpath %.c $(PROOT)/src $(LIBPNG) $(LIBART)/libart_lgpl $(ZLIBSDK)
+
+RRDLIBOBJS = \
+ $(OBJDIR)/rrd_afm.o \
+ $(OBJDIR)/rrd_afm_data.o \
+ $(OBJDIR)/rrd_create.o \
+ $(OBJDIR)/rrd_diff.o \
+ $(OBJDIR)/rrd_dump.o \
+ $(OBJDIR)/rrd_error.o \
+ $(OBJDIR)/rrd_fetch.o \
+ $(OBJDIR)/rrd_first.o \
+ $(OBJDIR)/rrd_format.o \
+ $(OBJDIR)/rrd_gfx.o \
+ $(OBJDIR)/rrd_graph.o \
+ $(OBJDIR)/rrd_graph_helper.o \
+ $(OBJDIR)/rrd_hw.o \
+ $(OBJDIR)/rrd_info.o \
+ $(OBJDIR)/rrd_last.o \
+ $(OBJDIR)/rrd_lastupdate.o \
+ $(OBJDIR)/rrd_nan_inf.o \
+ $(OBJDIR)/rrd_open.o \
+ $(OBJDIR)/rrd_resize.o \
+ $(OBJDIR)/rrd_restore.o \
+ $(OBJDIR)/rrd_rpncalc.o \
+ $(OBJDIR)/rrd_tune.o \
+ $(OBJDIR)/rrd_update.o \
+ $(OBJDIR)/rrd_version.o \
+ $(OBJDIR)/rrd_xport.o \
+ $(OBJDIR)/rrd_thread_safe.o \
+ $(EOLIST)
+
+XLIBOBJS = \
+ $(OBJDIR)/rrd_getopt.o \
+ $(OBJDIR)/rrd_getopt1.o \
+ $(OBJDIR)/art_rgba_svp.o \
+ $(OBJDIR)/hash_32.o \
+ $(OBJDIR)/parsetime.o \
+ $(OBJDIR)/pngsize.o \
+ $(EOLIST)
+
+PNGLIBOBJS = \
+ $(OBJDIR)/png.o \
+ $(OBJDIR)/pngerror.o \
+ $(OBJDIR)/pngget.o \
+ $(OBJDIR)/pngmem.o \
+ $(OBJDIR)/pngpread.o \
+ $(OBJDIR)/pngread.o \
+ $(OBJDIR)/pngrio.o \
+ $(OBJDIR)/pngrtran.o \
+ $(OBJDIR)/pngrutil.o \
+ $(OBJDIR)/pngset.o \
+ $(OBJDIR)/pngtrans.o \
+ $(OBJDIR)/pngwio.o \
+ $(OBJDIR)/pngwrite.o \
+ $(OBJDIR)/pngwtran.o \
+ $(OBJDIR)/pngwutil.o \
+ $(EOLIST)
+ifeq "$(wildcard $(LIBPNG)/pnggccrd.c)" "$(LIBPNG)/pnggccrd.c"
+PNGLIBOBJS += \
+ $(OBJDIR)/pnggccrd.o \
+ $(OBJDIR)/pngvcrd.o \
+ $(EOLIST)
+endif
+
+ZLIBOBJS = \
+ $(OBJDIR)/adler32.o \
+ $(OBJDIR)/compress.o \
+ $(OBJDIR)/crc32.o \
+ $(OBJDIR)/deflate.o \
+ $(OBJDIR)/inflate.o \
+ $(OBJDIR)/inffast.o \
+ $(OBJDIR)/inftrees.o \
+ $(OBJDIR)/trees.o \
+ $(OBJDIR)/zutil.o \
+ $(EOLIST)
+ifeq "$(wildcard $(ZLIBSDK)/infblock.c)" "$(ZLIBSDK)/infblock.c"
+ZLIBOBJS += \
+ $(OBJDIR)/infblock.o \
+ $(OBJDIR)/infcodes.o \
+ $(OBJDIR)/infutil.o \
+ $(EOLIST)
+endif
+
+ARTLIBOBJS = \
+ $(patsubst $(LIBART)/libart_lgpl/%.c,$(OBJDIR)/%.o,$(wildcard $(LIBART)/libart_lgpl/art_*.c))
+
+OBJS := $(RRDLIBOBJS) $(XLIBOBJS) $(PNGLIBOBJS) $(ARTLIBOBJS) $(ZLIBOBJS)
+OBJCGI := $(OBJS) $(OBJDIR)/rrd_cgi.o
+OBJTOOL := $(OBJS) $(OBJDIR)/rrd_tool.o
+
+LDLIBS += $(LIBFT2)/builds/netware/libc/libft2.$(LIBEXT)
+
+
+all: rrdtool rrdcgi
+
+rrdtool: $(OBJDIR) $(PROOT)/rrd_config.h $(OBJDIR)/rrdtool.nlm
+rrdcgi: $(OBJDIR) $(PROOT)/rrd_config.h $(OBJDIR)/rrdcgi.$(CGIEXT)
+librrd: $(OBJDIR) $(PROOT)/rrd_config.h $(OBJDIR)/librrd.$(LIBEXT)
+
+FORCE: ;
+
+dist: all $(DISTDIR) $(DISTDIR)/readme.txt
+ @-$(CP) $(OBJDIR)/rrdcgi.$(CGIEXT) $(DISTDIR)
+ @-$(CP) $(OBJDIR)/rrdtool.nlm $(DISTDIR)
+ @-$(CP) $(PROOT)/src/*.ttf $(DISTDIR)
+ @-$(CP) $(PROOT)/CHANGES $(DISTDIR)
+ @-$(CP) $(PROOT)/COPYING $(DISTDIR)
+ @-$(CP) $(PROOT)/COPYRIGHT $(DISTDIR)
+ @-$(CP) $(PROOT)/NEWS $(DISTDIR)
+ @-$(CP) $(PROOT)/README $(DISTDIR)
+ @echo Creating $(DISTARC)
+ @$(ZIP) $(DISTARC) $(DISTDIR)/* < $(DISTDIR)/readme.txt
+
+dev: librrd $(DEVLDIR) $(DEVLDIR)/readme.txt
+ @-mkdir $(DEVLDIR)$(DS)include
+ @-mkdir $(DEVLDIR)$(DS)lib
+ @-mkdir $(DEVLDIR)$(DS)src
+ @-$(CP) $(OBJDIR)/librrd.$(LIBEXT) $(DEVLDIR)/lib
+ @-$(CP) $(PROOT)/rrd_config.h $(DEVLDIR)/include
+ @-$(CP) $(PROOT)/src/rrd.h $(DEVLDIR)/include
+ @-$(CP) $(PROOT)/src/*.ttf $(DEVLDIR)/src
+ @-$(CP) $(PROOT)/CHANGES $(DEVLDIR)
+ @-$(CP) $(PROOT)/COPYING $(DEVLDIR)
+ @-$(CP) $(PROOT)/COPYRIGHT $(DEVLDIR)
+ @-$(CP) $(PROOT)/NEWS $(DEVLDIR)
+ @-$(CP) $(PROOT)/README $(DEVLDIR)
+ @echo Creating $(DEVLARC)
+ @$(ZIP) $(DEVLARC) $(DEVLDIR)/* < $(DEVLDIR)/readme.txt
+
+clean:
+ -$(RM) -r $(OBJDIR)
+ -$(RM) $(PROOT)/rrd_config.h
+
+distclean: clean
+ -$(RM) -r $(DISTDIR)
+ -$(RM) $(DISTARC)
+
+devclean: clean
+ -$(RM) -r $(DEVLDIR)
+ -$(RM) $(DEVLARC)
+
+$(OBJDIR):
+ @mkdir $@
+
+$(DISTDIR):
+ @mkdir $@
+
+$(DEVLDIR):
+ @mkdir $@
+
+$(OBJDIR)/version.inc: $(PROOT)/configure.ac $(OBJDIR) $(PROOT)/src/get_ver.awk
+ @echo Creating $@
+ @$(AWK) -f $(PROOT)/src/get_ver.awk $< > $@
+
+$(OBJDIR)/%.o: %.c
+ @echo Compiling $<
+ @$(CC) $(CFLAGS) -c $< -o $@
+
+$(OBJDIR)/rrdcgi.$(CGIEXT): $(OBJCGI) $(OBJDIR)/rrdcgi.def $(OBJDIR)/rrdcgi.xdc
+ @echo Linking $@
+ @-$(RM) $@
+ @$(LD) $(LDFLAGS) $(@:.$(CGIEXT)=.def)
+ifeq ($(LD),nlmconv)
+ @$(MV) $(notdir $@) $@
+endif
+ifneq ($(CGIEXT),nlm)
+ifeq ($(LD),mwldnlm)
+ @$(FIXNLMN) $@
+endif
+endif
+
+$(OBJDIR)/rrdtool.nlm: $(OBJTOOL) $(OBJDIR)/rrdtool.def $(OBJDIR)/rrdtool.xdc
+ @echo Linking $@
+ @-$(RM) $@
+ @$(LD) $(LDFLAGS) $(@:.nlm=.def)
+ifeq ($(LD),nlmconv)
+ @$(MV) $(notdir $@) $@
+endif
+
+$(OBJDIR)/librrd.$(LIBEXT): $(OBJS)
+ @echo Creating $@
+ @-$(RM) $@
+ @$(AR) $(ARFLAGS) $@ $^
+ifdef RANLIB
+ @$(RANLIB) $@
+endif
+
+$(OBJDIR)/%.xdc: Makefile
+ @echo Creating $@
+ @$(MPKXDC) $(XDCOPT) $@
+
+$(OBJDIR)/%.def: Makefile $(OBJDIR)/version.inc
+ @echo $(DL)# DEF Linker File for use with gcc and nlmconv$(DL) > $@
+ @echo $(DL)# or with Codewarrior command line compiler.$(DL) >> $@
+ @echo $(DL)# Do not edit this file - it is created by make!$(DL) >> $@
+ @echo $(DL)# All your changes will be lost!!$(DL) >> $@
+ @echo $(DL)#$(DL) >> $@
+ @echo $(DL)copyright "$(COPYR)"$(DL) >> $@
+ @echo $(DL)description "$(DESCR)"$(DL) >> $@
+ @echo $(DL)version $(RRD_VERSION)$(DL) >> $@
+ifdef NLMTYPE
+ @echo $(DL)type $(NLMTYPE)$(DL) >> $@
+else
+ @echo $(DL)type 0$(DL) >> $@
+endif
+ifdef STACK
+ @echo $(DL)stack $(STACK)$(DL) >> $@
+endif
+ @echo $(DL)threadname "$(notdir $(@:.def=))"$(DL) >> $@
+ifdef SCREEN
+ @echo $(DL)screenname "$(SCREEN)"$(DL) >> $@
+else
+ @echo $(DL)screenname "DEFAULT"$(DL) >> $@
+endif
+ifeq ($(DB),DEBUG)
+ @echo $(DL)debug$(DL) >> $@
+endif
+ifeq ($(LIBARCH),CLIB)
+ @echo $(DL)start _Prelude$(DL) >> $@
+ @echo $(DL)exit _Stop$(DL) >> $@
+ @echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/clib.imp$(DL) >> $@
+ @echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/threads.imp$(DL) >> $@
+ @echo $(DL)import @$(NDK_ROOT)/nwsdk/imports/nlmlib.imp$(DL) >> $@
+ @echo $(DL)module clib$(DL) >> $@
+else
+ @echo $(DL)start _LibCPrelude$(DL) >> $@
+ @echo $(DL)exit _LibCPostlude$(DL) >> $@
+ @echo $(DL)check _LibCCheckUnload$(DL) >> $@
+ @echo $(DL)import @$(NDK_ROOT)/libc/imports/libc.imp$(DL) >> $@
+ @echo $(DL)import @$(NDK_ROOT)/libc/imports/netware.imp$(DL) >> $@
+ @echo $(DL)module libc$(DL) >> $@
+ @echo $(DL)pseudopreemption$(DL) >> $@
+ @echo $(DL)flag_on 64$(DL) >> $@
+endif
+ifeq ($(LDRING),0)
+ @echo $(DL)flag_on 16$(DL) >> $@
+endif
+ifeq ($(LDRING),3)
+ @echo $(DL)flag_on 512$(DL) >> $@
+endif
+ifdef XDCOPT
+ @echo $(DL)xdcdata $(@:.def=.xdc)$(DL) >> $@
+endif
+ifeq ($(LD),nlmconv)
+ @echo $(DL)input $(OBJS) $(LDLIBS)$(DL) >> $@
+ @echo $(DL)input $(PRELUDE)$(DL) >> $@
+ @echo $(DL)output $(notdir $(@:.def=.nlm))$(DL) >> $@
+endif
+
+$(PROOT)/rrd_config.h: FORCE Makefile $(OBJDIR)/version.inc
+ @echo Creating $@
+ @echo $(DL)/* $(notdir $@) for NetWare target.$(DL) > $@
+ @echo $(DL)** Do not edit this file - it is created by make!$(DL) >> $@
+ @echo $(DL)** All your changes will be lost!!$(DL) >> $@
+ @echo $(DL)*/$(DL) >> $@
+ @echo $(DL)#ifndef NETWARE$(DL) >> $@
+ @echo $(DL)#error This $(notdir $@) is created for NetWare platform!$(DL) >> $@
+ @echo $(DL)#endif$(DL) >> $@
+ @echo $(DL)#ifndef RRD_CONFIG_H$(DL) >> $@
+ @echo $(DL)#define RRD_CONFIG_H$(DL) >> $@
+ @echo $(DL)#define OS "i586-pc-NetWare"$(DL) >> $@
+ @echo $(DL)#define PACKAGE_VERSION "$(RRD_VERSION_STR)"$(DL) >> $@
+ @echo $(DL)#define PACKAGE_BUGREPORT "tobi@oetiker.ch"$(DL) >> $@
+ @echo $(DL)#define NUMVERS $(RRD_NUMVERS)$(DL) >> $@
+ @echo $(DL)#define HAVE_ASSERT_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_DLFCN_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_DLOPEN 1$(DL) >> $@
+ @echo $(DL)#define HAVE_ERR_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_ERRNO_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_FCNTL_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_FIONBIO 1$(DL) >> $@
+ @echo $(DL)#define HAVE_FLOAT_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_GETTIMEOFDAY 1$(DL) >> $@
+ @echo $(DL)#define HAVE_INTTYPES_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_LIMITS_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_LONGLONG 1$(DL) >> $@
+ @echo $(DL)#define HAVE_LOCALE_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_MALLOC_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_MATH_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_MBSTOWCS 1$(DL) >> $@
+ @echo $(DL)#define HAVE_MEMMOVE 1$(DL) >> $@
+ @echo $(DL)#define HAVE_MKTIME 1$(DL) >> $@
+ @echo $(DL)#define HAVE_SELECT 1$(DL) >> $@
+ @echo $(DL)#define HAVE_SETLOCALE 1$(DL) >> $@
+ @echo $(DL)#define HAVE_SETJMP_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_SNPRINTF 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STDARG_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STDDEF_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STDINT_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STDLIB_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STRCASECMP 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STRDUP 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STRFTIME 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STRING_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STRLCAT 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STRLCPY 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STRSTR 1$(DL) >> $@
+ @echo $(DL)#define HAVE_SYS_PARAM_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_SYS_SELECT_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_SYS_STAT_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_SYS_TIME_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_SYS_TYPES_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_TIME_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_TZSET 1$(DL) >> $@
+ @echo $(DL)#define HAVE_UNAME 1$(DL) >> $@
+ @echo $(DL)#define HAVE_VSNPRINTF 1$(DL) >> $@
+ @echo $(DL)#define STDC_HEADERS 1$(DL) >> $@
+ @echo $(DL)#define TIME_WITH_SYS_TIME 1$(DL) >> $@
+ @echo $(DL)#define HAVE_ZLIB_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_LIBZ 1$(DL) >> $@
+ifdef NO_NULL_REALLOC
+ @echo $(DL)#define NO_NULL_REALLOC 1$(DL) >> $@
+ @echo $(DL)#define rrd_realloc(a,b) ( (a) == NULL ? malloc( (b) ) : realloc( (a) , (b) ))$(DL) >> $@
+else
+ @echo $(DL)#define rrd_realloc(a,b) realloc((a), (b))$(DL) >> $@
+endif
+ifdef RRD_DEFAULT_FONT
+ @echo $(DL)#define RRD_DEFAULT_FONT $(RRD_DEFAULT_FONT)$(DL) >> $@
+endif
+ @echo $(DL)#define RRDGRAPH_YLEGEND_ANGLE $(RRDGRAPH_YLEGEND_ANGLE)$(DL) >> $@
+ifdef WITH_PIECHART
+ @echo $(DL)#define WITH_PIECHART $(WITH_PIECHART)$(DL) >> $@
+endif
+ @echo $(DL)#endif /* RRD_CONFIG_H */$(DL) >> $@
+
+$(DISTDIR)/readme.txt: Makefile
+ @echo Creating $@
+ @echo $(DL)This is a binary distribution for NetWare platform.$(DL) > $@
+ @echo $(DL)RRDTool version $(RRD_VERSION_STR)$(DL) >> $@
+ @echo $(DL)Please download the complete RRDTool package for$(DL) >> $@
+ @echo $(DL)any further documentation:$(DL) >> $@
+ @echo $(DL)$(WWWURL)$(DL) >> $@
+
+$(DEVLDIR)/readme.txt: Makefile
+ @echo Creating $@
+ @echo $(DL)This is a development distribution for NetWare platform.$(DL) > $@
+ @echo $(DL)RRDTool version $(RRD_VERSION_STR)$(DL) >> $@
+ @echo $(DL)Please download the complete RRDTool package for$(DL) >> $@
+ @echo $(DL)any further documentation:$(DL) >> $@
+ @echo $(DL)$(WWWURL)$(DL) >> $@
+
+help:
+ @echo $(DL)===========================================================$(DL)
+ @echo $(DL)Novell NDK Base = $(NDKBASE)$(DL)
+ @echo $(DL)libpng Source = $(LIBPNG)$(DL)
+ @echo $(DL)libart Source = $(LIBART)$(DL)
+ @echo $(DL)Freetype 2 SDK = $(LIBFT2)$(DL)
+ @echo $(DL)Zlib SDK = $(ZLIBSDK)$(DL)
+ @echo $(DL)===========================================================$(DL)
+ @echo $(DL)RRDTool $(RRD_VERSION_STR) - available targets are:$(DL)
+ @echo $(DL)$(MAKE) all$(DL)
+ @echo $(DL)$(MAKE) rrdtool$(DL)
+ @echo $(DL)$(MAKE) rrdcgi$(DL)
+ @echo $(DL)$(MAKE) librrd$(DL)
+ @echo $(DL)$(MAKE) clean$(DL)
+ @echo $(DL)$(MAKE) dev$(DL)
+ @echo $(DL)$(MAKE) devclean$(DL)
+ @echo $(DL)$(MAKE) dist$(DL)
+ @echo $(DL)$(MAKE) distclean$(DL)
+ @echo $(DL)===========================================================$(DL)
+
+
diff --git a/program/rrdtool-1.2-release b/program/rrdtool-1.2-release
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+set -e
+VERSION=`perl -n -e 'm/\QAC_INIT([rrdtool],[\E(.+?)\Q])\E/ && print $1' configure.ac`
+PERLVERS=`perl -n -e 'm/NUMVERS=(\d+\.\d+)/ && print $1' configure.ac`
+set -x
+perl -i -p -e 's/^\$VERSION.+/\$VERSION='$PERLVERS';/' bindings/perl-*/*.pm
+perl -i -p -e 's/RRDtool 1\S+/RRDtool '$VERSION'/ && s/Copyright.+?Oetiker.+\d{4}/Copyright by Tobi Oetiker, 1997-2007/' src/*.h src/*.c
+perl -i -p -e 's/^Version:.+/Version: '$VERSION'/' rrdtool.spec
+perl -i -p -e 's/rrdtool-[\.\d]+\d(pre\d+)?(rc\d+)?/rrdtool-'$VERSION'/g' doc/rrdbuild.pod
+svn diff
+echo "Tagging and releasing rrdtool $VERSION ($PERLVERS). Press any Key to continue."
+read somekey
+svn commit -m "prepare for the release of rrdtool-$VERSION"
+mkdir /tmp/rrdtool-$$
+OPWD=`pwd`
+cd /tmp/rrdtool-$$
+svn checkout svn://svn.oetiker.ch/rrdtool/branches/1.2/program .
+svn log --stop-on-copy --xml --verbose svn://svn.oetiker.ch/rrdtool/branches/1.2/program | \
+ xsltproc --stringparam strip-prefix branches/1.2/program $OPWD/svn2cl.xsl - >CHANGES
+sh MakeMakefile
+PKG_CONFIG_PATH=/usr/pack/rrdtool-1.2svn-to/i686-debian-linux3.1/lib/pkgconfig/
+export PKG_CONFIG_PATH
+./configure
+make dist
+# do a test build
+tar zxvf rrdtool*.tar.gz
+cd rrdtool-$VERSION
+./configure
+make
+src/rrdtool
+cd ..
+scp CHANGES rrdtool*.tar.gz oposs@james:public_html/rrdtool/pub/
+ssh oposs@james "cd public_html/rrdtool/pub/;rm rrdtool.tar.gz;ln -s rrdtool-$VERSION.tar.gz rrdtool.tar.gz"
+cd ..
+rm -rf rrdtool-$$
+svn copy -m "tagging version $VERSION" svn://svn.oetiker.ch/rrdtool/branches/1.2/program svn://svn.oetiker.ch/rrdtool/tags/$VERSION
+
diff --git a/program/rrdtool.spec b/program/rrdtool.spec
--- /dev/null
+++ b/program/rrdtool.spec
@@ -0,0 +1,273 @@
+Summary: Round Robin Database Tool to store and display time-series data
+Name: rrdtool
+Version: 1.2.23
+Release: 3%{?dist}
+License: GPL
+Group: Applications/Databases
+URL: http://oss.oetiker.ch/%{name}/
+Source: http://oss.oetiker.ch/%{name}/pub/%{name}-%{version}.tar.gz
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+BuildRequires: gcc-c++, openssl-devel
+BuildRequires: libpng-devel, zlib-devel, libart_lgpl-devel >= 2.0
+BuildRequires: freetype-devel, python-devel >= 2.3
+Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version))
+
+%{!?python_sitearch: %define python_sitearch %(%{__python} -c 'from distutils import sysconfig; print sysconfig.get_python_lib(1)')}
+%{!?python_version: %define python_version %(%{__python} -c 'import sys; print sys.version.split(" ")[0]')}
+
+%description
+RRD is the Acronym for Round Robin Database. RRD is a system to store and
+display time-series data (i.e. network bandwidth, machine-room temperature,
+server load average). It stores the data in a very compact way that will not
+expand over time, and it presents useful graphs by processing the data to
+enforce a certain data density. It can be used either via simple wrapper
+scripts (from shell or Perl) or via frontends that poll network devices and
+put a friendly user interface on it.
+
+%package devel
+Summary: RRDtool libraries and header files
+Group: Development/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description devel
+RRD is the Acronym for Round Robin Database. RRD is a system to store and
+display time-series data (i.e. network bandwidth, machine-room temperature,
+server load average). This package allow you to use directly this library.
+
+%package doc
+Summary: RRDtool documentation
+Group: Documentation
+
+%description doc
+RRD is the Acronym for Round Robin Database. RRD is a system to store and
+display time-series data (i.e. network bandwidth, machine-room temperature,
+server load average). This package contains documentation on using RRD.
+
+%package -n perl-%{name}
+Summary: Perl RRDtool bindings
+Group: Development/Languages
+Requires: %{name} = %{version}-%{release}
+Obsoletes: %{name}-perl <= %{version}
+Provides: %{name}-perl = %{version}
+
+%description -n perl-%{name}
+The Perl RRDtool bindings
+
+%package -n python-%{name}
+Summary: Python RRDtool bindings
+Group: Development/Languages
+BuildRequires: python
+Requires: python >= %{python_version}
+Requires: %{name} = %{version}-%{release}
+
+%description -n python-%{name}
+Python RRDtool bindings.
+
+%prep
+%setup
+
+# Fix to find correct python dir on lib64
+%{__perl} -pi -e 's|get_python_lib\(0,0,prefix|get_python_lib\(1,0,prefix|g' \
+ configure
+
+# Shouldn't be necessary when using --libdir, but
+# introduces hardcoded rpaths where it shouldn't,
+# if not done...
+%{__perl} -pi.orig -e 's|/lib\b|/%{_lib}|g' \
+ configure Makefile.in
+
+%build
+%configure \
+ --program-prefix="%{?_program_prefix}" \
+ --libdir=%{_libdir} \
+ --disable-static \
+ --with-pic \
+ --with-perl-options='INSTALLDIRS="vendor"'
+
+# Fix another rpath issue
+%{__perl} -pi.orig -e 's|-Wl,--rpath -Wl,\$rp||g' \
+ bindings/perl-shared/Makefile.PL
+
+# Force RRDp bits where we want 'em, not sure yet why the
+# --with-perl-options and --libdir don't take
+pushd bindings/perl-piped/
+%{__perl} Makefile.PL INSTALLDIRS=vendor
+%{__perl} -pi.orig -e 's|/lib/perl|/%{_lib}/perl|g' Makefile
+popd
+
+%{__make} %{?_smp_mflags}
+
+# Fix @perl@ and @PERL@
+find examples/ -type f \
+ -exec %{__perl} -pi -e 's|^#! \@perl\@|#!%{__perl}|gi' {} \;
+find examples/ -name "*.pl" \
+ -exec %{__perl} -pi -e 's|\015||gi' {} \;
+
+%install
+rm -rf $RPM_BUILD_ROOT
+make DESTDIR="$RPM_BUILD_ROOT" install
+
+# Pesky RRDp.pm...
+%{__mv} $RPM_BUILD_ROOT%{perl_vendorarch}/../RRDp.pm $RPM_BUILD_ROOT%{perl_vendorarch}/
+
+# We only want .txt and .html files for the main documentation
+%{__mkdir_p} doc2/html doc2/txt
+%{__cp} -a doc/*.txt doc2/txt/
+%{__cp} -a doc/*.html doc2/html/
+
+# Put perl docs in perl package
+%{__mkdir_p} doc3/html
+%{__mv} doc2/html/RRD*.html doc3/html/
+
+# Clean up the examples
+%{__rm} -f examples/Makefile* examples/*.in
+
+# This is so rpm doesn't pick up perl module dependencies automatically
+find examples/ -type f -exec chmod 0644 {} \;
+
+# Clean up the buildroot
+%{__rm} -rf $RPM_BUILD_ROOT%{_datadir}/doc/%{name}-%{version}/{txt,html}/ \
+ $RPM_BUILD_ROOT%{perl_vendorarch}/ntmake.pl \
+ $RPM_BUILD_ROOT%{perl_archlib}/perllocal.pod \
+ $RPM_BUILD_ROOT%{_datadir}/%{name}/examples \
+ $RPM_BUILD_ROOT%{perl_vendorarch}/auto/*/{.packlist,*.bs}
+
+%clean
+%{__rm} -rf $RPM_BUILD_ROOT
+
+%post -p /sbin/ldconfig
+
+%postun -p /sbin/ldconfig
+
+%files
+%{_bindir}/*
+%{_libdir}/*.so.*
+%{_libdir}/rrdtool/*
+%{_datadir}/%{name}/fonts/*
+%{_mandir}/man1/*
+
+%files devel
+%defattr(-,root,root,-)
+%{_includedir}/*.h
+%exclude %{_libdir}/*.la
+%{_libdir}/*.so
+
+%files doc
+%defattr(-,root,root,-)
+%doc CHANGES CONTRIBUTORS COPYING COPYRIGHT README TODO NEWS THREADS doc2/html doc2/txt
+%doc examples
+
+%files -n perl-%{name}
+%defattr(-,root,root,-)
+%doc doc3/html
+%{_mandir}/man3/*
+%{perl_vendorarch}/*.pm
+%attr(0755,root,root) %{perl_vendorarch}/auto/RRDs/*
+
+%files -n python-%{name}
+%defattr(-,root,root,-)
+%doc bindings/python/AUTHORS bindings/python/COPYING bindings/python/README
+%{python_sitearch}/rrdtoolmodule.so
+
+%changelog
+* Mon Jun 05 2006 Jarod Wilson <jwilson@redhat.com> 1.2.13-3
+- Merge spec fixes from bz 185909
+
+* Sun Jun 04 2006 Jarod Wilson <jwilson@redhat.com> 1.2.13-2
+- Remove explicit perl dep, version grabbing using rpm during
+ rpmbuild not guaranteed to work (fails on ppc in plague),
+ and auto-gen perl deps are sufficient
+
+* Sat Jun 03 2006 Jarod Wilson <jwilson@redhat.com> 1.2.13-1
+- Update to release 1.2.13
+- Merge spec changes from dag, atrpms and mdk builds
+- Additional hacktastic contortions for lib64 & rpath messiness
+- Add missing post/postun ldconfig
+- Fix a bunch of rpmlint errors
+- Disable static libs, per FE guidelines
+- Split off docs
+
+* Wed Apr 19 2006 Chris Ricker <kaboom@oobleck.net> 1.2.12-1
+- Rev to 1.2
+
+* Fri May 20 2005 Matthias Saou <http://freshrpms.net/> 1.0.49-5
+- Include patch from Michael to fix perl module compilation on FC4 (#156242).
+
+* Fri May 20 2005 Matthias Saou <http://freshrpms.net/> 1.0.49-4
+- Fix for the php module patch (Joe Pruett, Dag Wieers), #156716.
+- Update source URL to new location since 1.2 is now the default stable.
+- Don't (yet) update to 1.0.50, as it introduces some changes in the perl
+ modules install.
+
+* Mon Jan 31 2005 Matthias Saou <http://freshrpms.net/> 1.0.49-3
+- Put perl modules in vendor_perl and not site_perl. #146513
+
+* Thu Jan 13 2005 Matthias Saou <http://freshrpms.net/> 1.0.49-2
+- Minor cleanups.
+
+* Thu Aug 25 2004 Dag Wieers <dag@wieers.com> - 1.0.49-1
+- Updated to release 1.0.49.
+
+* Wed Aug 25 2004 Dag Wieers <dag@wieers.com> - 1.0.48-3
+- Fixes for x86_64. (Garrick Staples)
+
+* Fri Jul 2 2004 Matthias Saou <http://freshrpms.net/> 1.0.48-3
+- Actually apply the patch for fixing the php module, doh!
+
+* Thu May 27 2004 Matthias Saou <http://freshrpms.net/> 1.0.48-2
+- Added php.d config entry to load the module once installed.
+
+* Thu May 13 2004 Dag Wieers <dag@wieers.com> - 1.0.48-1
+- Updated to release 1.0.48.
+
+* Tue Apr 06 2004 Dag Wieers <dag@wieers.com> - 1.0.47-1
+- Updated to release 1.0.47.
+
+* Thu Mar 4 2004 Matthias Saou <http://freshrpms.net/> 1.0.46-2
+- Change the strict dependency on perl to fix problem with the recent
+ update.
+
+* Mon Jan 5 2004 Matthias Saou <http://freshrpms.net/> 1.0.46-1
+- Update to 1.0.46.
+- Use system libpng and zlib instead of bundled ones.
+- Added php-rrdtool sub-package for the php4 module.
+
+* Fri Dec 5 2003 Matthias Saou <http://freshrpms.net/> 1.0.45-4
+- Added epoch to the perl dependency to work with rpm > 4.2.
+- Fixed the %% escaping in the perl dep.
+
+* Mon Nov 17 2003 Matthias Saou <http://freshrpms.net/> 1.0.45-2
+- Rebuild for Fedora Core 1.
+
+* Sun Aug 3 2003 Matthias Saou <http://freshrpms.net/>
+- Update to 1.0.45.
+
+* Wed Apr 16 2003 Matthias Saou <http://freshrpms.net/>
+- Update to 1.0.42.
+
+* Mon Mar 31 2003 Matthias Saou <http://freshrpms.net/>
+- Rebuilt for Red Hat Linux 9.
+
+* Wed Mar 5 2003 Matthias Saou <http://freshrpms.net/>
+- Added explicit perl version dependency.
+
+* Sun Feb 23 2003 Matthias Saou <http://freshrpms.net/>
+- Update to 1.0.41.
+
+* Fri Jan 31 2003 Matthias Saou <http://freshrpms.net/>
+- Update to 1.0.40.
+- Spec file cleanup.
+
+* Fri Jul 05 2002 Henri Gomez <hgomez@users.sourceforge.net>
+- 1.0.39
+
+* Mon Jun 03 2002 Henri Gomez <hgomez@users.sourceforge.net>
+- 1.0.38
+
+* Fri Apr 19 2002 Henri Gomez <hgomez@users.sourceforge.net>
+- 1.0.37
+
+* Tue Mar 12 2002 Henri Gomez <hgomez@users.sourceforge.net>
+- 1.0.34
+- rrdtools include zlib 1.1.4 which fix vulnerabilities in 1.1.3
+
diff --git a/program/src/DejaVuSansMono-Roman.ttf b/program/src/DejaVuSansMono-Roman.ttf
new file mode 100644 (file)
index 0000000..691c8d6
Binary files /dev/null and b/program/src/DejaVuSansMono-Roman.ttf differ
index 0000000..691c8d6
Binary files /dev/null and b/program/src/DejaVuSansMono-Roman.ttf differ
diff --git a/program/src/Makefile.am b/program/src/Makefile.am
--- /dev/null
+++ b/program/src/Makefile.am
@@ -0,0 +1,134 @@
+## Process this file with automake to produce Makefile.in
+
+#AUTOMAKE_OPTIONS = foreign
+#
+#ACLOCAL_M4 = $(top_srcdir)/config/aclocal.m4
+#AUTOHEADER = @AUTOHEADER@ --localdir=$(top_srcdir)/config
+fontsdir = $(datadir)/rrdtool/fonts
+fonts_DATA = DejaVuSansMono-Roman.ttf
+
+#INCLUDES = $(FREETYPE_INCLUDES) $(ART_INCLUDES) \
+# $(PNG_INCLUDES) $(ZLIB_INCLUDES)
+RRD_DEFAULT_FONT=@RRD_DEFAULT_FONT@
+AM_CPPFLAGS = -DRRD_DEFAULT_FONT=\"$(RRD_DEFAULT_FONT)\" -DNUMVERS=@NUMVERS@
+
+UPD_C_FILES = \
+ rrd_getopt.c \
+ rrd_getopt1.c \
+ parsetime.c \
+ rrd_hw.c \
+ rrd_diff.c \
+ rrd_format.c \
+ rrd_info.c \
+ rrd_error.c \
+ rrd_open.c \
+ rrd_nan_inf.c \
+ rrd_rpncalc.c \
+ rrd_update.c
+
+RRD_C_FILES = \
+ hash_32.c \
+ pngsize.c \
+ rrd_create.c \
+ rrd_dump.c \
+ rrd_fetch.c \
+ rrd_graph.c \
+ rrd_graph_helper.c \
+ rrd_last.c \
+ rrd_lastupdate.c \
+ rrd_first.c \
+ rrd_resize.c \
+ rrd_restore.c \
+ rrd_tune.c \
+ rrd_version.c \
+ rrd_xport.c \
+ art_rgba_svp.c \
+ rrd_gfx.c \
+ rrd_afm.c rrd_afm_data.c \
+ rrd_tool.c
+
+noinst_HEADERS = \
+ art_rgba_svp.h \
+ unused.h \
+ rrd_gfx.h \
+ rrd_getopt.h parsetime.h \
+ rrd_format.h rrd_tool.h rrd_xport.h rrd.h rrd_hw.h rrd_rpncalc.h \
+ rrd_nan_inf.h fnv.h rrd_graph.h rrd_afm.h rrd_afm_data.h \
+ rrd_is_thread_safe.h
+
+noinst_LTLIBRARIES = librrdupd.la
+
+lib_LTLIBRARIES = librrd.la
+if BUILD_MULTITHREAD
+lib_LTLIBRARIES += librrd_th.la
+endif
+
+librrdupd_la_SOURCES = $(UPD_C_FILES) rrd_not_thread_safe.c
+librrdupd_la_LIBADD = $(CORE_LIBS)
+
+librrd_la_SOURCES = $(RRD_C_FILES)
+librrd_la_LIBADD = librrdupd.la $(ALL_LIBS)
+
+# This flag accepts an argument of the form current[:revision[:age]]. So,
+# passing -version-info 3:12:1 sets current to 3, revision to 12, and age to 1.
+#
+# If either revision or age are omitted, they default to 0. Also note that
+# age must be less than or equal to the current interface number.
+#
+# Here are a set of rules to help you update your library version information:
+#
+# 1. Start with version information of 0:0:0 for each libtool library.
+#
+# 2. Update the version information only immediately before a public
+# release of your software. More frequent updates are unnecessary, and
+# only guarantee that the current interface number gets larger faster.
+#
+# 3. If the library source code has changed at all since the last update,
+# then increment revision (c:r:a becomes c:r+1:a).
+#
+# 4. If any interfaces have been added, removed, or changed since the last
+# update, increment current, and set revision to 0.
+#
+# 5. If any interfaces have been added since the last public release, then
+# increment age.
+#
+# 6. If any interfaces have been removed since the last public release,
+# then set age to 0.
+#
+# Never try to set the interface numbers so that they correspond to the
+# release number of your package. This is an abuse that only fosters
+# misunderstanding of the purpose of library versions. Instead, use the
+# -release flag (see Release numbers), but be warned that every release of
+# your package will not be binary compatible with any other release.
+#
+# see http://www.gnu.org/software/libtool/manual.html#SEC32 for explanation
+librrd_la_LDFLAGS = -version-info 2:11:0
+
+librrd_th_la_SOURCES = $(UPD_C_FILES) $(RRD_C_FILES) rrd_thread_safe.c
+librrd_th_la_CFLAGS = $(MULTITHREAD_CFLAGS)
+librrd_th_la_LDFLAGS = $(MULTITHREAD_LDFLAGS) -version-info 2:11:0
+librrd_th_la_LIBADD = $(ALL_LIBS)
+
+include_HEADERS = rrd.h
+
+bin_PROGRAMS = rrdtool rrdupdate
+
+if BUILD_RRDCGI
+bin_PROGRAMS += rrdcgi
+endif
+
+rrdcgi_SOURCES = rrd_cgi.c
+rrdcgi_LDADD = librrd.la
+
+rrdupdate_SOURCES = rrdupdate.c
+rrdupdate_LDADD = librrdupd.la
+
+rrdtool_SOURCES =
+rrdtool_DEPENDENCIES = rrd_tool.o librrd.la
+rrdtool_LDADD = librrd.la
+
+# strftime is here because we do not usually need it. unices have propper
+# iso date support
+EXTRA_DIST= strftime.c strftime.h $(fonts_DATA) \
+ win32comp.c rrd_thread_safe_nt.c get_ver.awk
+
diff --git a/program/src/VeraMono.ttf b/program/src/VeraMono.ttf
new file mode 100644 (file)
index 0000000..139f0b4
Binary files /dev/null and b/program/src/VeraMono.ttf differ
index 0000000..139f0b4
Binary files /dev/null and b/program/src/VeraMono.ttf differ
diff --git a/program/src/art_rgba_svp.c b/program/src/art_rgba_svp.c
--- /dev/null
@@ -0,0 +1,333 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * art_rgba_svp.c: A slightly modified version of art_rgb_svp to render into rgba buffer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Authors:
+ * Raph Levien <raph@acm.org>
+ * Lauris Kaplinski <lauris@ariman.ee>
+ *
+ * Copyright (C) 1998 Raph Levien
+ *
+ */
+
+#define SP_ART_RGBA_SVP_C
+
+/* Render a sorted vector path into an RGBA buffer. */
+
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_render_aa.h>
+#include <libart_lgpl/art_rgb.h>
+
+#include "art_rgba_svp.h"
+#include "unused.h"
+
+static void art_rgba_fill_run (art_u8 * linebuf, art_u8 r, art_u8 g, art_u8 b, int n);
+static void art_rgba_run_alpha (art_u8 * linebuf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n);
+
+typedef struct _ArtRgbaSVPAlphaData ArtRgbaSVPAlphaData;
+
+struct _ArtRgbaSVPAlphaData {
+ int alphatab[256];
+ art_u8 r, g, b, alpha;
+ art_u8 *buf;
+ int rowstride;
+ int libart_x0, libart_x1;
+};
+
+static void
+art_rgba_svp_alpha_callback (void *callback_data, int UNUSED(y),
+ int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+ ArtRgbaSVPAlphaData *data = callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int libart_x0, libart_x1;
+ int k;
+ art_u8 r, g, b;
+ int *alphatab;
+ int alpha;
+
+ linebuf = data->buf;
+ libart_x0 = data->libart_x0;
+ libart_x1 = data->libart_x1;
+
+ r = data->r;
+ g = data->g;
+ b = data->b;
+ alphatab = data->alphatab;
+
+ if (n_steps > 0)
+ {
+ run_x1 = steps[0].x;
+ if (run_x1 > libart_x0)
+ {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgba_run_alpha (linebuf,
+ r, g, b, alphatab[alpha],
+ run_x1 - libart_x0);
+ }
+
+ /* render the steps into tmpbuf */
+ for (k = 0; k < n_steps - 1; k++)
+ {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0)
+ {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgba_run_alpha (linebuf + (run_x0 - libart_x0) * 4,
+ r, g, b, alphatab[alpha],
+ run_x1 - run_x0);
+ }
+ }
+ running_sum += steps[k].delta;
+ if (libart_x1 > run_x1)
+ {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgba_run_alpha (linebuf + (run_x1 - libart_x0) * 4,
+ r, g, b, alphatab[alpha],
+ libart_x1 - run_x1);
+ }
+ }
+ else
+ {
+ alpha = (running_sum >> 16) & 0xff;
+ if (alpha)
+ art_rgba_run_alpha (linebuf,
+ r, g, b, alphatab[alpha],
+ libart_x1 - libart_x0);
+ }
+
+ data->buf += data->rowstride;
+}
+
+static void
+art_rgba_svp_alpha_opaque_callback (void *callback_data, int UNUSED(y),
+ int start,
+ ArtSVPRenderAAStep *steps, int n_steps)
+{
+ ArtRgbaSVPAlphaData *data = callback_data;
+ art_u8 *linebuf;
+ int run_x0, run_x1;
+ art_u32 running_sum = start;
+ int libart_x0, libart_x1;
+ int k;
+ art_u8 r, g, b;
+ int *alphatab;
+ int alpha;
+
+ linebuf = data->buf;
+ libart_x0 = data->libart_x0;
+ libart_x1 = data->libart_x1;
+
+ r = data->r;
+ g = data->g;
+ b = data->b;
+ alphatab = data->alphatab;
+
+ if (n_steps > 0)
+ {
+ run_x1 = steps[0].x;
+ if (run_x1 > libart_x0)
+ {
+ alpha = running_sum >> 16;
+ if (alpha)
+ {
+ if (alpha >= 255)
+ art_rgba_fill_run (linebuf,
+ r, g, b,
+ run_x1 - libart_x0);
+ else
+ art_rgba_run_alpha (linebuf,
+ r, g, b, alphatab[alpha],
+ run_x1 - libart_x0);
+ }
+ }
+
+ /* render the steps into tmpbuf */
+ for (k = 0; k < n_steps - 1; k++)
+ {
+ running_sum += steps[k].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[k + 1].x;
+ if (run_x1 > run_x0)
+ {
+ alpha = running_sum >> 16;
+ if (alpha)
+ {
+ if (alpha >= 255)
+ art_rgba_fill_run (linebuf + (run_x0 - libart_x0) * 4,
+ r, g, b,
+ run_x1 - run_x0);
+ else
+ art_rgba_run_alpha (linebuf + (run_x0 - libart_x0) * 4,
+ r, g, b, alphatab[alpha],
+ run_x1 - run_x0);
+ }
+ }
+ }
+ running_sum += steps[k].delta;
+ if (libart_x1 > run_x1)
+ {
+ alpha = running_sum >> 16;
+ if (alpha)
+ {
+ if (alpha >= 255)
+ art_rgba_fill_run (linebuf + (run_x1 - libart_x0) * 4,
+ r, g, b,
+ libart_x1 - run_x1);
+ else
+ art_rgba_run_alpha (linebuf + (run_x1 - libart_x0) * 4,
+ r, g, b, alphatab[alpha],
+ libart_x1 - run_x1);
+ }
+ }
+ }
+ else
+ {
+ alpha = running_sum >> 16;
+ if (alpha)
+ {
+ if (alpha >= 255)
+ art_rgba_fill_run (linebuf,
+ r, g, b,
+ libart_x1 - libart_x0);
+ else
+ art_rgba_run_alpha (linebuf,
+ r, g, b, alphatab[alpha],
+ libart_x1 - libart_x0);
+ }
+ }
+
+ data->buf += data->rowstride;
+}
+
+/**
+ * gnome_print_art_rgba_svp_alpha: Alpha-composite sorted vector path over RGBA buffer.
+ * @svp: The source sorted vector path.
+ * @libart_x0: Left coordinate of destination rectangle.
+ * @libart_y0: Top coordinate of destination rectangle.
+ * @libart_x1: Right coordinate of destination rectangle.
+ * @libart_y1: Bottom coordinate of destination rectangle.
+ * @rgba: Color in 0xRRGGBBAA format.
+ * @buf: Destination RGB buffer.
+ * @rowstride: Rowstride of @buf buffer.
+ * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
+ *
+ * Renders the shape specified with @svp over the @buf RGB buffer.
+ * @libart_x1 - @x0 specifies the width, and @libart_y1 - @libart_y0 specifies the height,
+ * of the rectangle rendered. The new pixels are stored starting at
+ * the first byte of @buf. Thus, the @x0 and @libart_y0 parameters specify
+ * an offset within @svp, and may be tweaked as a way of doing
+ * integer-pixel translations without fiddling with @svp itself.
+ *
+ * The @rgba argument specifies the color for the rendering. Pixels of
+ * entirely 0 winding number are left untouched. Pixels of entirely
+ * 1 winding number have the color @rgba composited over them (ie,
+ * are replaced by the red, green, blue components of @rgba if the alpha
+ * component is 0xff). Pixels of intermediate coverage are interpolated
+ * according to the rule in @alphagamma, or default to linear if
+ * @alphagamma is NULL.
+ **/
+void
+gnome_print_art_rgba_svp_alpha (const ArtSVP *svp,
+ int libart_x0, int libart_y0, int libart_x1, int libart_y1,
+ art_u32 rgba,
+ art_u8 *buf, int rowstride,
+ ArtAlphaGamma UNUSED(*alphagamma))
+{
+ ArtRgbaSVPAlphaData data;
+ int r, g, b, alpha;
+ int i;
+ int a, da;
+
+ r = rgba >> 24;
+ g = (rgba >> 16) & 0xff;
+ b = (rgba >> 8) & 0xff;
+ alpha = rgba & 0xff;
+
+ data.r = r;
+ data.g = g;
+ data.b = b;
+ data.alpha = alpha;
+
+ a = 0x8000;
+ da = (alpha * 66051 + 0x80) >> 8; /* 66051 equals 2 ^ 32 / (255 * 255) */
+
+ for (i = 0; i < 256; i++)
+ {
+ data.alphatab[i] = a >> 16;
+ a += da;
+ }
+
+ data.buf = buf;
+ data.rowstride = rowstride;
+ data.libart_x0 = libart_x0;
+ data.libart_x1 = libart_x1;
+ if (alpha == 255)
+ art_svp_render_aa (svp, libart_x0, libart_y0, libart_x1, libart_y1, art_rgba_svp_alpha_opaque_callback,
+ &data);
+ else
+ art_svp_render_aa (svp, libart_x0, libart_y0, libart_x1, libart_y1, art_rgba_svp_alpha_callback, &data);
+}
+
+static void
+art_rgba_fill_run (art_u8 * buf, art_u8 r, art_u8 g, art_u8 b, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++) {
+ * buf++ = r;
+ * buf++ = g;
+ * buf++ = b;
+ * buf++ = 255;
+ }
+}
+
+/* fixme: this */
+
+static void
+art_rgba_run_alpha (art_u8 * buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n)
+{
+ int i;
+ int br, bg, bb, ba;
+ int cr, cg, cb;
+
+ for (i = 0; i < n; i++) {
+ br = * (buf + 0);
+ bg = * (buf + 1);
+ bb = * (buf + 2);
+ ba = * (buf + 3);
+
+ cr = (br * ba + 0x80) >> 8;
+ cg = (bg * ba + 0x80) >> 8;
+ cb = (bb * ba + 0x80) >> 8;
+
+ * buf++ = cr + (((r - cr) * alpha + 0x80) >> 8);
+ * buf++ = cg + (((g - cg) * alpha + 0x80) >> 8);
+ * buf++ = cb + (((b - cb) * alpha + 0x80) >> 8);
+ * buf++ = ba + (((255 - ba) * alpha + 0x80) >> 8);
+ }
+}
+
+
diff --git a/program/src/art_rgba_svp.h b/program/src/art_rgba_svp.h
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef SP_ART_RGBA_SVP_H
+#define SP_ART_RGBA_SVP_H
+
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_alphagamma.h>
+#include <libart_lgpl/art_affine.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_uta.h>
+
+void
+gnome_print_art_rgba_svp_alpha (const ArtSVP *svp,
+ int x0, int y0, int x1, int y1,
+ art_u32 rgba,
+ art_u8 *buf, int rowstride,
+ ArtAlphaGamma *alphagamma);
+
+#endif /* SP_ART_RGBA_SVP_H */
diff --git a/program/src/compile_afm.pl b/program/src/compile_afm.pl
--- /dev/null
@@ -0,0 +1,479 @@
+#!/usr/local/bin/perl -w
+
+require 5.005;
+use strict;
+
+# The glyps list can be downloaded from
+# http://partners.adobe.com/asn/developer/type/glyphlist.txt
+# This URL is from this page:
+# http://partners.adobe.com/asn/developer/type/unicodegn.html
+# which is refered from
+# http://partners.adobe.com/asn/developer/technotes/fonts.html
+
+my $onlyHelvetica = 0;
+
+my %globalName2Unicode;
+my %font_code = ();
+
+my $indent0 = "";
+my $indent1 = " ";
+my $indent2 = $indent1 x 3;
+
+my $q = 0;
+my $qU = 0;
+
+sub read_glyphlist
+{
+ my $fn ="glyphlist.txt";
+ open(FH, $fn)
+ || die "Can't read $fn\n";
+ my %seen = ();
+ while (<FH>) {
+ next if /^\s*#/;
+ next unless /^([0-9A-F]{4});(\w+);/;
+ my $unicode = 0 + hex($1);
+ my $name = $2;
+ next if ($globalName2Unicode{$name});
+ $globalName2Unicode{$name} = $unicode;
+ }
+ close(FH);
+}
+
+sub process_all_fonts
+{
+ my $dir = ".";
+ my $wc = "*.afm";
+ $wc = "Helvetica.afm" if $onlyHelvetica;
+ $wc = "ZapfDin.afm" if 0;
+ $wc = "Helve*.afm" if 0;
+ $wc = "Times-BoldItalic.afm" if 0;
+ foreach my $fn (glob("$dir/$wc")) {
+ process_font($fn);
+ }
+}
+
+sub process_font
+{
+ my ($fn) = @_;
+ print STDERR "Compiling afm file: $fn\n";
+ my %fi = (); # font info
+ my $c = "";
+ $fi{C} = \$c;
+ $fi{ligaturesR} = {};
+ $fi{FontSpecificUnicodeNameToChar} = {};
+ $fi{filename} = $fn;
+ $fi{filename} =~ s/.*\///;
+ open(FH, $fn) || die "Can't open $fn\n";
+ print STDERR "Reads global font info\n" if $q;
+ while (<FH>) {
+ chomp;
+ next if /^\s*$/ || /^\s*#/;
+ last if /^StartCharMetrics/;
+ next unless (/^(\S+)\s+(\S(.*\S)?)/);
+ my $id = $1;
+ my $value = $2;
+ $value =~ s/\s+/ /g;
+ $fi{"Afm$id"} = $value;
+ }
+ my $fontName = $fi{AfmFontName};
+ $c .= "\n\n/* ". ("-" x 66) . "*/\n";
+ $c .= "/* FontName: $fontName */\n";
+ $c .= "/* FullName: $fi{AfmFullName} */\n";
+ $c .= "/* FamilyName: $fi{AfmFamilyName} */\n";
+ $fi{cName} = $fontName;
+ $fi{cName} =~ s/\W/_/g;
+ my %charMetrics = ();
+ my %kerning = ();
+ read_charmetrics(\%fi, \%charMetrics);
+ while (<FH>) {
+ read_kerning(\%fi, \%kerning) if /^StartKernPairs/;
+ }
+ if (0) {
+ my @names = keys %charMetrics;
+ print STDERR "Did read ", ($#names + 1), " font metrics\n";
+ }
+ write_font(\%fi, \%charMetrics, \%kerning);
+}
+
+sub read_charmetrics
+{
+ my ($fiR, $charMetricsR) = @_;
+ print STDERR "Reads char metric info\n" if $q;
+ my $isZapfDingbats = $$fiR{AfmFontName} eq "ZapfDingbats";
+ my $ligaturesR = $$fiR{ligaturesR};
+ my %ligatures = ();
+ my %seenUnicodes = ();
+ while (<FH>) {
+ chomp;
+ next if /^\s*$/ || /^\s*#/;
+ last if /^EndCharMetrics/;
+#next unless /N S / || /N comma /;
+#next unless /N ([sfil]|fi) /;
+#print "$_\n";
+ my $line = $_;
+# C 102 ; WX 333 ; N f ; B -169 -205 446 698 ; L i fi ; L l fl ;
+ my ($width, $unicode, $name, @charLigatures);
+ foreach (split/\s*;\s*/, $line) {
+ if (/^C\s+(-?\d+)/) {
+ $unicode = 0 + $1;
+ } elsif (/^N\s+(\w+)/) {
+ $name = $1;
+ } elsif (/^WX?\s+(-?\d+)/) {
+ $width = normalize_width($1, 0);
+ } elsif (/^L\s+(\w+)\s+(\w+)/) {
+ push(@charLigatures, $1, $2);
+ }
+ }
+ if ($unicode < 0) {
+ unless (defined $name) {
+ print STDERR "Glyph missing name and code: $_\n";
+ next;
+ }
+ $unicode = name2uni($fiR, $name);
+ print STDERR "name2uni: $name -> $unicode\n" if $qU && 0;
+ } elsif (defined $name) {
+ my $std = $globalName2Unicode{$name};
+ if (!defined $std) {
+ print STDERR "Adds unicode mapping: ",
+ "$name -> $unicode\n" if $qU;
+ ${$$fiR{FontSpecificUnicodeNameToChar}}{$name} = $unicode;
+ } else {
+ $unicode = $std;
+ }
+ }
+ if (!defined($unicode) || $unicode <= 0) {
+ next if $isZapfDingbats && $name =~ /^a(\d+)$/;
+ next if $$fiR{AfmFontName} eq "Symbol" && $name eq "apple";
+ print STDERR "Glyph '$name' has unknown unicode: $_\n";
+ next;
+ }
+ unless (defined $width) {
+ print STDERR "Glyph '$name' missing width: $_\n";
+ next;
+ }
+ if ($seenUnicodes{$unicode}) {
+ print STDERR "Duplicate character: unicode = $unicode, ",
+ "$name and ", $seenUnicodes{$unicode},
+ " (might be due to Adobe charset remapping)\n";
+ next;
+ }
+ $seenUnicodes{$unicode} = $name;
+ my %c = ();
+ $c{name} = $name;
+ $c{unicode} = $unicode;
+ $c{width} = $width;
+ $$charMetricsR{$unicode} = \%c;
+ $ligatures{$unicode} = \@charLigatures if $#charLigatures >= 0;
+ }
+ foreach my $unicode (keys %ligatures) {
+ my $aR = $ligatures{$unicode};
+ my $unicode2 = name2uni($fiR, $$aR[0]);
+ my $unicode3 = name2uni($fiR, $$aR[1]);
+ unless ($unicode2) {
+ print STDERR "Missing ligature char 1: $$aR[0]\n";
+ next;
+ }
+ unless ($unicode3) {
+ print STDERR "Missing ligature char 2: $$aR[1]\n";
+ next;
+ }
+ my $key = sprintf("%04d;%04d", $unicode, $unicode2);
+ $$ligaturesR{$key} = $unicode3;
+ }
+}
+
+sub name2uni
+{
+ my ($fiR, $name) = @_;
+ my $fontMapR = $$fiR{FontSpecificUnicodeNameToChar};
+ return $globalName2Unicode{$name} || $$fontMapR{$name};
+}
+
+sub read_kerning
+{
+ my ($fiR, $kerningR) = @_;
+ print STDERR "Reads kerning info\n" if $q;
+ while (<FH>) {
+ chomp;
+ next if /^\s*$/ || /^\s*#/;
+ last if /^EndKernPairs/;
+ unless (/^KPX\s+(\w+)\s+(\w+)\s+(-?\d+)\s*$/) {
+ print STDERR "Can't parse kern spec: $_\n";
+ next;
+ }
+ my $name1 = $1;
+ my $name2 = $2;
+ my $delta = normalize_width($3, 1);
+ next unless $delta;
+ my $unicode1 = name2uni($fiR, $name1);
+ my $unicode2 = name2uni($fiR, $name2);
+ unless ($unicode1 && $unicode2) {
+ print "Unknown kern pair: $name1 and $name2\n";
+ next;
+ }
+ my $charR = $$kerningR{$unicode1};
+ unless (defined $charR) {
+ my %c = ();
+ $charR = \%c;
+ $$kerningR{$unicode1} = $charR;
+ }
+ $$charR{$unicode2} = $delta;
+ }
+}
+
+sub write_font
+{
+ my ($fiR, $charMetricsR, $kerningR) = @_;
+ print STDERR "Writes font\n" if $q;
+ my $cR = $$fiR{C};
+ $$fiR{widthsA} = make_array();
+ $$fiR{kerning_indexA} = make_array();
+ $$fiR{kerning_dataA} = make_array();
+ $$fiR{highchars_indexA} = make_array();
+ $$fiR{ligaturesA} = make_array();
+ write_font_metrics($fiR, $charMetricsR, $kerningR);
+ write_ligatures($fiR);
+ my $widths_count = array_size($$fiR{widthsA});
+ my $kerning_index_count = array_size($$fiR{kerning_indexA});
+ my $kerning_data_count = array_size($$fiR{kerning_dataA});
+ my $highchars_count = array_size($$fiR{highchars_indexA});
+ my $ligatures_count = array_size($$fiR{ligaturesA}) / 3;
+ my $info_code = "";
+ my $i2 = $indent2;
+ my $packedSize = $widths_count + 2 * $kerning_index_count +
+ $kerning_data_count + 2 * $highchars_count +
+ 3 * 2 * $ligatures_count;
+ $info_code .= $indent1 . "{ /* $$fiR{filename} $packedSize bytes */\n";
+ $info_code .= $i2 . "\"$$fiR{AfmFontName}\",";
+ $info_code .= " \"$$fiR{AfmFullName}\",\n";
+ $info_code .= $i2 . $$fiR{widthsACName} . ",\n";
+ $info_code .= $i2 . $$fiR{kerning_indexACName} . ",\n";
+ $info_code .= $i2 . $$fiR{kerning_dataACName} . ",\n";
+ $info_code .= $i2 . $$fiR{highchars_indexACName} . ", ";
+ $info_code .= $highchars_count . ",\n";
+ $info_code .= $i2 . $$fiR{ligaturesACName} . ", ";
+ $info_code .= $ligatures_count;
+ $info_code .= "},\n";
+ $font_code{$$fiR{AfmFullName}} = { TABLES => $$cR, INFO => $info_code};
+}
+
+sub write_font_metrics
+{
+ my ($fiR, $charMetricsR, $kerningR) = @_;
+ print STDERR "Writes font metrics\n" if $q;
+ my $lastUnicode = 31;
+ my $cR = $$fiR{C};
+ my $widthsA = $$fiR{widthsA};
+ my $kerning_indexA = $$fiR{kerning_indexA};
+ my $kerning_dataA = $$fiR{kerning_dataA};
+ my $highchars_indexA = $$fiR{highchars_indexA};
+ my @uniArray = sort { $a <=> $b } keys %$charMetricsR;
+ my $highchars_count = 0;
+ my $had_kerning = 0;
+ while (1) {
+ my $fill = 0;
+ if ($#uniArray < 0) {
+ last if $lastUnicode > 126;
+ $fill = 1;
+ } elsif ($lastUnicode < 126 && $uniArray[0] > $lastUnicode + 1) {
+ $fill = 1;
+ }
+ if ($fill) {
+ $lastUnicode++;
+#print STDERR "fill for $lastUnicode, $#uniArray, $uniArray[0]\n";
+ append_to_array($widthsA, 0);
+ append_to_array($kerning_indexA, 0);
+ next;
+ }
+ my $unicode = shift @uniArray;
+ next if $unicode < 32;
+ $lastUnicode = $unicode;
+ my $metricsR = $$charMetricsR{$unicode};
+ if ($unicode > 126) {
+ append_to_array($highchars_indexA, $unicode);
+ $highchars_count++;
+ }
+ my $m = $$metricsR{width};
+ $m = "/* ".array_size($widthsA)."=$unicode */". $m if 0;
+ append_to_array($widthsA, $m);
+ my $kerningInfoR = $$kerningR{$unicode};
+ my $kerning_index = 0;
+ if (defined $kerningInfoR) {
+ my @kerns = ();
+ foreach my $unicode2 (sort { $a <=> $b } keys %$kerningInfoR) {
+ my $delta = $$kerningInfoR{$unicode2};
+ append_escaped_16bit_int(\@kerns, $unicode2);
+ push(@kerns, $delta);
+ $had_kerning = 1;
+ }
+ $kerning_index = append_8bit_subarray($kerning_dataA, 2, @kerns);
+ }
+ append_to_array($kerning_indexA, $kerning_index);
+ }
+ $$fiR{kerning_indexA} = make_array() if !$had_kerning;
+ write_array($fiR, "widths", "afm_cuint8");
+ write_array($fiR, "kerning_index", "afm_sint16");
+ write_array($fiR, "kerning_data", "afm_cuint8");
+ write_array($fiR, "highchars_index", "afm_cuint16");
+}
+
+sub write_ligatures
+{
+ my ($fiR) = @_;
+ print STDERR "Writes font ligatures\n" if $q;
+ my $ligaturesA = $$fiR{ligaturesA};
+ my $ligaturesR = $$fiR{ligaturesR};
+ foreach (sort keys %$ligaturesR) {
+ unless (/^(\w{4});(\w{4})$/) {
+ die "Invalid ligature key: $_";
+ }
+ append_to_array($ligaturesA, $1 + 0, $2 + 0, $$ligaturesR{$_});
+ }
+ write_array($fiR, "ligatures", "afm_cunicode");
+}
+
+sub indent
+{
+ my ($num) = @_;
+ return " " x $num;
+}
+
+sub make_array
+{
+ my @a = ();
+ return \@a;
+}
+
+sub append_to_array
+{
+ my ($aR, @newElements) = @_;
+ my $z1 = array_size($aR);
+ push(@$aR, @newElements);
+ my $z2 = array_size($aR);
+ my $zz = $#newElements +1;
+}
+
+sub append_8bit_subarray
+{
+ my ($aR, $elementsPerItem, @newElements) = @_;
+ push(@$aR, 42) if !array_size($aR); # initial dummy value
+ my $idx = $#{$aR} + 1;
+#print "append_8bit_subarray ", ($#newElements+1), " = (", join(", ", @newElements), ") -> $idx\n";
+ append_escaped_16bit_int($aR, ($#newElements + 1) / $elementsPerItem);
+ push(@$aR, @newElements);
+ die "Can't handle that big sub array, sorry...\n" if $idx > 50000;
+ return $idx;
+}
+
+sub append_escaped_16bit_int
+{
+ my ($aR, $count) = @_;
+ die "Invalid count = 0\n" unless $count;
+ if ($count >= 510) {
+ push(@$aR, 1, int($count / 256), int($count % 256));
+ print STDERR "full: $count\n" if 0;
+ } elsif ($count >= 254) {
+ push(@$aR, 0, $count - 254);
+ print STDERR "semi: $count\n" if 0;
+ } else {
+ push(@$aR, $count + 1);
+ }
+}
+
+sub array_size
+{
+ my ($aR) = @_;
+ return $#{$aR} + 1;
+}
+
+sub write_array
+{
+ my ($fiR, $name, $type) = @_;
+ my $aR = $$fiR{$name."A"};
+ my $cName = $$fiR{cName};
+ my $num = $#{$aR} + 1;
+ my $array_name_key = $name."ACName";
+ if ($num == 0) {
+ $$fiR{$array_name_key} = "NULL";
+ return;
+ }
+ my $cR = $$fiR{C};
+ my $array_name = "afm_" . $cName . "_" . $name;
+ $$fiR{$array_name_key} = $array_name;
+ $$cR .= "static $type $array_name" . "[] = { /* $num */\n";
+ my $line = $indent1;
+ for (my $i = 0; $i < $num; $i++) {
+ $line .= "," if $i > 0;
+ if (length($line) > 65) {
+ $line .= "\n";
+ $$cR .= $line;
+ $line = $indent1;
+ }
+ $line .= $$aR[$i];
+ }
+ $line .= "\n";
+ $$cR .= $line;
+ $$cR .= "};\n";
+}
+
+sub normalize_width
+{
+ my ($w, $signed) = @_;
+ my $n = int(($w + 3) / 6);
+ if ($signed) {
+ $n = -128 if $n < -128;
+ $n = 127 if $n > 127;
+ $n = 256 + $n if $n < 0; # make unsigned.
+ } else {
+ $n = 0 if $n < 0;
+ $n = 255 if $n > 255;
+ }
+ return $n;
+}
+
+sub main
+{
+ my $cfn = "../../src/rrd_afm_data.c";
+ read_glyphlist();
+ process_all_fonts();
+ my @fonts = sort keys %font_code;
+ unless ($#fonts >= 0) {
+ die "You must have at least 1 font.\n";
+ }
+ open(CFILE, ">$cfn") || die "Can't create $cfn\n";
+ print CFILE header($cfn);
+ print CFILE ${$font_code{$_}}{TABLES} foreach @fonts;
+ print CFILE "const afm_fontinfo afm_fontinfolist[] = {\n";
+ print CFILE ${$font_code{$_}}{INFO} foreach @fonts;
+ print CFILE $indent1 . "{ 0, 0, 0 }\n";
+ print CFILE $indent0 . "};\n";
+ print CFILE $indent0 . "const int afm_fontinfo_count = ",
+ ($#fonts + 1), ";\n";
+ close(CFILE);
+ print STDERR "Compiled ", ($#fonts+1), " fonts.\n";
+}
+
+sub header
+{
+ my ($fn) = @_;
+ $fn =~ s/.*\///;
+ my $h = $fn;
+ $h =~ s/\.c$/.h/;
+ return <<"END";
+/****************************************************************************
+ * RRDtool 1.1.x Copyright Tobias Oetiker, 1997 - 2002
+ ****************************************************************************
+ * $fn Encoded afm (Adobe Font Metrics) for selected fonts.
+ ****************************************************************************
+ *
+ * THIS FILE IS AUTOGENERATED BY PERL. DO NOT EDIT.
+ *
+ ****************************************************************************/
+
+#include "$h"
+#include <stdlib.h>
+
+END
+}
+
+main();
diff --git a/program/src/fnv.h b/program/src/fnv.h
--- /dev/null
+++ b/program/src/fnv.h
@@ -0,0 +1,108 @@
+/*
+ * fnv - Fowler/Noll/Vo- hash code
+ *
+ * @(#) $Revision$
+ * @(#) $Id$
+ * @(#) $Source$
+ *
+ ***
+ *
+ * Fowler/Noll/Vo- hash
+ *
+ * The basis of this hash algorithm was taken from an idea sent
+ * as reviewer comments to the IEEE POSIX P1003.2 committee by:
+ *
+ * Phong Vo (http://www.research.att.com/info/kpv/)
+ * Glenn Fowler (http://www.research.att.com/~gsf/)
+ *
+ * In a subsequent ballot round:
+ *
+ * Landon Curt Noll (http://reality.sgi.com/chongo/)
+ *
+ * improved on their algorithm. Some people tried this hash
+ * and found that it worked rather well. In an EMail message
+ * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.
+ *
+ * FNV hashes are architected to be fast while maintaining a low
+ * collision rate. The FNV speed allows one to quickly hash lots
+ * of data while maintaining a reasonable collision rate. See:
+ *
+ * http://reality.sgi.com/chongo/tech/comp/fnv/
+ *
+ * for more details as well as other forms of the FNV hash.
+ *
+ ***
+ *
+ * NOTE: The FNV-0 historic hash is not recommended. One should use
+ * the FNV-1 hash instead.
+ *
+ * To use the 32 bit FNV-0 historic hash, pass FNV0_32_INIT as the
+ * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str().
+ *
+ * To use the 64 bit FNV-0 historic hash, pass FNV0_64_INIT as the
+ * Fnv64_t hashval argument to fnv_64_buf() or fnv_64_str().
+ *
+ * To use the recommended 32 bit FNV-1 hash, pass FNV1_32_INIT as the
+ * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str().
+ *
+ * To use the recommended 64 bit FNV-1 hash, pass FNV1_64_INIT as the
+ * Fnv64_t hashval argument to fnv_64_buf() or fnv_64_str().
+ *
+ ***
+ *
+ * Please do not copyright this code. This code is in the public domain.
+ *
+ * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
+ * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * By:
+ * chongo <Landon Curt Noll> /\oo/\
+ * http://reality.sgi.com/chongo/
+ * EMail: chongo_fnv at prime dot engr dot sgi dot com
+ *
+ * Share and Enjoy! :-)
+ */
+
+#if !defined(__FNV_H__)
+#define __FNV_H__
+
+
+/*
+ * 32 bit FNV-0 hash type
+ */
+typedef unsigned long Fnv32_t;
+
+
+/*
+ * 32 bit FNV-0 zero initial basis
+ *
+ * This historic hash is not recommended. One should use
+ * the FNV-1 hash and inital basis instead.
+ */
+#define FNV0_32_INIT ((Fnv32_t)0)
+
+
+/*
+ * 32 bit FNV-1 non-zero initial basis
+ *
+ * The FNV-1 initial basis is the FNV-0 hash of the following 32 octets:
+ *
+ * chongo <Landon Curt Noll> /\../\
+ *
+ * Note that the \'s above are not back-slashing escape characters.
+ * They are literal ASCII backslash 0x5c characters.
+ */
+#define FNV1_32_INIT ((Fnv32_t)0x811c9dc5)
+
+Fnv32_t fnv_32_buf(const void *, size_t, Fnv32_t);
+
+Fnv32_t fnv_32_str(const char *, Fnv32_t );
+
+unsigned long FnvHash(const char *);
+
+#endif /* __FNV_H__ */
diff --git a/program/src/gdpng.c b/program/src/gdpng.c
--- /dev/null
+++ b/program/src/gdpng.c
@@ -0,0 +1,71 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * gdpng.c add PNG output routine to gd library
+ *****************************************************************************/
+
+#include <png.h>
+#include <gd.h>
+#include <stdlib.h>
+
+typedef struct _jmpbuf_wrapper {
+ jmp_buf jmpbuf;
+} jmpbuf_wrapper;
+
+static jmpbuf_wrapper gdPngJmpbufStruct;
+
+void gdImagePng(gdImagePtr im, FILE *out)
+{
+ int i;
+ png_colorp palette;
+ png_structp png_write_ptr =
+ png_create_write_struct(PNG_LIBPNG_VER_STRING,
+ (png_voidp)NULL,
+ /* we would need to point to error handlers
+ here to do it properly */
+ (png_error_ptr)NULL, (png_error_ptr)NULL);
+ png_infop info_ptr = png_create_info_struct(png_write_ptr);
+
+ if (setjmp(gdPngJmpbufStruct.jmpbuf)) {
+ png_destroy_write_struct(&png_write_ptr, &info_ptr);
+ return;
+ }
+
+ palette = (png_colorp)png_malloc (png_write_ptr,
+ im->colorsTotal*sizeof(png_color));
+ if (palette == NULL){
+ png_destroy_write_struct(&png_write_ptr, &info_ptr);
+ return;
+ }
+
+
+ png_init_io(png_write_ptr, out);
+ png_set_write_status_fn(png_write_ptr, NULL);
+ png_set_IHDR(png_write_ptr,info_ptr,
+ im->sx,im->sy,im->colorsTotal > 16 ? 8:4,
+ PNG_COLOR_TYPE_PALETTE,
+ im->interlace ? PNG_INTERLACE_ADAM7: PNG_INTERLACE_NONE ,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+ for(i=0;i<im->colorsTotal;i++){
+ palette[i].red = im->red[i];
+ palette[i].green = im->green[i];
+ palette[i].blue = im->blue[i];
+ }
+ png_set_PLTE(png_write_ptr, info_ptr, palette, im->colorsTotal);
+
+ /* choose between speed (1) and space (9) optimisation */
+ /* we want to be fast ... */
+ png_set_compression_level(png_write_ptr,1);
+ png_set_filter(png_write_ptr,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
+ /* store file info */
+ png_write_info(png_write_ptr, info_ptr);
+ png_set_packing(png_write_ptr);
+ png_write_image(png_write_ptr, im->pixels);
+ png_write_end(png_write_ptr, info_ptr);
+ png_free(png_write_ptr, palette);
+ png_destroy_write_struct(&png_write_ptr, &info_ptr);
+}
+
+
+
+
diff --git a/program/src/get_ver.awk b/program/src/get_ver.awk
--- /dev/null
+++ b/program/src/get_ver.awk
@@ -0,0 +1,40 @@
+# ****************************************************************************
+# RRDtool 1.2.19 Copyright by Tobi Oetiker, 1997-2007
+# ****************************************************************************
+# get_ver.awk AWK Script for non-configure builds
+# ****************************************************************************
+# $Id: get_ver.awk 1000 2007-14-02 05:51:34Z oetiker $
+# ****************************************************************************
+BEGIN {
+ # fetch rrdtool version number from input file and write them to STDOUT
+ while ((getline < ARGV[1]) > 0) {
+ if (match ($0, /^AC_INIT/)) {
+ split($1, t, ",");
+ my_ver_str = substr(t[2],2,length(t[2])-3);
+ split(my_ver_str, v, ".");
+ gsub("[^0-9].*$", "", v[3]);
+ my_ver = v[1] "," v[2] "," v[3];
+ }
+ if (match ($0, /^NUMVERS=/)) {
+ split($1, t, "=");
+ my_ver_num = t[2];
+ }
+ }
+ # read from from input file, replace placeholders, and write to STDOUT
+ if (ARGV[2]) {
+ while ((getline < ARGV[2]) > 0) {
+ if (match ($0, /@@NUMVERS@@/)) {
+ gsub("@@NUMVERS@@", my_ver_num, $0);
+ }
+ if (match ($0, /@@PACKAGE_VERSION@@/)) {
+ gsub("@@PACKAGE_VERSION@@", "" my_ver_str "", $0);
+ }
+ print;
+ }
+ } else {
+ print "RRD_VERSION = " my_ver "";
+ print "RRD_VERSION_STR = " my_ver_str "";
+ print "RRD_NUMVERS = " my_ver_num "";
+ }
+}
+
diff --git a/program/src/hash_32.c b/program/src/hash_32.c
--- /dev/null
+++ b/program/src/hash_32.c
@@ -0,0 +1,152 @@
+/*
+ * hash_32 - 32 bit Fowler/Noll/Vo hash code
+ *
+ *
+ ***
+ *
+ * Fowler/Noll/Vo hash
+ *
+ * The basis of this hash algorithm was taken from an idea sent
+ * as reviewer comments to the IEEE POSIX P1003.2 committee by:
+ *
+ * Phong Vo (http://www.research.att.com/info/kpv/)
+ * Glenn Fowler (http://www.research.att.com/~gsf/)
+ *
+ * In a subsequent ballot round:
+ *
+ * Landon Curt Noll (http://reality.sgi.com/chongo/)
+ *
+ * improved on their algorithm. Some people tried this hash
+ * and found that it worked rather well. In an EMail message
+ * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash.
+ *
+ * FNV hashes are architected to be fast while maintaining a low
+ * collision rate. The FNV speed allows one to quickly hash lots
+ * of data while maintaining a reasonable collision rate. See:
+ *
+ * http://reality.sgi.com/chongo/tech/comp/fnv/
+ *
+ * for more details as well as other forms of the FNV hash.
+ ***
+ *
+ * NOTE: The FNV-0 historic hash is not recommended. One should use
+ * the FNV-1 hash instead.
+ *
+ * To use the 32 bit FNV-0 historic hash, pass FNV0_32_INIT as the
+ * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str().
+ *
+ * To use the recommended 32 bit FNV-1 hash, pass FNV1_32_INIT as the
+ * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str().
+ *
+ ***
+ *
+ * Please do not copyright this code. This code is in the public domain.
+ *
+ * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
+ * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
+ * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
+ * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * By:
+ * chongo <Landon Curt Noll> /\oo/\
+ * http://reality.sgi.com/chongo/
+ * EMail: chongo_fnv at prime dot engr dot sgi dot com
+ *
+ * Share and Enjoy! :-)
+ */
+
+#include <stdlib.h>
+#include "fnv.h"
+
+
+/*
+ * 32 bit magic FNV-0 and FNV-1 prime
+ */
+#define FNV_32_PRIME ((Fnv32_t)0x01000193)
+
+
+/*
+ * fnv_32_buf - perform a 32 bit Fowler/Noll/Vo hash on a buffer
+ *
+ * input:
+ * buf - start of buffer to hash
+ * len - length of buffer in octets
+ * hval - previous hash value or 0 if first call
+ *
+ * returns:
+ * 32 bit hash as a static hash type
+ *
+ * NOTE: To use the 32 bit FNV-0 historic hash, use FNV0_32_INIT as the hval
+ * argument on the first call to either fnv_32_buf() or fnv_32_str().
+ *
+ * NOTE: To use the recommended 32 bit FNV-1 hash, use FNV1_32_INIT as the hval
+ * argument on the first call to either fnv_32_buf() or fnv_32_str().
+ */
+Fnv32_t
+fnv_32_buf(const void *buf, size_t len, Fnv32_t hval)
+{
+ const unsigned char *bp = (const unsigned char *)buf; /* start of buffer */
+ const unsigned char *be = bp + len; /* beyond end of buffer */
+
+ /*
+ * FNV-1 hash each octet in the buffer
+ */
+ while (bp < be) {
+
+ /* multiply by the 32 bit FNV magic prime mod 2^64 */
+ hval *= FNV_32_PRIME;
+
+ /* xor the bottom with the current octet */
+ hval ^= (Fnv32_t)*bp++;
+ }
+
+ /* return our new hash value */
+ return hval;
+}
+
+
+/*
+ * fnv_32_str - perform a 32 bit Fowler/Noll/Vo hash on a string
+ *
+ * input:
+ * str - string to hash
+ * hval - previous hash value or 0 if first call
+ *
+ * returns:
+ * 32 bit hash as a static hash type
+ *
+ * NOTE: To use the 32 bit FNV-0 historic hash, use FNV0_32_INIT as the hval
+ * argument on the first call to either fnv_32_buf() or fnv_32_str().
+ *
+ * NOTE: To use the recommended 32 bit FNV-1 hash, use FNV1_32_INIT as the hval
+ * argument on the first call to either fnv_32_buf() or fnv_32_str().
+ */
+Fnv32_t
+fnv_32_str(const char *str, Fnv32_t hval)
+{
+ const unsigned char *s = (const unsigned char *)str; /* unsigned string */
+
+ /*
+ * FNV-1 hash each octet in the buffer
+ */
+ while (*s) {
+
+ /* multiply by the 32 bit FNV magic prime mod 2^64 */
+ hval *= FNV_32_PRIME;
+
+ /* xor the bottom with the current octet */
+ hval ^= (Fnv32_t)*s++;
+ }
+
+ /* return our new hash value */
+ return hval;
+}
+
+/* a wrapper function for fnv_32_str */
+unsigned long FnvHash(const char *str)
+{
+ return fnv_32_str(str,FNV1_32_INIT);
+}
diff --git a/program/src/parsetime.c b/program/src/parsetime.c
--- /dev/null
+++ b/program/src/parsetime.c
@@ -0,0 +1,981 @@
+/*
+ * parsetime.c - parse time for at(1)
+ * Copyright (C) 1993, 1994 Thomas Koenig
+ *
+ * modifications for English-language times
+ * Copyright (C) 1993 David Parsons
+ *
+ * A lot of modifications and extensions
+ * (including the new syntax being useful for RRDB)
+ * Copyright (C) 1999 Oleg Cherevko (aka Olwi Deer)
+ *
+ * severe structural damage inflicted by Tobi Oetiker in 1999
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are 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. The name of the author(s) may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) 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, WETHER 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.
+ */
+
+/* NOTE: nothing in here is thread-safe!!!! Not even the localtime
+ calls ... */
+
+/*
+ * The BNF-like specification of the time syntax parsed is below:
+ *
+ * As usual, [ X ] means that X is optional, { X } means that X may
+ * be either omitted or specified as many times as needed,
+ * alternatives are separated by |, brackets are used for grouping.
+ * (# marks the beginning of comment that extends to the end of line)
+ *
+ * TIME-SPECIFICATION ::= TIME-REFERENCE [ OFFSET-SPEC ] |
+ * OFFSET-SPEC |
+ * ( START | END ) OFFSET-SPEC
+ *
+ * TIME-REFERENCE ::= NOW | TIME-OF-DAY-SPEC [ DAY-SPEC-1 ] |
+ * [ TIME-OF-DAY-SPEC ] DAY-SPEC-2
+ *
+ * TIME-OF-DAY-SPEC ::= NUMBER (':') NUMBER [am|pm] | # HH:MM
+ * 'noon' | 'midnight' | 'teatime'
+ *
+ * DAY-SPEC-1 ::= NUMBER '/' NUMBER '/' NUMBER | # MM/DD/[YY]YY
+ * NUMBER '.' NUMBER '.' NUMBER | # DD.MM.[YY]YY
+ * NUMBER # Seconds since 1970
+ * NUMBER # YYYYMMDD
+ *
+ * DAY-SPEC-2 ::= MONTH-NAME NUMBER [NUMBER] | # Month DD [YY]YY
+ * 'yesterday' | 'today' | 'tomorrow' |
+ * DAY-OF-WEEK
+ *
+ *
+ * OFFSET-SPEC ::= '+'|'-' NUMBER TIME-UNIT { ['+'|'-'] NUMBER TIME-UNIT }
+ *
+ * TIME-UNIT ::= SECONDS | MINUTES | HOURS |
+ * DAYS | WEEKS | MONTHS | YEARS
+ *
+ * NOW ::= 'now' | 'n'
+ *
+ * START ::= 'start' | 's'
+ * END ::= 'end' | 'e'
+ *
+ * SECONDS ::= 'seconds' | 'second' | 'sec' | 's'
+ * MINUTES ::= 'minutes' | 'minute' | 'min' | 'm'
+ * HOURS ::= 'hours' | 'hour' | 'hr' | 'h'
+ * DAYS ::= 'days' | 'day' | 'd'
+ * WEEKS ::= 'weeks' | 'week' | 'wk' | 'w'
+ * MONTHS ::= 'months' | 'month' | 'mon' | 'm'
+ * YEARS ::= 'years' | 'year' | 'yr' | 'y'
+ *
+ * MONTH-NAME ::= 'jan' | 'january' | 'feb' | 'february' | 'mar' | 'march' |
+ * 'apr' | 'april' | 'may' | 'jun' | 'june' | 'jul' | 'july' |
+ * 'aug' | 'august' | 'sep' | 'september' | 'oct' | 'october' |
+ * 'nov' | 'november' | 'dec' | 'december'
+ *
+ * DAY-OF-WEEK ::= 'sunday' | 'sun' | 'monday' | 'mon' | 'tuesday' | 'tue' |
+ * 'wednesday' | 'wed' | 'thursday' | 'thu' | 'friday' | 'fri' |
+ * 'saturday' | 'sat'
+ *
+ *
+ * As you may note, there is an ambiguity with respect to
+ * the 'm' time unit (which can mean either minutes or months).
+ * To cope with this, code tries to read users mind :) by applying
+ * certain heuristics. There are two of them:
+ *
+ * 1. If 'm' is used in context of (i.e. right after the) years,
+ * months, weeks, or days it is assumed to mean months, while
+ * in the context of hours, minutes, and seconds it means minutes.
+ * (e.g., in -1y6m or +3w1m 'm' means 'months', while in
+ * -3h20m or +5s2m 'm' means 'minutes')
+ *
+ * 2. Out of context (i.e. right after the '+' or '-' sign) the
+ * meaning of 'm' is guessed from the number it directly follows.
+ * Currently, if the number absolute value is below 25 it is assumed
+ * that 'm' means months, otherwise it is treated as minutes.
+ * (e.g., -25m == -25 minutes, while +24m == +24 months)
+ *
+ */
+
+/* System Headers */
+
+/* Local headers */
+
+#include "rrd_tool.h"
+#include <stdarg.h>
+
+/* Structures and unions */
+
+enum { /* symbols */
+ MIDNIGHT, NOON, TEATIME,
+ PM, AM, YESTERDAY, TODAY, TOMORROW, NOW, START, END,
+ SECONDS, MINUTES, HOURS, DAYS, WEEKS, MONTHS, YEARS,
+ MONTHS_MINUTES,
+ NUMBER, PLUS, MINUS, DOT, COLON, SLASH, ID, JUNK,
+ JAN, FEB, MAR, APR, MAY, JUN,
+ JUL, AUG, SEP, OCT, NOV, DEC,
+ SUN, MON, TUE, WED, THU, FRI, SAT
+ };
+
+/* the below is for plus_minus() */
+#define PREVIOUS_OP (-1)
+
+/* parse translation table - table driven parsers can be your FRIEND!
+ */
+struct SpecialToken {
+ char *name; /* token name */
+ int value; /* token id */
+};
+static struct SpecialToken VariousWords[] = {
+ { "midnight", MIDNIGHT }, /* 00:00:00 of today or tomorrow */
+ { "noon", NOON }, /* 12:00:00 of today or tomorrow */
+ { "teatime", TEATIME }, /* 16:00:00 of today or tomorrow */
+ { "am", AM }, /* morning times for 0-12 clock */
+ { "pm", PM }, /* evening times for 0-12 clock */
+ { "tomorrow", TOMORROW },
+ { "yesterday", YESTERDAY },
+ { "today", TODAY },
+ { "now", NOW },
+ { "n", NOW },
+ { "start", START },
+ { "s", START },
+ { "end", END },
+ { "e", END },
+
+ { "jan", JAN },
+ { "feb", FEB },
+ { "mar", MAR },
+ { "apr", APR },
+ { "may", MAY },
+ { "jun", JUN },
+ { "jul", JUL },
+ { "aug", AUG },
+ { "sep", SEP },
+ { "oct", OCT },
+ { "nov", NOV },
+ { "dec", DEC },
+ { "january", JAN },
+ { "february", FEB },
+ { "march", MAR },
+ { "april", APR },
+ { "may", MAY },
+ { "june", JUN },
+ { "july", JUL },
+ { "august", AUG },
+ { "september", SEP },
+ { "october", OCT },
+ { "november", NOV },
+ { "december", DEC },
+ { "sunday", SUN },
+ { "sun", SUN },
+ { "monday", MON },
+ { "mon", MON },
+ { "tuesday", TUE },
+ { "tue", TUE },
+ { "wednesday", WED },
+ { "wed", WED },
+ { "thursday", THU },
+ { "thu", THU },
+ { "friday", FRI },
+ { "fri", FRI },
+ { "saturday", SAT },
+ { "sat", SAT },
+ { NULL, 0 } /*** SENTINEL ***/
+};
+
+static struct SpecialToken TimeMultipliers[] = {
+ { "second", SECONDS }, /* seconds multiplier */
+ { "seconds", SECONDS }, /* (pluralized) */
+ { "sec", SECONDS }, /* (generic) */
+ { "s", SECONDS }, /* (short generic) */
+ { "minute", MINUTES }, /* minutes multiplier */
+ { "minutes", MINUTES }, /* (pluralized) */
+ { "min", MINUTES }, /* (generic) */
+ { "m", MONTHS_MINUTES }, /* (short generic) */
+ { "hour", HOURS }, /* hours ... */
+ { "hours", HOURS }, /* (pluralized) */
+ { "hr", HOURS }, /* (generic) */
+ { "h", HOURS }, /* (short generic) */
+ { "day", DAYS }, /* days ... */
+ { "days", DAYS }, /* (pluralized) */
+ { "d", DAYS }, /* (short generic) */
+ { "week", WEEKS }, /* week ... */
+ { "weeks", WEEKS }, /* (pluralized) */
+ { "wk", WEEKS }, /* (generic) */
+ { "w", WEEKS }, /* (short generic) */
+ { "month", MONTHS }, /* week ... */
+ { "months", MONTHS }, /* (pluralized) */
+ { "mon", MONTHS }, /* (generic) */
+ { "year", YEARS }, /* year ... */
+ { "years", YEARS }, /* (pluralized) */
+ { "yr", YEARS }, /* (generic) */
+ { "y", YEARS }, /* (short generic) */
+ { NULL, 0 } /*** SENTINEL ***/
+};
+
+/* File scope variables */
+
+/* context dependent list of specials for parser to recognize,
+ * required for us to be able distinguish between 'mon' as 'month'
+ * and 'mon' as 'monday'
+ */
+static struct SpecialToken *Specials;
+
+static const char **scp; /* scanner - pointer at arglist */
+static char scc; /* scanner - count of remaining arguments */
+static const char *sct; /* scanner - next char pointer in current argument */
+static int need; /* scanner - need to advance to next argument */
+
+static char *sc_token=NULL; /* scanner - token buffer */
+static size_t sc_len; /* scanner - length of token buffer */
+static int sc_tokid; /* scanner - token id */
+
+/* Local functions */
+static void EnsureMemFree (void);
+
+static void EnsureMemFree (void)
+{
+ if( sc_token )
+ {
+ free(sc_token);
+ sc_token = NULL;
+ }
+}
+
+/*
+ * A hack to compensate for the lack of the C++ exceptions
+ *
+ * Every function func that might generate parsing "exception"
+ * should return TIME_OK (aka NULL) or pointer to the error message,
+ * and should be called like this: try(func(args));
+ *
+ * if the try is not successful it will reset the token pointer ...
+ *
+ * [NOTE: when try(...) is used as the only statement in the "if-true"
+ * part of the if statement that also has an "else" part it should be
+ * either enclosed in the curly braces (despite the fact that it looks
+ * like a single statement) or NOT followed by the ";"]
+ */
+#define try(b) { \
+ char *_e; \
+ if((_e=(b))) \
+ { \
+ EnsureMemFree(); \
+ return _e; \
+ } \
+ }
+
+/*
+ * The panic() function was used in the original code to die, we redefine
+ * it as macro to start the chain of ascending returns that in conjunction
+ * with the try(b) above will simulate a sort of "exception handling"
+ */
+
+#define panic(e) { \
+ return (e); \
+ }
+
+/*
+ * ve() and e() are used to set the return error,
+ * the most appropriate use for these is inside panic(...)
+ */
+#define MAX_ERR_MSG_LEN 1024
+static char errmsg[ MAX_ERR_MSG_LEN ];
+
+static char *
+ve ( char *fmt, va_list ap )
+{
+#ifdef HAVE_VSNPRINTF
+ vsnprintf( errmsg, MAX_ERR_MSG_LEN, fmt, ap );
+#else
+ vsprintf( errmsg, fmt, ap );
+#endif
+ EnsureMemFree();
+ return( errmsg );
+}
+
+static char *
+e ( char *fmt, ... )
+{
+ char *err;
+ va_list ap;
+ va_start( ap, fmt );
+ err = ve( fmt, ap );
+ va_end( ap );
+ return( err );
+}
+
+/* Compare S1 and S2, ignoring case, returning less than, equal to or
+ greater than zero if S1 is lexicographically less than,
+ equal to or greater than S2. -- copied from GNU libc*/
+static int
+mystrcasecmp (s1, s2)
+ const char *s1;
+ const char *s2;
+{
+ const unsigned char *p1 = (const unsigned char *) s1;
+ const unsigned char *p2 = (const unsigned char *) s2;
+ unsigned char c1, c2;
+
+ if (p1 == p2)
+ return 0;
+
+ do
+ {
+ c1 = tolower (*p1++);
+ c2 = tolower (*p2++);
+ if (c1 == '\0')
+ break;
+ }
+ while (c1 == c2);
+
+ return c1 - c2;
+}
+
+/*
+ * parse a token, checking if it's something special to us
+ */
+static int
+parse_token(char *arg)
+{
+ int i;
+
+ for (i=0; Specials[i].name != NULL; i++)
+ if (mystrcasecmp(Specials[i].name, arg) == 0)
+ return sc_tokid = Specials[i].value;
+
+ /* not special - must be some random id */
+ return sc_tokid = ID;
+} /* parse_token */
+
+
+
+/*
+ * init_scanner() sets up the scanner to eat arguments
+ */
+static char *
+init_scanner(int argc, const char **argv)
+{
+ scp = argv;
+ scc = argc;
+ need = 1;
+ sc_len = 1;
+ while (argc-- > 0)
+ sc_len += strlen(*argv++);
+
+ sc_token = (char *) malloc(sc_len*sizeof(char));
+ if( sc_token == NULL )
+ return "Failed to allocate memory";
+ return TIME_OK;
+} /* init_scanner */
+
+/*
+ * token() fetches a token from the input stream
+ */
+static int
+token()
+{
+ int idx;
+
+ while (1) {
+ memset(sc_token, '\0', sc_len);
+ sc_tokid = EOF;
+ idx = 0;
+
+ /* if we need to read another argument, walk along the argument list;
+ * when we fall off the arglist, we'll just return EOF forever
+ */
+ if (need) {
+ if (scc < 1)
+ return sc_tokid;
+ sct = *scp;
+ scp++;
+ scc--;
+ need = 0;
+ }
+ /* eat whitespace now - if we walk off the end of the argument,
+ * we'll continue, which puts us up at the top of the while loop
+ * to fetch the next argument in
+ */
+ while (isspace((unsigned char)*sct) || *sct == '_' || *sct == ',' )
+ ++sct;
+ if (!*sct) {
+ need = 1;
+ continue;
+ }
+
+ /* preserve the first character of the new token
+ */
+ sc_token[0] = *sct++;
+
+ /* then see what it is
+ */
+ if (isdigit((unsigned char)(sc_token[0]))) {
+ while (isdigit((unsigned char)(*sct)))
+ sc_token[++idx] = *sct++;
+ sc_token[++idx] = '\0';
+ return sc_tokid = NUMBER;
+ }
+ else if (isalpha((unsigned char)(sc_token[0]))) {
+ while (isalpha((unsigned char)(*sct)))
+ sc_token[++idx] = *sct++;
+ sc_token[++idx] = '\0';
+ return parse_token(sc_token);
+ }
+ else switch(sc_token[0]) {
+ case ':': return sc_tokid = COLON;
+ case '.': return sc_tokid = DOT;
+ case '+': return sc_tokid = PLUS;
+ case '-': return sc_tokid = MINUS;
+ case '/': return sc_tokid = SLASH;
+ default:
+ /*OK, we did not make it ... */
+ sct--;
+ return sc_tokid = EOF;
+ }
+ } /* while (1) */
+} /* token */
+
+
+/*
+ * expect2() gets a token and complains if it's not the token we want
+ */
+static char *
+expect2(int desired, char *complain_fmt, ...)
+{
+ va_list ap;
+ va_start( ap, complain_fmt );
+ if (token() != desired) {
+ panic(ve( complain_fmt, ap ));
+ }
+ va_end( ap );
+ return TIME_OK;
+
+} /* expect2 */
+
+
+/*
+ * plus_minus() is used to parse a single NUMBER TIME-UNIT pair
+ * for the OFFSET-SPEC.
+ * It also applies those m-guessing heuristics.
+ */
+static char *
+plus_minus(struct rrd_time_value *ptv, int doop)
+{
+ static int op = PLUS;
+ static int prev_multiplier = -1;
+ int delta;
+
+ if( doop >= 0 )
+ {
+ op = doop;
+ try(expect2(NUMBER,"There should be number after '%c'", op == PLUS ? '+' : '-'));
+ prev_multiplier = -1; /* reset months-minutes guessing mechanics */
+ }
+ /* if doop is < 0 then we repeat the previous op
+ * with the prefetched number */
+
+ delta = atoi(sc_token);
+
+ if( token() == MONTHS_MINUTES )
+ {
+ /* hard job to guess what does that -5m means: -5mon or -5min? */
+ switch(prev_multiplier)
+ {
+ case DAYS:
+ case WEEKS:
+ case MONTHS:
+ case YEARS:
+ sc_tokid = MONTHS;
+ break;
+
+ case SECONDS:
+ case MINUTES:
+ case HOURS:
+ sc_tokid = MINUTES;
+ break;
+
+ default:
+ if( delta < 6 ) /* it may be some other value but in the context
+ * of RRD who needs less than 6 min deltas? */
+ sc_tokid = MONTHS;
+ else
+ sc_tokid = MINUTES;
+ }
+ }
+ prev_multiplier = sc_tokid;
+ switch (sc_tokid) {
+ case YEARS:
+ ptv->tm.tm_year += (op == PLUS) ? delta : -delta;
+ return TIME_OK;
+ case MONTHS:
+ ptv->tm.tm_mon += (op == PLUS) ? delta : -delta;
+ return TIME_OK;
+ case WEEKS:
+ delta *= 7;
+ /* FALLTHRU */
+ case DAYS:
+ ptv->tm.tm_mday += (op == PLUS) ? delta : -delta;
+ return TIME_OK;
+ case HOURS:
+ ptv->offset += (op == PLUS) ? delta*60*60 : -delta*60*60;
+ return TIME_OK;
+ case MINUTES:
+ ptv->offset += (op == PLUS) ? delta*60 : -delta*60;
+ return TIME_OK;
+ case SECONDS:
+ ptv->offset += (op == PLUS) ? delta : -delta;
+ return TIME_OK;
+ default: /*default unit is seconds */
+ ptv->offset += (op == PLUS) ? delta : -delta;
+ return TIME_OK;
+ }
+ panic(e("well-known time unit expected after %d", delta));
+ /* NORETURN */
+ return TIME_OK; /* to make compiler happy :) */
+} /* plus_minus */
+
+
+/*
+ * tod() computes the time of day (TIME-OF-DAY-SPEC)
+ */
+static char *
+tod(struct rrd_time_value *ptv)
+{
+ int hour, minute = 0;
+ int tlen;
+ /* save token status in case we must abort */
+ int scc_sv = scc;
+ const char *sct_sv = sct;
+ int sc_tokid_sv = sc_tokid;
+
+ tlen = strlen(sc_token);
+
+ /* first pick out the time of day - we assume a HH (COLON|DOT) MM time
+ */
+ if (tlen > 2) {
+ return TIME_OK;
+ }
+
+ hour = atoi(sc_token);
+
+ token();
+ if (sc_tokid == SLASH || sc_tokid == DOT) {
+ /* guess we are looking at a date */
+ scc = scc_sv;
+ sct = sct_sv;
+ sc_tokid = sc_tokid_sv;
+ sprintf (sc_token,"%d", hour);
+ return TIME_OK;
+ }
+ if (sc_tokid == COLON ) {
+ try(expect2(NUMBER,
+ "Parsing HH:MM syntax, expecting MM as number, got none"));
+ minute = atoi(sc_token);
+ if (minute > 59) {
+ panic(e("parsing HH:MM syntax, got MM = %d (>59!)", minute ));
+ }
+ token();
+ }
+
+ /* check if an AM or PM specifier was given
+ */
+ if (sc_tokid == AM || sc_tokid == PM) {
+ if (hour > 12) {
+ panic(e("there cannot be more than 12 AM or PM hours"));
+ }
+ if (sc_tokid == PM) {
+ if (hour != 12) /* 12:xx PM is 12:xx, not 24:xx */
+ hour += 12;
+ } else {
+ if (hour == 12) /* 12:xx AM is 00:xx, not 12:xx */
+ hour = 0;
+ }
+ token();
+ }
+ else if (hour > 23) {
+ /* guess it was not a time then ... */
+ scc = scc_sv;
+ sct = sct_sv;
+ sc_tokid = sc_tokid_sv;
+ sprintf (sc_token,"%d", hour);
+ return TIME_OK;
+ }
+ ptv->tm.tm_hour = hour;
+ ptv->tm.tm_min = minute;
+ ptv->tm.tm_sec = 0;
+ if (ptv->tm.tm_hour == 24) {
+ ptv->tm.tm_hour = 0;
+ ptv->tm.tm_mday++;
+ }
+ return TIME_OK;
+} /* tod */
+
+
+/*
+ * assign_date() assigns a date, adjusting year as appropriate
+ */
+static char *
+assign_date(struct rrd_time_value *ptv, long mday, long mon, long year)
+{
+ if (year > 138) {
+ if (year > 1970)
+ year -= 1900;
+ else {
+ panic(e("invalid year %d (should be either 00-99 or >1900)",
+ year));
+ }
+ } else if( year >= 0 && year < 38 ) {
+ year += 100; /* Allow year 2000-2037 to be specified as */
+ } /* 00-37 until the problem of 2038 year will */
+ /* arise for unices with 32-bit time_t :) */
+ if (year < 70) {
+ panic(e("won't handle dates before epoch (01/01/1970), sorry"));
+ }
+
+ ptv->tm.tm_mday = mday;
+ ptv->tm.tm_mon = mon;
+ ptv->tm.tm_year = year;
+ return TIME_OK;
+} /* assign_date */
+
+
+/*
+ * day() picks apart DAY-SPEC-[12]
+ */
+static char *
+day(struct rrd_time_value *ptv)
+{
+ /* using time_t seems to help portability with 64bit oses */
+ time_t mday=0, wday, mon, year = ptv->tm.tm_year;
+ int tlen;
+
+ switch (sc_tokid) {
+ case YESTERDAY:
+ ptv->tm.tm_mday--;
+ /* FALLTRHU */
+ case TODAY: /* force ourselves to stay in today - no further processing */
+ token();
+ break;
+ case TOMORROW:
+ ptv->tm.tm_mday++;
+ token();
+ break;
+
+ case JAN: case FEB: case MAR: case APR: case MAY: case JUN:
+ case JUL: case AUG: case SEP: case OCT: case NOV: case DEC:
+ /* do month mday [year]
+ */
+ mon = (sc_tokid-JAN);
+ try(expect2(NUMBER,
+ "the day of the month should follow month name"));
+ mday = atol(sc_token);
+ if (token() == NUMBER) {
+ year = atol(sc_token);
+ token();
+ }
+ else
+ year = ptv->tm.tm_year;
+ try(assign_date(ptv, mday, mon, year));
+ break;
+
+ case SUN: case MON: case TUE:
+ case WED: case THU: case FRI:
+ case SAT:
+ /* do a particular day of the week
+ */
+ wday = (sc_tokid-SUN);
+ ptv->tm.tm_mday += (wday - ptv->tm.tm_wday);
+ token();
+ break;
+ /*
+ mday = ptv->tm.tm_mday;
+ mday += (wday - ptv->tm.tm_wday);
+ ptv->tm.tm_wday = wday;
+
+ try(assign_date(ptv, mday, ptv->tm.tm_mon, ptv->tm.tm_year));
+ break;
+ */
+
+ case NUMBER:
+ /* get numeric <sec since 1970>, MM/DD/[YY]YY, or DD.MM.[YY]YY
+ */
+ tlen = strlen(sc_token);
+ mon = atol(sc_token);
+ if (mon > 10*365*24*60*60) {
+ ptv->tm=*localtime(&mon);
+ token();
+ break;
+ }
+
+ if (mon > 19700101 && mon < 24000101){ /*works between 1900 and 2400 */
+ char cmon[3],cmday[3],cyear[5];
+ strncpy(cyear,sc_token,4);cyear[4]='\0';
+ year = atol(cyear);
+ strncpy(cmon,&(sc_token[4]),2);cmon[2]='\0';
+ mon = atol(cmon);
+ strncpy(cmday,&(sc_token[6]),2);cmday[2]='\0';
+ mday = atol(cmday);
+ token();
+ } else {
+ token();
+
+ if (mon <= 31 && (sc_tokid == SLASH || sc_tokid == DOT)) {
+ int sep;
+ sep = sc_tokid;
+ try(expect2(NUMBER,"there should be %s number after '%c'",
+ sep == DOT ? "month" : "day", sep == DOT ? '.' : '/'));
+ mday = atol(sc_token);
+ if (token() == sep) {
+ try(expect2(NUMBER,"there should be year number after '%c'",
+ sep == DOT ? '.' : '/'));
+ year = atol(sc_token);
+ token();
+ }
+
+ /* flip months and days for European timing
+ */
+ if (sep == DOT) {
+ long x = mday;
+ mday = mon;
+ mon = x;
+ }
+ }
+ }
+
+ mon--;
+ if(mon < 0 || mon > 11 ) {
+ panic(e("did you really mean month %d?", mon+1));
+ }
+ if(mday < 1 || mday > 31) {
+ panic(e("I'm afraid that %d is not a valid day of the month",
+ mday));
+ }
+ try(assign_date(ptv, mday, mon, year));
+ break;
+ } /* case */
+ return TIME_OK;
+} /* month */
+
+
+/* Global functions */
+
+
+/*
+ * parsetime() is the external interface that takes tspec, parses
+ * it and puts the result in the rrd_time_value structure *ptv.
+ * It can return either absolute times (these are ensured to be
+ * correct) or relative time references that are expected to be
+ * added to some absolute time value and then normalized by
+ * mktime() The return value is either TIME_OK (aka NULL) or
+ * the pointer to the error message in the case of problems
+ */
+char *
+parsetime(const char *tspec, struct rrd_time_value *ptv)
+{
+ time_t now = time(NULL);
+ int hr = 0;
+ /* this MUST be initialized to zero for midnight/noon/teatime */
+
+ Specials = VariousWords; /* initialize special words context */
+
+ try(init_scanner( 1, &tspec ));
+
+ /* establish the default time reference */
+ ptv->type = ABSOLUTE_TIME;
+ ptv->offset = 0;
+ ptv->tm = *localtime(&now);
+ ptv->tm.tm_isdst = -1; /* mk time can figure this out for us ... */
+
+ token();
+ switch (sc_tokid) {
+ case PLUS:
+ case MINUS:
+ break; /* jump to OFFSET-SPEC part */
+
+ case START:
+ ptv->type = RELATIVE_TO_START_TIME;
+ goto KeepItRelative;
+ case END:
+ ptv->type = RELATIVE_TO_END_TIME;
+ KeepItRelative:
+ ptv->tm.tm_sec = 0;
+ ptv->tm.tm_min = 0;
+ ptv->tm.tm_hour = 0;
+ ptv->tm.tm_mday = 0;
+ ptv->tm.tm_mon = 0;
+ ptv->tm.tm_year = 0;
+ /* FALLTHRU */
+ case NOW:
+ {
+ int time_reference = sc_tokid;
+ token();
+ if( sc_tokid == PLUS || sc_tokid == MINUS )
+ break;
+ if( time_reference != NOW ) {
+ panic(e("'start' or 'end' MUST be followed by +|- offset"));
+ }
+ else
+ if( sc_tokid != EOF ) {
+ panic(e("if 'now' is followed by a token it must be +|- offset"));
+ }
+ };
+ break;
+
+ /* Only absolute time specifications below */
+ case NUMBER:
+ {
+ long hour_sv = ptv->tm.tm_hour;
+ long year_sv = ptv->tm.tm_year;
+ ptv->tm.tm_hour = 30;
+ ptv->tm.tm_year = 30000;
+ try(tod(ptv))
+ try(day(ptv))
+ if ( ptv->tm.tm_hour == 30 && ptv->tm.tm_year != 30000 ){
+ try(tod(ptv))
+ }
+ if ( ptv->tm.tm_hour == 30 ){
+ ptv->tm.tm_hour = hour_sv;
+ }
+ if ( ptv->tm.tm_year == 30000 ){
+ ptv->tm.tm_year = year_sv;
+ }
+ };
+ break;
+ /* fix month parsing */
+ case JAN: case FEB: case MAR: case APR: case MAY: case JUN:
+ case JUL: case AUG: case SEP: case OCT: case NOV: case DEC:
+ try(day(ptv));
+ if (sc_tokid != NUMBER) break;
+ try(tod(ptv))
+ break;
+
+ /* evil coding for TEATIME|NOON|MIDNIGHT - we've initialized
+ * hr to zero up above, then fall into this case in such a
+ * way so we add +12 +4 hours to it for teatime, +12 hours
+ * to it for noon, and nothing at all for midnight, then
+ * set our rettime to that hour before leaping into the
+ * month scanner
+ */
+ case TEATIME:
+ hr += 4;
+ /* FALLTHRU */
+ case NOON:
+ hr += 12;
+ /* FALLTHRU */
+ case MIDNIGHT:
+ /* if (ptv->tm.tm_hour >= hr) {
+ ptv->tm.tm_mday++;
+ ptv->tm.tm_wday++;
+ } */ /* shifting does not makes sense here ... noon is noon */
+ ptv->tm.tm_hour = hr;
+ ptv->tm.tm_min = 0;
+ ptv->tm.tm_sec = 0;
+ token();
+ try(day(ptv));
+ break;
+ default:
+ panic(e("unparsable time: %s%s",sc_token,sct));
+ break;
+ } /* ugly case statement */
+
+ /*
+ * the OFFSET-SPEC part
+ *
+ * (NOTE, the sc_tokid was prefetched for us by the previous code)
+ */
+ if( sc_tokid == PLUS || sc_tokid == MINUS ) {
+ Specials = TimeMultipliers; /* switch special words context */
+ while( sc_tokid == PLUS || sc_tokid == MINUS ||
+ sc_tokid == NUMBER ) {
+ if( sc_tokid == NUMBER ) {
+ try(plus_minus(ptv, PREVIOUS_OP ));
+ } else
+ try(plus_minus(ptv, sc_tokid));
+ token(); /* We will get EOF eventually but that's OK, since
+ token() will return us as many EOFs as needed */
+ }
+ }
+
+ /* now we should be at EOF */
+ if( sc_tokid != EOF ) {
+ panic(e("unparsable trailing text: '...%s%s'", sc_token, sct));
+ }
+
+ ptv->tm.tm_isdst = -1; /* for mktime to guess DST status */
+ if( ptv->type == ABSOLUTE_TIME )
+ if( mktime( &ptv->tm ) == -1 ) { /* normalize & check */
+ /* can happen for "nonexistent" times, e.g. around 3am */
+ /* when winter -> summer time correction eats a hour */
+ panic(e("the specified time is incorrect (out of range?)"));
+ }
+ EnsureMemFree();
+ return TIME_OK;
+} /* parsetime */
+
+
+int proc_start_end (struct rrd_time_value *start_tv,
+ struct rrd_time_value *end_tv,
+ time_t *start,
+ time_t *end){
+ if (start_tv->type == RELATIVE_TO_END_TIME && /* same as the line above */
+ end_tv->type == RELATIVE_TO_START_TIME) {
+ rrd_set_error("the start and end times cannot be specified "
+ "relative to each other");
+ return -1;
+ }
+
+ if (start_tv->type == RELATIVE_TO_START_TIME) {
+ rrd_set_error("the start time cannot be specified relative to itself");
+ return -1;
+ }
+
+ if (end_tv->type == RELATIVE_TO_END_TIME) {
+ rrd_set_error("the end time cannot be specified relative to itself");
+ return -1;
+ }
+
+ if( start_tv->type == RELATIVE_TO_END_TIME) {
+ struct tm tmtmp;
+ *end = mktime(&(end_tv->tm)) + end_tv->offset;
+ tmtmp = *localtime(end); /* reinit end including offset */
+ tmtmp.tm_mday += start_tv->tm.tm_mday;
+ tmtmp.tm_mon += start_tv->tm.tm_mon;
+ tmtmp.tm_year += start_tv->tm.tm_year;
+ *start = mktime(&tmtmp) + start_tv->offset;
+ } else {
+ *start = mktime(&(start_tv->tm)) + start_tv->offset;
+ }
+ if (end_tv->type == RELATIVE_TO_START_TIME) {
+ struct tm tmtmp;
+ *start = mktime(&(start_tv->tm)) + start_tv->offset;
+ tmtmp = *localtime(start);
+ tmtmp.tm_mday += end_tv->tm.tm_mday;
+ tmtmp.tm_mon += end_tv->tm.tm_mon;
+ tmtmp.tm_year += end_tv->tm.tm_year;
+ *end = mktime(&tmtmp) + end_tv->offset;
+ } else {
+ *end = mktime(&(end_tv->tm)) + end_tv->offset;
+ }
+ return 0;
+} /* proc_start_end */
+
+
+
+
+
+
+
diff --git a/program/src/parsetime.h b/program/src/parsetime.h
--- /dev/null
+++ b/program/src/parsetime.h
@@ -0,0 +1,8 @@
+#ifndef __PARSETIME_H__
+#define __PARSETIME_H__
+
+#include <stdio.h>
+
+#include "rrd.h"
+
+#endif
diff --git a/program/src/pngsize.c b/program/src/pngsize.c
--- /dev/null
+++ b/program/src/pngsize.c
@@ -0,0 +1,50 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * pngsize.c determine the size of a PNG image
+ *****************************************************************************/
+
+#include <png.h>
+#include "rrd_tool.h"
+
+int
+PngSize(FILE *fd, long *width, long *height)
+{
+ png_structp png_read_ptr =
+ png_create_read_struct(PNG_LIBPNG_VER_STRING,
+ (png_voidp)NULL,
+ /* we would need to point to error handlers
+ here to do it properly */
+ (png_error_ptr)NULL, (png_error_ptr)NULL);
+
+ png_infop info_ptr = png_create_info_struct(png_read_ptr);
+
+ (*width)=0;
+ (*height)=0;
+
+/* this is to make compile on aix work since they seem to define jmpbuf
+ to be _jmpbuf which breaks compilation */
+
+#ifdef jmpbuf
+#undef jmpbuf
+#endif
+
+ if (setjmp(png_read_ptr->jmpbuf)){
+ png_destroy_read_struct(&png_read_ptr, &info_ptr, (png_infopp)NULL);
+ return 0;
+ }
+
+ png_init_io(png_read_ptr,fd);
+ png_read_info(png_read_ptr, info_ptr);
+ (*width)=png_get_image_width(png_read_ptr, info_ptr);
+ (*height)=png_get_image_height(png_read_ptr, info_ptr);
+
+ png_destroy_read_struct(&png_read_ptr, &info_ptr, NULL);
+ if (*width >0 && *height >0)
+ return 1;
+ else
+ return 0;
+}
+
+
+
diff --git a/program/src/rrd.h b/program/src/rrd.h
--- /dev/null
+++ b/program/src/rrd.h
@@ -0,0 +1,152 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrdlib.h Public header file for librrd
+ *****************************************************************************
+ * $Id$
+ * $Log$
+ * Revision 1.9 2005/02/13 16:13:33 oetiker
+ * let rrd_graph return the actual value range it picked ...
+ * -- Henrik Stoerner <henrik@hswn.dk>
+ *
+ * Revision 1.8 2004/05/26 22:11:12 oetiker
+ * reduce compiler warnings. Many small fixes. -- Mike Slifcak <slif@bellsouth.net>
+ *
+ * Revision 1.7 2003/11/12 22:14:26 oetiker
+ * allow to pass an open filehandle into rrd_graph as an extra argument
+ *
+ * Revision 1.6 2003/11/11 19:46:21 oetiker
+ * replaced time_value with rrd_time_value as MacOS X introduced a struct of that name in their standard headers
+ *
+ * Revision 1.5 2003/04/25 18:35:08 jake
+ * Alternate update interface, updatev. Returns info about CDPs written to disk as result of update. Output format is similar to rrd_info, a hash of key-values.
+ *
+ * Revision 1.4 2003/04/01 22:52:23 jake
+ * Fix Win32 build. VC++ 6.0 and 7.0 now use the thread-safe code.
+ *
+ * Revision 1.3 2003/02/13 07:05:27 oetiker
+ * Find attached the patch I promised to send to you. Please note that there
+ * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c
+ * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This
+ * library is identical to librrd, but it contains support code for per-thread
+ * global variables currently used for error information only. This is similar
+ * to how errno per-thread variables are implemented. librrd_th must be linked
+ * alongside of libpthred
+ *
+ * There is also a new file "THREADS", holding some documentation.
+ *
+ * -- Peter Stamfest <peter@stamfest.at>
+ *
+ * Revision 1.2 2002/05/07 21:58:32 oetiker
+ * new command rrdtool xport integrated
+ * -- Wolfgang Schrimm <Wolfgang.Schrimm@urz.uni-heidelberg.de>
+ *
+ * Revision 1.1.1.1 2001/02/25 22:25:05 oetiker
+ * checkin
+ *
+ *****************************************************************************/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _RRDLIB_H
+#define _RRDLIB_H
+
+#include <time.h>
+#include <stdio.h> /* for FILE */
+
+/* Transplanted from rrd_format.h */
+typedef double rrd_value_t; /* the data storage type is
+ * double */
+/* END rrd_format.h */
+
+/* main function blocks */
+int rrd_create(int, char **);
+int rrd_update(int, char **);
+int rrd_graph(int, char **, char ***, int *, int *, FILE *, double *, double *);
+int rrd_fetch(int, char **, time_t *, time_t *, unsigned long *,
+ unsigned long *, char ***, rrd_value_t **);
+int rrd_restore(int, char **);
+int rrd_dump(int, char **);
+int rrd_tune(int, char **);
+time_t rrd_last(int, char **);
+time_t rrd_first(int, char **);
+int rrd_resize(int, char **);
+char * rrd_strversion(void);
+double rrd_version(void);
+int rrd_xport(int, char **, int *, time_t *, time_t *,
+ unsigned long *, unsigned long *,
+ char ***, rrd_value_t **);
+
+/* thread-safe (hopefully) */
+int rrd_create_r(const char *filename,
+ unsigned long pdp_step, time_t last_up,
+ int argc, const char **argv);
+/* NOTE: rrd_update_r are only thread-safe if no at-style time
+ specifications get used!!! */
+
+int rrd_update_r(const char *filename, const char *_template,
+ int argc, const char **argv);
+int rrd_fetch_r(const char *filename, const char* cf,
+ time_t *start, time_t *end,
+ unsigned long *step,
+ unsigned long *ds_cnt,
+ char ***ds_namv,
+ rrd_value_t **data);
+int rrd_dump_r(const char *filename, char *outname);
+time_t rrd_last_r(const char *filename);
+time_t rrd_first_r(const char *filename, int rraindex);
+
+/* Transplanted from parsetime.h */
+typedef enum {
+ ABSOLUTE_TIME,
+ RELATIVE_TO_START_TIME,
+ RELATIVE_TO_END_TIME
+} timetype;
+
+#define TIME_OK NULL
+
+struct rrd_time_value {
+ timetype type;
+ long offset;
+ struct tm tm;
+};
+
+char *parsetime(const char *spec, struct rrd_time_value *ptv);
+/* END parsetime.h */
+
+struct rrd_context {
+ int len;
+ int errlen;
+ char *lib_errstr;
+ char *rrd_error;
+};
+
+/* returns the current per-thread rrd_context */
+struct rrd_context *rrd_get_context(void);
+
+
+int proc_start_end (struct rrd_time_value *, struct rrd_time_value *, time_t *, time_t *);
+
+/* HELPER FUNCTIONS */
+void rrd_set_error(char *,...);
+void rrd_clear_error(void);
+int rrd_test_error(void);
+char *rrd_get_error(void);
+
+/** MULTITHREADED HELPER FUNCTIONS */
+struct rrd_context *rrd_new_context(void);
+void rrd_free_context (struct rrd_context *buf);
+
+/* void rrd_set_error_r (struct rrd_context *, char *, ...); */
+/* void rrd_clear_error_r(struct rrd_context *); */
+/* int rrd_test_error_r (struct rrd_context *); */
+/* char *rrd_get_error_r (struct rrd_context *); */
+
+int LockRRD(FILE *);
+
+#endif /* _RRDLIB_H */
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/program/src/rrd_afm.c b/program/src/rrd_afm.c
--- /dev/null
+++ b/program/src/rrd_afm.c
@@ -0,0 +1,287 @@
+/****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd_afm.h Parsing afm tables to find width of strings.
+ ****************************************************************************
+ * $Id$
+*/
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__) && !defined(HAVE_CONFIG_H)
+#include "../win32/config.h"
+#else
+#ifdef HAVE_CONFIG_H
+#include "../rrd_config.h"
+#endif
+#endif
+
+#include "rrd_afm.h"
+#include "rrd_afm_data.h"
+
+#include <stdio.h>
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+
+#include "unused.h"
+
+#if 0
+# define DEBUG 1
+# define DLOG(x) fprintf x
+#else
+# define DEBUG 0
+# define DLOG(x)
+#endif
+
+/* Adobe SVG View and Batik 1.1.1 can't handle ligatures.
+ So disable it as we just waste speed.
+ Besides, it doesn't matter much in normal text.
+*/
+#define ENABLE_LIGATURES 0
+
+static const afm_fontinfo *afm_last_used_font = NULL;
+static const char *last_unknown_font = NULL;
+
+#define is_font(p, name) \
+ (!strcmp(p->postscript_name, name) || !strcmp(p->fullname, name))
+
+static const afm_fontinfo *afm_searchfont(const char *name)
+{
+ int i;
+ const afm_fontinfo *p = afm_last_used_font;
+ if (p && is_font(p, name))
+ return p;
+ p = afm_fontinfolist;
+ for (i = 0; i < afm_fontinfo_count; i++, p++) {
+ if (is_font(p, name)) {
+ afm_last_used_font = p;
+ return p;
+ }
+ }
+ return NULL;
+}
+
+
+/* returns always a font, never NULL.
+ The rest of the code depends on the result never being NULL.
+ See rrd_afm.h */
+static const afm_fontinfo *afm_findfont(const char *name)
+{
+ const afm_fontinfo *p = afm_searchfont(name);
+ if (p)
+ return p;
+ if (!last_unknown_font || strcmp(name, last_unknown_font)) {
+ fprintf(stderr, "Can't find font '%s'\n", name);
+ last_unknown_font = name;
+ }
+ p = afm_searchfont(RRD_AFM_DEFAULT_FONT);
+ if (p)
+ return p;
+ return afm_fontinfolist; /* anything, just anything. */
+}
+
+const char *afm_get_font_postscript_name(const char* font)
+{
+ const afm_fontinfo *p = afm_findfont(font);
+ return p->postscript_name;
+}
+
+const char *afm_get_font_name(const char* font)
+{
+ const afm_fontinfo *p = afm_findfont(font);
+ return p->fullname;
+}
+
+double afm_get_ascender(const char* font, double size)
+{
+ const afm_fontinfo *p = afm_findfont(font);
+ return size * p->ascender / 1000.0;
+}
+
+double afm_get_descender(const char* font, double size)
+{
+ const afm_fontinfo *p = afm_findfont(font);
+ return size * p->descender / 1000.0;
+}
+
+static int afm_find_char_index(const afm_fontinfo *fontinfo,
+ afm_cunicode ch1)
+{
+ int idx = ch1 - 32;
+ afm_cuint16 *indexP;
+ int numIndexChars, i;
+ if (idx <= 0)
+ return 0;
+ if (idx <= 126 - 32)
+ return idx;
+ indexP = fontinfo->highchars_index;
+ if (indexP == 0)
+ return 0;
+ numIndexChars = fontinfo->highchars_count;
+ DLOG((stderr, " find highbit, num = %d\n", numIndexChars));
+ if (ch1 >= 161 && ch1 <= 255) {
+ idx = ch1 - 161;
+ DLOG((stderr, " 161, idx = %d -> %d\n", idx, indexP[idx]));
+ if (idx < numIndexChars && indexP[idx] == ch1) {
+ idx += 127 - 32;
+ DLOG((stderr, " 161-guessed ok to %d\n", idx));
+ return idx;
+ }
+ }
+ for (i = 0; i < numIndexChars; i++) {
+ DLOG((stderr, " compares to %d -> %d\n", indexP[i], i));
+ if (indexP[i] == ch1)
+ return i + 127 - 32;
+ }
+ DLOG((stderr, "Did not find %d in highchars_index ??\n", ch1));
+ return 0;
+}
+
+#if ENABLE_LIGATURES
+static afm_cunicode afm_find_combined_ligature(const afm_fontinfo *fontinfo,
+ afm_cunicode ch1, afm_cunicode ch2)
+{
+ afm_cunicode *p = fontinfo->ligatures;
+ int num = fontinfo->ligatures_count;
+ int i;
+ if (!num)
+ return 0;
+ DLOG((stderr, " find-lig, num = %d\n", num));
+ for (i = 0; i < num; i++, p += 3) {
+ DLOG((stderr, " lig: %d + %d -> %d (%c %c %c)\n",
+ p[0], p[1], p[2], p[0], p[1], p[2]));
+ if (ch1 == *p && ch2 == p[1]) {
+ DLOG((stderr, " matches.\n"));
+ return p[2];
+ }
+ }
+ return 0;
+}
+#endif
+
+#define READ_ESCAPED(p, val) \
+ if ((val = *p++) == 0) { \
+ val = 254 + *p++; \
+ } else if (!--val) { \
+ val = *p++ << 8; \
+ val |= *p++; \
+ }
+
+
+static long afm_find_kern(const afm_fontinfo *fontinfo,
+ int kern_idx, afm_cunicode ch2)
+{
+ afm_cuint8 *p8 = fontinfo->kerning_data + kern_idx;
+ int num;
+ READ_ESCAPED(p8, num);
+ DLOG((stderr, " find kern, num pairs = %d\n", num));
+ while (num > 0) {
+ afm_unicode ch;
+ READ_ESCAPED(p8, ch);
+ DLOG((stderr, " pair-char = %d\n", ch));
+ if (ch == ch2) {
+ DLOG((stderr, " got kern = %d\n", *(afm_csint8*)p8));
+ return *(afm_csint8*)p8;
+ }
+ p8++;
+ num--;
+ }
+ return 0;
+}
+
+/* measure width of a text string */
+double afm_get_text_width( double start, const char* font, double size,
+ double tabwidth, const char* text)
+{
+#ifdef HAVE_MBSTOWCS
+ size_t clen = strlen(text) + 1;
+ wchar_t *cstr = malloc(sizeof(wchar_t) * clen); /* yes we are allocating probably too much here, I know */
+ int text_count = mbstowcs(cstr, text, clen);
+ double w;
+ if (text_count == -1)
+ text_count = mbstowcs(cstr, "Enc-Err", 6);
+#ifdef __APPLE__
+ while (text_count > 0) {
+ text_count--;
+ cstr[text_count] = afm_fix_osx_charset(cstr[text_count]); /* unsafe macro */
+ }
+#endif
+ w = afm_get_text_width_wide(start, font, size, tabwidth, cstr);
+ free(cstr);
+ return w;
+#else
+ return afm_get_text_width_wide(start, font, size, tabwidth, text);
+#endif
+}
+
+double afm_get_text_width_wide( double UNUSED(start), const char* font, double size,
+ double UNUSED(tabwidth), const afm_char* text)
+{
+ const afm_fontinfo *fontinfo = afm_findfont(font);
+ long width = 0;
+ double widthf;
+ const afm_char *up = text;
+ DLOG((stderr, "================= %s\n", text));
+ if (fontinfo == NULL) {
+ while (*up)
+ up++;
+ return size * (up - text);
+ }
+ while (1) {
+ afm_unicode ch1, ch2;
+ int idx1, kern_idx;
+ if ((ch1 = *up) == 0)
+ break;
+ ch2 = *++up;
+ DLOG((stderr, "------------- Loop: %d + %d (%c%c) at %d\n",
+ ch1, ch2, ch1, ch2 ? ch2 : ' ',
+ (up - (const unsigned char*)text) - 1));
+ idx1 = afm_find_char_index(fontinfo, ch1);
+ DLOG((stderr, " idx1 = %d\n", idx1));
+#if ENABLE_LIGATURES
+ if (ch2) {
+ int ch1_new = afm_find_combined_ligature(fontinfo, ch1, ch2);
+ DLOG((stderr, " lig-ch = %d\n", ch1_new));
+ if (ch1_new) {
+ ch1 = ch1_new;
+ idx1 = afm_find_char_index(fontinfo, ch1);
+ ch2 = *++up;
+ DLOG((stderr, " -> idx1 = %d, ch2 = %d (%c)\n",
+ idx1, ch2, ch2 ? ch2 : ' '));
+ }
+ }
+#endif
+ width += fontinfo->widths[idx1];
+ DLOG((stderr, "Plain width of %d = %d\n", ch1, fontinfo->widths[idx1]));
+ if (fontinfo->kerning_index && ch2) {
+ kern_idx = fontinfo->kerning_index[idx1];
+ DLOG((stderr, " kern_idx = %d\n", kern_idx));
+ if (kern_idx > 0)
+ width += afm_find_kern(fontinfo, kern_idx, ch2);
+ }
+ }
+ widthf = (width * 6 / 1000.0) * size;
+ DLOG((stderr, "Returns %ld (%ld) -> %f\n", width, width * 6, widthf));
+ return widthf;
+}
+
+#ifdef __APPLE__
+const unsigned char afm_mac2iso[128] = {
+ '\xC4', '\xC5', '\xC7', '\xC9', '\xD1', '\xD6', '\xDC', '\xE1', /* 80 */
+ '\xE0', '\xE2', '\xE4', '\xE3', '\xE5', '\xE7', '\xE9', '\xE8', /* 88 */
+ '\xEA', '\xEB', '\xED', '\xEC', '\xEE', '\xEF', '\xF1', '\xF3', /* 90 */
+ '\xF2', '\xF4', '\xF6', '\xF5', '\xFA', '\xF9', '\xFB', '\xFC', /* 98 */
+ '\xDD', '\xB0', '\xA2', '\xA3', '\xA7', ' ', '\xB6', '\xDF', /* A0 */
+ '\xAE', '\xA9', ' ', '\xB4', '\xA8', ' ', '\xC6', '\xD8', /* A8 */
+ ' ', '\xB1', '\xBE', ' ', '\xA5', '\xB5', ' ', ' ', /* B0 */
+ '\xBD', '\xBC', ' ', '\xAA', '\xBA', ' ', '\xE6', '\xF8', /* B8 */
+ '\xBF', '\xA1', '\xAC', ' ', ' ', ' ', ' ', '\xAB', /* C0 */
+ '\xBB', ' ', '\xA0', '\xC0', '\xC3', '\xD5', ' ', '\xA6', /* C8 */
+ '\xAD', ' ', '"', '"', '\'', '\'', '\xF7', '\xD7', /* D0 */
+ '\xFF', ' ', ' ', '\xA4', '\xD0', '\xF0', '\xDE', '\xFE', /* D8 */
+ '\xFD', '\xB7', ' ', ' ', ' ', '\xC2', '\xCA', '\xC1', /* E0 */
+ '\xCB', '\xC8', '\xCD', '\xCE', '\xCF', '\xCC', '\xD3', '\xD4', /* E8 */
+ ' ', '\xD2', '\xDA', '\xDB', '\xD9', ' ', ' ', ' ', /* F0 */
+ '\xAF', ' ', ' ', ' ', '\xB8', ' ', ' ', ' ', /* F8 */
+};
+#endif
diff --git a/program/src/rrd_afm.h b/program/src/rrd_afm.h
--- /dev/null
+++ b/program/src/rrd_afm.h
@@ -0,0 +1,54 @@
+/****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd_afm.h Parsing afm tables to find width of strings.
+ ****************************************************************************/
+
+#ifndef RRD_AFM_H
+#define RRD_AFM_H
+
+#include <stdlib.h>
+
+#ifdef HAVE_MBSTOWCS
+#define afm_char wchar_t
+#else
+#define afm_char unsigned char
+#endif
+/*
+ If the font specified by the name parameter in the routes below
+ is not found
+ (because it is not compiled into rrd_afm_data.c by compile_afm.pl)
+ the font specified by RRD_AFM_DEFAULT_FONT will be used.
+ If it is not installed, it uses the first font compiled
+ into rrd_afm_data.c
+ So they will always use some font.
+*/
+
+#define RRD_AFM_DEFAULT_FONT "Courier"
+
+/* measure width of a text string */
+/* fontname can be full name or postscript name */
+double afm_get_text_width( double start, const char* font, double size,
+ double tabwidth, const char* text);
+double afm_get_text_width_wide( double start, const char* font, double size,
+ double tabwidth, const afm_char* text);
+
+double afm_get_ascender(const char* font, double size);
+double afm_get_descender(const char* font, double size);
+
+/* get postscript name from fullname or postscript name */
+const char *afm_get_font_postscript_name ( const char* font);
+const char *afm_get_font_name(const char* font);
+
+/* cc -E -dM /dev/null */
+#ifdef __APPLE__
+/* need charset conversion from macintosh to unicode. */
+extern const unsigned char afm_mac2iso[128];
+#define afm_fix_osx_charset(c) \
+ ( (c) >= 128 && (c) <= 255 ? afm_mac2iso[(c) - 128] : (c))
+#else
+/* UNSAFE macro */
+#define afm_fix_osx_charset(x) (x)
+#endif
+
+#endif
diff --git a/program/src/rrd_afm_data.c b/program/src/rrd_afm_data.c
--- /dev/null
@@ -0,0 +1,3338 @@
+/****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd_afm_data.c Encoded afm (Adobe Font Metrics) for selected fonts.
+ ****************************************************************************
+ *
+ * THIS FILE IS AUTOGENERATED BY PERL. DO NOT EDIT.
+ *
+ ****************************************************************************/
+
+#include "rrd_afm_data.h"
+#include <stdlib.h>
+
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Courier */
+/* FullName: Courier */
+/* FamilyName: Courier */
+static afm_cuint8 afm_Courier_widths[] = { /* 315 */
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100
+};
+static afm_cuint16 afm_Courier_highchars_index[] = { /* 220 */
+ 161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+ 178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+ 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+ 210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+ 226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+ 242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+ 258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+ 280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+ 313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+ 336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+ 354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+ 380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+ 8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+ 8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+ 64257,64258
+};
+static afm_cunicode afm_Courier_ligatures[] = { /* 3 */
+ 102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Courier-Bold */
+/* FullName: Courier Bold */
+/* FamilyName: Courier */
+static afm_cuint8 afm_Courier_Bold_widths[] = { /* 315 */
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100
+};
+static afm_cuint16 afm_Courier_Bold_highchars_index[] = { /* 220 */
+ 161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+ 178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+ 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+ 210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+ 226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+ 242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+ 258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+ 280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+ 313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+ 336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+ 354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+ 380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+ 8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+ 8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+ 64257,64258
+};
+static afm_cunicode afm_Courier_Bold_ligatures[] = { /* 3 */
+ 102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Courier-BoldOblique */
+/* FullName: Courier Bold Oblique */
+/* FamilyName: Courier */
+static afm_cuint8 afm_Courier_BoldOblique_widths[] = { /* 315 */
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100
+};
+static afm_cuint16 afm_Courier_BoldOblique_highchars_index[] = { /* 220 */
+ 161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+ 178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+ 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+ 210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+ 226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+ 242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+ 258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+ 280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+ 313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+ 336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+ 354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+ 380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+ 8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+ 8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+ 64257,64258
+};
+static afm_cunicode afm_Courier_BoldOblique_ligatures[] = { /* 3 */
+ 102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Courier-Oblique */
+/* FullName: Courier Oblique */
+/* FamilyName: Courier */
+static afm_cuint8 afm_Courier_Oblique_widths[] = { /* 315 */
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,
+ 100,100,100,100,100,100,100,100,100,100,100
+};
+static afm_cuint16 afm_Courier_Oblique_highchars_index[] = { /* 220 */
+ 161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+ 178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+ 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+ 210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+ 226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+ 242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+ 258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+ 280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+ 313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+ 336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+ 354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+ 380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+ 8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+ 8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+ 64257,64258
+};
+static afm_cunicode afm_Courier_Oblique_ligatures[] = { /* 3 */
+ 102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Helvetica */
+/* FullName: Helvetica */
+/* FamilyName: Helvetica */
+static afm_cuint8 afm_Helvetica_widths[] = { /* 315 */
+ 46,46,59,93,93,148,111,32,56,56,65,97,46,56,46,46,93,93,93,93,93,
+ 93,93,93,93,93,46,46,97,97,97,93,169,111,111,120,120,111,102,130,
+ 120,46,83,111,93,139,120,130,111,130,120,111,102,120,111,157,111,
+ 111,102,46,46,46,78,93,56,93,93,83,93,93,46,93,93,37,37,83,37,139,
+ 93,93,93,93,56,83,46,93,83,120,83,83,83,56,43,56,97,56,93,93,93,
+ 93,43,93,56,123,62,93,97,123,56,67,97,56,56,56,93,90,46,56,56,61,
+ 93,139,139,139,102,111,111,111,111,111,111,167,120,111,111,111,111,
+ 46,46,46,46,120,120,130,130,130,130,130,97,130,120,120,120,120,111,
+ 111,102,93,93,93,93,93,93,148,83,93,93,93,93,46,46,46,46,93,93,93,
+ 93,93,93,93,97,102,93,93,93,93,83,93,83,111,93,111,93,111,93,120,
+ 83,120,83,120,107,120,93,111,93,111,93,111,93,111,93,130,93,130,
+ 93,46,46,46,37,46,46,111,83,93,37,93,37,93,50,93,37,120,93,120,93,
+ 120,93,130,93,130,93,167,157,120,56,120,56,120,56,111,83,111,83,
+ 111,83,102,46,102,53,120,93,120,93,120,93,120,93,111,102,83,102,
+ 83,102,83,93,111,83,56,56,56,56,56,56,56,56,93,167,37,37,37,56,56,
+ 56,93,93,58,167,167,56,56,28,93,167,79,102,100,97,76,92,92,92,79,
+ 42,83,83
+};
+static afm_sint16 afm_Helvetica_kerning_index[] = { /* 315 */
+ 1,0,0,0,0,0,0,0,0,0,0,0,29,0,38,0,0,0,0,0,0,0,0,0,0,0,49,52,0,0,
+ 0,0,0,55,170,197,202,0,241,0,0,0,345,418,510,0,0,545,594,687,710,
+ 772,777,938,966,1115,0,1259,0,0,0,0,0,0,0,1407,1419,1468,0,1476,
+ 1494,1575,1587,0,0,1595,0,1638,1668,1700,1718,0,1730,1847,0,0,1854,
+ 1924,1994,2017,2087,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,2130,2245,2360,2475,2590,2705,0,2820,0,0,
+ 0,0,0,0,0,0,0,0,2825,2874,2923,2972,3021,0,3070,3119,3147,3175,3203,
+ 3231,0,0,3379,3391,3403,3415,3427,3439,0,3451,3459,3477,3495,3513,
+ 0,0,0,0,0,3531,3563,3581,3599,3617,3635,0,3653,0,0,0,0,3882,0,3952,
+ 4022,4137,4149,4264,4276,4391,4403,4408,4416,4421,4429,0,4468,0,
+ 0,4507,0,4525,0,4543,0,4561,0,4579,0,4591,0,0,0,0,0,0,4603,4695,
+ 4738,0,4773,0,4808,0,4843,0,0,4878,0,4910,0,4942,4974,5023,5041,
+ 5090,0,0,5108,5170,5287,5349,5466,5528,5645,5650,5657,5662,5669,
+ 5674,5681,0,5842,0,6003,0,6031,0,6059,0,6087,0,6115,0,6263,0,6306,
+ 0,6349,0,6392,6397,0,0,0,0,0,0,0,0,0,0,6404,6409,0,0,6447,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+static afm_cuint8 afm_Helvetica_kerning_data[] = { /* 6450 */
+ 42,11,85,249,87,249,88,250,90,242,222,242,0,100,249,0,102,249,0,
+ 122,242,1,32,24,247,1,32,28,252,3,1,32,25,240,1,32,29,240,4,33,247,
+ 1,32,25,240,1,32,29,240,2,33,249,2,33,249,49,68,252,72,252,80,252,
+ 82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,250,120,250,
+ 122,250,200,252,211,252,212,252,213,252,214,252,215,252,217,252,
+ 218,249,219,249,220,249,221,249,222,240,250,252,251,252,252,252,
+ 253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,0,78,
+ 252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,249,0,
+ 113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,12,45,
+ 254,47,254,86,255,218,255,219,255,220,255,221,255,0,108,255,0,112,
+ 255,0,114,255,0,116,255,3,45,252,47,252,18,45,245,47,245,66,250,
+ 87,245,88,250,90,242,193,250,194,250,195,250,196,250,197,250,198,
+ 250,222,242,0,2,250,0,4,250,0,6,250,0,122,242,45,45,232,47,232,66,
+ 244,98,249,102,252,112,252,115,249,193,244,194,244,195,244,196,244,
+ 197,244,198,244,225,249,226,249,227,249,228,249,229,249,230,249,
+ 233,252,234,252,235,252,236,252,243,252,244,252,245,252,246,252,
+ 247,252,249,252,0,2,244,0,3,249,0,4,244,0,5,249,0,6,244,0,7,249,
+ 0,21,252,0,25,252,0,27,252,0,29,252,0,79,252,0,83,252,0,87,249,0,
+ 89,249,0,91,249,32,45,252,47,252,66,254,98,254,118,254,193,254,194,
+ 254,195,254,196,254,197,254,198,254,225,254,226,254,227,254,228,
+ 254,229,254,230,254,250,254,251,254,252,254,253,254,0,2,254,0,3,
+ 254,0,4,254,0,5,254,0,6,254,0,7,254,0,109,254,0,113,254,0,115,254,
+ 0,117,254,40,80,249,102,250,112,250,118,252,122,249,211,249,212,
+ 249,213,249,214,249,215,249,217,249,233,250,234,250,235,250,236,
+ 250,243,250,244,250,245,250,246,250,247,250,249,250,250,252,251,
+ 252,252,252,253,252,254,249,0,1,249,0,21,250,0,25,250,0,27,250,0,
+ 29,250,0,78,249,0,79,250,0,82,249,0,83,250,0,109,252,0,113,252,0,
+ 115,252,0,117,252,14,85,239,87,239,88,245,90,234,122,252,222,234,
+ 254,252,0,1,252,0,100,239,0,102,239,0,122,234,1,32,25,230,1,32,29,
+ 234,22,45,250,47,250,66,254,85,250,87,249,88,252,89,247,90,245,193,
+ 254,194,254,195,254,196,254,197,254,198,254,222,245,0,2,254,0,4,
+ 254,0,6,254,0,100,250,0,102,250,0,122,245,41,45,227,47,227,66,237,
+ 98,250,102,249,112,249,193,237,194,237,195,237,196,237,197,237,198,
+ 237,225,250,226,250,227,250,228,250,229,250,230,250,233,249,234,
+ 249,235,249,236,249,243,249,244,249,245,249,246,249,247,249,249,
+ 249,0,2,237,0,3,250,0,4,237,0,5,250,0,6,237,0,7,250,0,21,249,0,25,
+ 249,0,27,249,0,29,249,0,79,249,0,83,249,10,86,255,218,255,219,255,
+ 220,255,221,255,0,108,255,0,112,255,0,114,255,0,116,255,27,80,254,
+ 85,252,86,250,87,249,88,252,90,249,211,254,212,254,213,254,214,254,
+ 215,254,217,254,218,250,219,250,220,250,221,250,222,249,0,78,254,
+ 0,82,254,0,100,252,0,102,252,0,108,250,0,112,250,0,114,250,0,116,
+ 250,0,122,249,3,45,254,47,254,70,45,237,46,234,47,237,59,254,60,
+ 254,66,237,80,250,98,237,102,237,112,237,115,237,118,237,120,237,
+ 122,237,193,237,194,237,195,237,196,237,197,237,198,237,211,250,
+ 212,250,213,250,214,250,215,250,217,250,225,237,226,237,227,237,
+ 228,247,229,237,230,237,233,247,234,237,235,237,236,237,243,237,
+ 244,237,245,237,246,247,247,237,249,237,250,237,251,237,252,237,
+ 253,237,254,237,0,1,247,0,2,237,0,3,247,0,4,237,0,5,247,0,6,237,
+ 0,7,237,0,21,247,0,25,237,0,27,237,0,29,237,0,78,250,0,79,247,0,
+ 82,250,0,83,237,0,87,237,0,89,237,0,91,237,0,109,247,0,113,237,0,
+ 115,237,0,117,237,13,45,250,47,250,66,250,193,250,194,250,195,250,
+ 196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,65,45,236,46,244,
+ 47,236,59,250,60,250,66,244,72,250,80,250,98,245,102,244,112,244,
+ 118,245,193,244,194,244,195,244,196,244,197,244,198,244,211,250,
+ 212,250,213,250,214,250,215,250,217,250,225,245,226,245,227,245,
+ 228,245,229,245,230,245,233,244,234,244,235,244,236,244,243,244,
+ 244,244,245,244,246,244,247,244,249,244,250,245,251,245,252,245,
+ 253,245,0,2,244,0,3,245,0,4,244,0,5,245,0,6,244,0,7,245,0,21,244,
+ 0,25,244,0,27,244,0,29,244,0,32,250,0,36,250,0,78,250,0,79,244,0,
+ 82,250,0,83,244,0,109,245,0,113,245,0,115,245,0,117,245,63,45,244,
+ 46,250,47,244,66,249,80,254,98,250,102,252,112,252,118,252,122,254,
+ 193,249,194,249,195,249,196,249,197,249,198,249,211,254,212,254,
+ 213,254,214,254,215,254,217,254,225,250,226,250,227,250,228,250,
+ 229,250,230,250,233,252,234,252,235,252,236,252,243,252,244,252,
+ 245,252,246,252,247,252,249,252,250,252,251,252,252,252,253,252,
+ 254,254,0,1,254,0,2,249,0,3,250,0,4,249,0,5,250,0,6,249,0,7,250,
+ 0,21,252,0,25,252,0,27,252,0,29,252,0,78,254,0,79,252,0,82,254,0,
+ 83,252,0,109,252,0,113,252,0,115,252,0,117,252,65,45,234,46,234,
+ 47,234,59,247,60,247,66,239,80,243,98,234,102,234,106,254,112,234,
+ 118,239,193,239,194,239,195,239,196,239,197,239,198,239,211,243,
+ 212,243,213,243,214,243,215,243,217,243,225,234,226,234,227,234,
+ 228,234,229,234,230,234,233,234,234,234,235,234,236,234,238,254,
+ 243,234,244,234,245,234,246,234,247,234,249,234,250,239,251,239,
+ 252,239,253,239,0,2,239,0,3,245,0,4,239,0,5,245,0,6,239,0,7,234,
+ 0,21,245,0,25,234,0,27,234,0,29,234,0,49,254,0,78,243,0,79,234,0,
+ 82,243,0,83,234,0,109,239,0,113,239,0,115,239,0,117,239,6,119,254,
+ 120,254,122,252,254,252,0,1,252,21,45,250,47,250,99,255,109,254,
+ 118,254,119,254,122,254,250,254,251,254,252,254,253,254,254,254,
+ 0,1,254,0,60,254,0,62,254,0,68,254,0,109,254,0,113,254,0,115,254,
+ 0,117,254,4,45,254,108,254,0,57,254,9,45,254,47,254,119,252,120,
+ 254,121,252,122,254,254,254,0,1,254,34,45,252,47,252,98,252,102,
+ 252,112,252,225,252,226,252,227,252,228,252,229,252,230,252,233,
+ 252,234,252,235,252,236,252,243,252,244,252,245,252,246,252,247,
+ 252,249,252,0,3,252,0,5,252,0,7,252,0,21,252,0,25,252,0,27,252,0,
+ 29,252,0,51,252,0,79,252,0,83,252,1,32,25,8,1,32,29,10,5,115,255,
+ 0,87,255,0,89,255,0,91,255,4,122,252,254,252,0,1,252,19,102,254,
+ 112,254,233,254,234,254,235,254,236,254,243,254,244,254,245,254,
+ 246,254,247,254,249,254,0,21,254,0,25,254,0,27,254,0,29,254,0,79,
+ 254,0,83,254,13,118,255,122,254,250,255,251,255,252,255,253,255,
+ 254,254,0,1,254,0,109,255,0,113,255,0,115,255,0,117,255,14,118,255,
+ 119,254,122,254,250,255,251,255,252,255,253,255,254,254,0,1,254,
+ 0,109,255,0,113,255,0,115,255,0,117,255,9,45,250,47,250,119,254,
+ 120,254,121,252,122,252,254,252,0,1,252,6,45,251,47,251,122,252,
+ 254,252,0,1,252,50,45,249,47,249,59,5,60,5,98,255,106,3,108,3,109,
+ 3,110,4,111,4,113,5,117,7,118,3,119,5,122,5,225,255,226,255,227,
+ 255,228,255,229,255,230,255,237,3,238,3,239,3,240,3,242,4,250,3,
+ 251,3,252,3,253,3,254,5,0,1,5,0,3,255,0,5,255,0,7,255,0,45,3,0,49,
+ 3,0,57,3,0,60,3,0,62,3,0,68,3,0,70,4,0,72,4,0,74,4,0,101,7,0,109,
+ 3,0,113,3,0,115,3,0,117,3,4,45,254,47,254,120,252,31,45,244,47,244,
+ 98,253,102,253,112,253,225,253,226,253,227,253,228,253,229,253,230,
+ 253,233,253,234,253,235,253,236,253,243,253,244,253,245,253,246,
+ 253,247,253,249,253,0,3,253,0,5,253,0,7,253,0,21,253,0,25,253,0,
+ 27,253,0,29,253,0,79,253,0,83,253,31,45,247,47,247,98,254,102,255,
+ 112,255,225,254,226,254,227,254,228,254,229,254,230,254,233,255,
+ 234,255,235,255,236,255,243,255,244,255,245,255,246,255,247,255,
+ 249,255,0,3,254,0,5,254,0,7,254,0,21,255,0,25,255,0,27,255,0,29,
+ 255,0,79,255,0,83,255,10,102,252,233,252,234,252,235,252,236,252,
+ 0,21,252,0,25,252,0,27,252,0,29,252,31,45,240,47,240,98,254,102,
+ 254,112,254,225,254,226,254,227,254,228,254,229,254,230,254,233,
+ 254,234,254,235,254,236,254,243,254,244,254,245,254,246,254,247,
+ 254,249,254,0,3,254,0,5,254,0,7,254,0,21,254,0,25,254,0,27,254,0,
+ 29,254,0,79,254,0,83,254,19,102,254,112,254,233,254,234,254,235,
+ 254,236,254,243,254,244,254,245,254,246,254,247,254,249,254,0,21,
+ 254,0,25,254,0,27,254,0,29,254,0,79,254,0,83,254,49,68,252,72,252,
+ 80,252,82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,250,
+ 120,250,122,250,200,252,211,252,212,252,213,252,214,252,215,252,
+ 217,252,218,249,219,249,220,249,221,249,222,240,250,252,251,252,
+ 252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,
+ 0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,
+ 249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,
+ 49,68,252,72,252,80,252,82,252,85,237,86,249,87,245,88,249,90,240,
+ 118,252,119,250,120,250,122,250,200,252,211,252,212,252,213,252,
+ 214,252,215,252,217,252,218,249,219,249,220,249,221,249,222,240,
+ 250,252,251,252,252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,
+ 0,32,252,0,36,252,0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,
+ 0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,
+ 252,0,122,240,49,68,252,72,252,80,252,82,252,85,237,86,249,87,245,
+ 88,249,90,240,118,252,119,250,120,250,122,250,200,252,211,252,212,
+ 252,213,252,214,252,215,252,217,252,218,249,219,249,220,249,221,
+ 249,222,240,250,252,251,252,252,252,253,252,254,250,0,1,250,0,8,
+ 252,0,14,252,0,32,252,0,36,252,0,78,252,0,82,252,0,100,237,0,102,
+ 237,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,
+ 0,116,249,0,117,252,0,122,240,49,68,252,72,252,80,252,82,252,85,
+ 237,86,249,87,245,88,249,90,240,118,252,119,250,120,250,122,250,
+ 200,252,211,252,212,252,213,252,214,252,215,252,217,252,218,249,
+ 219,249,220,249,221,249,222,240,250,252,251,252,252,252,253,252,
+ 254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,0,78,252,0,82,
+ 252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,249,0,113,252,
+ 0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,49,68,252,72,252,
+ 80,252,82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,250,
+ 120,250,122,250,200,252,211,252,212,252,213,252,214,252,215,252,
+ 217,252,218,249,219,249,220,249,221,249,222,240,250,252,251,252,
+ 252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,
+ 0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,
+ 249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,
+ 49,68,252,72,252,80,252,82,252,85,237,86,249,87,245,88,249,90,240,
+ 118,252,119,250,120,250,122,250,200,252,211,252,212,252,213,252,
+ 214,252,215,252,217,252,218,249,219,249,220,249,221,249,222,240,
+ 250,252,251,252,252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,
+ 0,32,252,0,36,252,0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,
+ 0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,
+ 252,0,122,240,3,45,252,47,252,22,45,250,47,250,66,254,85,250,87,
+ 249,88,252,89,247,90,245,193,254,194,254,195,254,196,254,197,254,
+ 198,254,222,245,0,2,254,0,4,254,0,6,254,0,100,250,0,102,250,0,122,
+ 245,22,45,250,47,250,66,254,85,250,87,249,88,252,89,247,90,245,193,
+ 254,194,254,195,254,196,254,197,254,198,254,222,245,0,2,254,0,4,
+ 254,0,6,254,0,100,250,0,102,250,0,122,245,22,45,250,47,250,66,254,
+ 85,250,87,249,88,252,89,247,90,245,193,254,194,254,195,254,196,254,
+ 197,254,198,254,222,245,0,2,254,0,4,254,0,6,254,0,100,250,0,102,
+ 250,0,122,245,22,45,250,47,250,66,254,85,250,87,249,88,252,89,247,
+ 90,245,193,254,194,254,195,254,196,254,197,254,198,254,222,245,0,
+ 2,254,0,4,254,0,6,254,0,100,250,0,102,250,0,122,245,22,45,250,47,
+ 250,66,254,85,250,87,249,88,252,89,247,90,245,193,254,194,254,195,
+ 254,196,254,197,254,198,254,222,245,0,2,254,0,4,254,0,6,254,0,100,
+ 250,0,102,250,0,122,245,22,45,250,47,250,66,254,85,250,87,249,88,
+ 252,89,247,90,245,193,254,194,254,195,254,196,254,197,254,198,254,
+ 222,245,0,2,254,0,4,254,0,6,254,0,100,250,0,102,250,0,122,245,13,
+ 45,250,47,250,66,250,193,250,194,250,195,250,196,250,197,250,198,
+ 250,0,2,250,0,4,250,0,6,250,13,45,250,47,250,66,250,193,250,194,
+ 250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,13,45,
+ 250,47,250,66,250,193,250,194,250,195,250,196,250,197,250,198,250,
+ 0,2,250,0,4,250,0,6,250,13,45,250,47,250,66,250,193,250,194,250,
+ 195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,65,45,234,
+ 46,234,47,234,59,247,60,247,66,239,80,243,98,234,102,234,106,254,
+ 112,234,118,239,193,239,194,239,195,239,196,239,197,239,198,239,
+ 211,243,212,243,213,243,214,243,215,243,217,243,225,234,226,234,
+ 227,234,228,245,229,234,230,234,233,234,234,234,235,234,236,234,
+ 238,254,243,234,244,234,245,234,246,234,247,234,249,234,250,239,
+ 251,239,252,239,253,239,0,2,239,0,3,245,0,4,239,0,5,245,0,6,239,
+ 0,7,234,0,21,245,0,25,234,0,27,234,0,29,234,0,49,254,0,78,243,0,
+ 79,245,0,82,243,0,83,234,0,109,239,0,113,239,0,115,239,0,117,239,
+ 6,119,254,120,254,122,252,254,252,0,1,252,6,119,254,120,254,122,
+ 252,254,252,0,1,252,6,119,254,120,254,122,252,254,252,0,1,252,6,
+ 119,254,120,254,122,252,254,252,0,1,252,6,119,254,120,254,122,252,
+ 254,252,0,1,252,6,119,254,120,254,122,252,254,252,0,1,252,4,45,254,
+ 108,254,0,57,254,9,45,254,47,254,119,252,120,254,121,252,122,254,
+ 254,254,0,1,254,9,45,254,47,254,119,252,120,254,121,252,122,254,
+ 254,254,0,1,254,9,45,254,47,254,119,252,120,254,121,252,122,254,
+ 254,254,0,1,254,9,45,254,47,254,119,252,120,254,121,252,122,254,
+ 254,254,0,1,254,14,118,255,119,254,122,254,250,255,251,255,252,255,
+ 253,255,254,254,0,1,254,0,109,255,0,113,255,0,115,255,0,117,255,
+ 9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+ 9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+ 9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+ 9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+ 9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+ 95,45,241,47,241,98,248,99,248,100,248,101,248,102,248,103,248,104,
+ 248,105,248,106,248,107,248,108,248,109,248,110,248,111,248,112,
+ 248,113,248,114,248,115,248,116,248,117,248,118,248,119,245,120,
+ 245,121,243,122,245,123,248,225,248,226,248,227,248,228,248,229,
+ 248,230,248,232,248,233,248,234,248,235,248,236,248,237,248,238,
+ 248,239,248,240,248,242,248,243,248,244,248,245,248,246,248,247,
+ 248,249,248,250,248,251,248,252,248,253,248,254,245,0,1,245,0,3,
+ 248,0,5,248,0,7,248,0,9,248,0,15,248,0,19,248,0,21,248,0,25,248,
+ 0,27,248,0,29,248,0,33,248,0,37,248,0,45,248,0,49,248,0,57,248,0,
+ 60,248,0,62,248,0,68,248,0,70,248,0,72,248,0,74,248,0,79,248,0,83,
+ 248,0,87,248,0,89,248,0,91,248,0,93,248,0,97,248,0,99,248,0,101,
+ 248,0,109,248,0,113,248,0,115,248,0,117,248,0,124,248,0,126,248,
+ 0,128,248,1,2,25,248,31,45,240,47,240,98,254,102,254,112,254,225,
+ 254,226,254,227,254,228,254,229,254,230,254,233,254,234,254,235,
+ 254,236,254,243,254,244,254,245,254,246,254,247,254,249,254,0,3,
+ 254,0,5,254,0,7,254,0,21,254,0,25,254,0,27,254,0,29,254,0,79,254,
+ 0,83,254,31,45,240,47,240,98,254,102,254,112,254,225,254,226,254,
+ 227,254,228,254,229,254,230,254,233,254,234,254,235,254,236,254,
+ 243,254,244,254,245,254,246,254,247,254,249,254,0,3,254,0,5,254,
+ 0,7,254,0,21,254,0,25,254,0,27,254,0,29,254,0,79,254,0,83,254,49,
+ 68,252,72,252,80,252,82,252,85,237,86,249,87,245,88,249,90,240,118,
+ 252,119,250,120,250,122,250,200,252,211,252,212,252,213,252,214,
+ 252,215,252,217,252,218,249,219,249,220,249,221,249,222,240,250,
+ 252,251,252,252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,
+ 252,0,36,252,0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,
+ 252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,
+ 0,122,240,6,119,254,120,254,122,252,254,252,0,1,252,49,68,252,72,
+ 252,80,252,82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,
+ 250,120,250,122,250,200,252,211,252,212,252,213,252,214,252,215,
+ 252,217,252,218,249,219,249,220,249,221,249,222,240,250,252,251,
+ 252,252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,
+ 36,252,0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,
+ 0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,
+ 240,6,119,254,120,254,122,252,254,252,0,1,252,49,68,252,72,252,80,
+ 252,82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,250,120,
+ 250,122,250,200,252,211,252,212,252,213,252,214,252,215,252,217,
+ 252,218,249,219,249,220,249,221,249,222,240,250,252,251,252,252,
+ 252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,0,
+ 78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,249,
+ 0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,6,119,
+ 254,120,254,122,252,254,252,0,1,252,3,45,252,47,252,4,45,254,108,
+ 254,0,57,254,3,45,252,47,252,4,45,254,108,254,0,57,254,18,45,245,
+ 47,245,66,250,87,245,88,250,90,242,193,250,194,250,195,250,196,250,
+ 197,250,198,250,222,242,0,2,250,0,4,250,0,6,250,0,122,242,18,45,
+ 245,47,245,66,250,87,245,88,250,90,242,193,250,194,250,195,250,196,
+ 250,197,250,198,250,222,242,0,2,250,0,4,250,0,6,250,0,122,242,9,
+ 45,254,47,254,119,252,120,254,121,252,122,254,254,254,0,1,254,9,
+ 45,254,47,254,119,252,120,254,121,252,122,254,254,254,0,1,254,9,
+ 45,254,47,254,119,252,120,254,121,252,122,254,254,254,0,1,254,9,
+ 45,254,47,254,119,252,120,254,121,252,122,254,254,254,0,1,254,5,
+ 115,255,0,87,255,0,89,255,0,91,255,5,115,255,0,87,255,0,89,255,0,
+ 91,255,40,80,249,102,250,112,250,118,252,122,249,211,249,212,249,
+ 213,249,214,249,215,249,217,249,233,250,234,250,235,250,236,250,
+ 243,250,244,250,245,250,246,250,247,250,249,250,250,252,251,252,
+ 252,252,253,252,254,249,0,1,249,0,21,250,0,25,250,0,27,250,0,29,
+ 250,0,78,249,0,79,250,0,82,249,0,83,250,0,109,252,0,113,252,0,115,
+ 252,0,117,252,19,102,254,112,254,233,254,234,254,235,254,236,254,
+ 243,254,244,254,245,254,246,254,247,254,249,254,0,21,254,0,25,254,
+ 0,27,254,0,29,254,0,79,254,0,83,254,14,85,239,87,239,88,245,90,234,
+ 122,252,222,234,254,252,0,1,252,0,100,239,0,102,239,0,122,234,1,
+ 32,25,230,1,32,29,234,14,85,239,87,239,88,245,90,234,122,252,222,
+ 234,254,252,0,1,252,0,100,239,0,102,239,0,122,234,1,32,25,230,1,
+ 32,29,234,14,85,239,87,239,88,245,90,234,122,252,222,234,254,252,
+ 0,1,252,0,100,239,0,102,239,0,122,234,1,32,25,230,1,32,29,234,14,
+ 85,239,87,239,88,245,90,234,122,252,222,234,254,252,0,1,252,0,100,
+ 239,0,102,239,0,122,234,1,32,25,230,1,32,29,234,14,118,255,119,254,
+ 122,254,250,255,251,255,252,255,253,255,254,254,0,1,254,0,109,255,
+ 0,113,255,0,115,255,0,117,255,14,118,255,119,254,122,254,250,255,
+ 251,255,252,255,253,255,254,254,0,1,254,0,109,255,0,113,255,0,115,
+ 255,0,117,255,14,118,255,119,254,122,254,250,255,251,255,252,255,
+ 253,255,254,254,0,1,254,0,109,255,0,113,255,0,115,255,0,117,255,
+ 22,45,250,47,250,66,254,85,250,87,249,88,252,89,247,90,245,193,254,
+ 194,254,195,254,196,254,197,254,198,254,222,245,0,2,254,0,4,254,
+ 0,6,254,0,100,250,0,102,250,0,122,245,9,45,250,47,250,119,254,120,
+ 254,121,252,122,252,254,252,0,1,252,22,45,250,47,250,66,254,85,250,
+ 87,249,88,252,89,247,90,245,193,254,194,254,195,254,196,254,197,
+ 254,198,254,222,245,0,2,254,0,4,254,0,6,254,0,100,250,0,102,250,
+ 0,122,245,9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,
+ 0,1,252,27,80,254,85,252,86,250,87,249,88,252,90,249,211,254,212,
+ 254,213,254,214,254,215,254,217,254,218,250,219,250,220,250,221,
+ 250,222,249,0,78,254,0,82,254,0,100,252,0,102,252,0,108,250,0,112,
+ 250,0,114,250,0,116,250,0,122,249,50,45,249,47,249,59,5,60,5,98,
+ 255,106,3,108,3,109,3,110,4,111,4,113,5,117,7,118,3,119,5,122,5,
+ 225,255,226,255,227,255,228,255,229,255,230,255,237,3,238,3,239,
+ 3,240,3,242,4,250,3,251,3,252,3,253,3,254,5,0,1,5,0,3,255,0,5,255,
+ 0,7,255,0,45,3,0,49,3,0,57,3,0,60,3,0,62,3,0,68,3,0,70,4,0,72,4,
+ 0,74,4,0,101,7,0,109,3,0,113,3,0,115,3,0,117,3,27,80,254,85,252,
+ 86,250,87,249,88,252,90,249,211,254,212,254,213,254,214,254,215,
+ 254,217,254,218,250,219,250,220,250,221,250,222,249,0,78,254,0,82,
+ 254,0,100,252,0,102,252,0,108,250,0,112,250,0,114,250,0,116,250,
+ 0,122,249,50,45,249,47,249,59,5,60,5,98,255,106,3,108,3,109,3,110,
+ 4,111,4,113,5,117,7,118,3,119,5,122,5,225,255,226,255,227,255,228,
+ 255,229,255,230,255,237,3,238,3,239,3,240,3,242,4,250,3,251,3,252,
+ 3,253,3,254,5,0,1,5,0,3,255,0,5,255,0,7,255,0,45,3,0,49,3,0,57,3,
+ 0,60,3,0,62,3,0,68,3,0,70,4,0,72,4,0,74,4,0,101,7,0,109,3,0,113,
+ 3,0,115,3,0,117,3,27,80,254,85,252,86,250,87,249,88,252,90,249,211,
+ 254,212,254,213,254,214,254,215,254,217,254,218,250,219,250,220,
+ 250,221,250,222,249,0,78,254,0,82,254,0,100,252,0,102,252,0,108,
+ 250,0,112,250,0,114,250,0,116,250,0,122,249,50,45,249,47,249,59,
+ 5,60,5,98,255,106,3,108,3,109,3,110,4,111,4,113,5,117,7,118,3,119,
+ 5,122,5,225,255,226,255,227,255,228,255,229,255,230,255,237,3,238,
+ 3,239,3,240,3,242,4,250,3,251,3,252,3,253,3,254,5,0,1,5,0,3,255,
+ 0,5,255,0,7,255,0,45,3,0,49,3,0,57,3,0,60,3,0,62,3,0,68,3,0,70,4,
+ 0,72,4,0,74,4,0,101,7,0,109,3,0,113,3,0,115,3,0,117,3,3,45,254,47,
+ 254,4,45,254,47,254,120,252,3,45,254,47,254,4,45,254,47,254,120,
+ 252,3,45,254,47,254,4,45,254,47,254,120,252,70,45,237,46,234,47,
+ 237,59,254,60,254,66,237,80,250,98,237,102,237,112,237,115,237,118,
+ 237,120,237,122,237,193,237,194,237,195,237,196,237,197,237,198,
+ 237,211,250,212,250,213,250,214,250,215,250,217,250,225,237,226,
+ 237,227,237,228,247,229,237,230,237,233,247,234,237,235,237,236,
+ 237,243,237,244,237,245,237,246,247,247,237,249,237,250,237,251,
+ 237,252,237,253,237,254,237,0,1,247,0,2,237,0,3,247,0,4,237,0,5,
+ 247,0,6,237,0,7,237,0,21,247,0,25,237,0,27,237,0,29,237,0,78,250,
+ 0,79,247,0,82,250,0,83,237,0,87,237,0,89,237,0,91,237,0,109,247,
+ 0,113,237,0,115,237,0,117,237,70,45,237,46,234,47,237,59,254,60,
+ 254,66,237,80,250,98,237,102,237,112,237,115,237,118,237,120,237,
+ 122,237,193,237,194,237,195,237,196,237,197,237,198,237,211,250,
+ 212,250,213,250,214,250,215,250,217,250,225,237,226,237,227,237,
+ 228,247,229,237,230,237,233,247,234,237,235,237,236,237,243,237,
+ 244,237,245,237,246,247,247,237,249,237,250,237,251,237,252,237,
+ 253,237,254,237,0,1,247,0,2,237,0,3,247,0,4,237,0,5,247,0,6,237,
+ 0,7,237,0,21,247,0,25,237,0,27,237,0,29,237,0,78,250,0,79,247,0,
+ 82,250,0,83,237,0,87,237,0,89,237,0,91,237,0,109,247,0,113,237,0,
+ 115,237,0,117,237,13,45,250,47,250,66,250,193,250,194,250,195,250,
+ 196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,13,45,250,47,250,
+ 66,250,193,250,194,250,195,250,196,250,197,250,198,250,0,2,250,0,
+ 4,250,0,6,250,13,45,250,47,250,66,250,193,250,194,250,195,250,196,
+ 250,197,250,198,250,0,2,250,0,4,250,0,6,250,13,45,250,47,250,66,
+ 250,193,250,194,250,195,250,196,250,197,250,198,250,0,2,250,0,4,
+ 250,0,6,250,65,45,234,46,234,47,234,59,247,60,247,66,239,80,243,
+ 98,234,102,234,106,254,112,234,118,239,193,239,194,239,195,239,196,
+ 239,197,239,198,239,211,243,212,243,213,243,214,243,215,243,217,
+ 243,225,234,226,234,227,234,228,245,229,234,230,234,233,234,234,
+ 234,235,234,236,234,238,254,243,234,244,234,245,234,246,234,247,
+ 234,249,234,250,239,251,239,252,239,253,239,0,2,239,0,3,245,0,4,
+ 239,0,5,245,0,6,239,0,7,234,0,21,245,0,25,234,0,27,234,0,29,234,
+ 0,49,254,0,78,243,0,79,234,0,82,243,0,83,234,0,109,239,0,113,239,
+ 0,115,239,0,117,239,19,102,254,112,254,233,254,234,254,235,254,236,
+ 254,243,254,244,254,245,254,246,254,247,254,249,254,0,21,254,0,25,
+ 254,0,27,254,0,29,254,0,79,254,0,83,254,19,102,254,112,254,233,254,
+ 234,254,235,254,236,254,243,254,244,254,245,254,246,254,247,254,
+ 249,254,0,21,254,0,25,254,0,27,254,0,29,254,0,79,254,0,83,254,19,
+ 102,254,112,254,233,254,234,254,235,254,236,254,243,254,244,254,
+ 245,254,246,254,247,254,249,254,0,21,254,0,25,254,0,27,254,0,29,
+ 254,0,79,254,0,83,254,3,45,254,47,254,4,45,254,47,254,120,252,2,
+ 1,32,24,247,14,33,245,101,249,115,249,116,249,0,19,249,0,87,249,
+ 0,89,249,0,91,249,0,93,249,0,97,249,0,99,249,1,2,25,249,1,32,25,
+ 247,2,33,250
+};
+static afm_cuint16 afm_Helvetica_highchars_index[] = { /* 220 */
+ 161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+ 178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+ 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+ 210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+ 226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+ 242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+ 258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+ 280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+ 313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+ 336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+ 354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+ 380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+ 8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+ 8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+ 64257,64258
+};
+static afm_cunicode afm_Helvetica_ligatures[] = { /* 3 */
+ 102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Helvetica-Bold */
+/* FullName: Helvetica Bold */
+/* FamilyName: Helvetica */
+static afm_cuint8 afm_Helvetica_Bold_widths[] = { /* 315 */
+ 46,56,79,93,93,148,120,40,56,56,65,97,46,56,46,46,93,93,93,93,93,
+ 93,93,93,93,93,56,56,97,97,97,102,163,120,120,120,120,111,102,130,
+ 120,46,93,120,102,139,120,130,111,130,120,111,102,120,111,157,111,
+ 111,102,56,46,56,97,93,56,93,102,93,102,93,56,102,102,46,46,93,46,
+ 148,102,102,102,102,65,93,56,102,93,130,93,93,83,65,47,65,97,56,
+ 93,93,93,93,47,93,56,123,62,93,97,123,56,67,97,56,56,56,102,93,46,
+ 56,56,61,93,139,139,139,102,120,120,120,120,120,120,167,120,111,
+ 111,111,111,46,46,46,46,120,120,130,130,130,130,130,97,130,120,120,
+ 120,120,111,111,102,93,93,93,93,93,93,148,93,93,93,93,93,46,46,46,
+ 46,102,102,102,102,102,102,102,97,102,102,102,102,102,93,102,93,
+ 120,93,120,93,120,93,120,93,120,93,120,124,120,102,111,93,111,93,
+ 111,93,111,93,130,102,130,102,46,46,46,46,46,46,120,93,102,46,102,
+ 46,102,67,102,46,120,102,120,102,120,102,130,102,130,102,167,157,
+ 120,65,120,65,120,65,111,93,111,93,111,93,102,56,102,65,120,102,
+ 120,102,120,102,120,102,111,102,83,102,83,102,83,93,111,93,56,56,
+ 56,56,56,56,56,56,93,167,46,46,46,83,83,83,93,93,58,167,167,56,56,
+ 28,93,167,82,102,100,97,92,92,92,92,82,42,102,102
+};
+static afm_sint16 afm_Helvetica_Bold_kerning_index[] = { /* 315 */
+ 1,0,0,0,0,0,0,0,0,0,0,0,29,0,40,0,0,0,0,0,0,0,0,0,0,0,51,54,0,0,
+ 0,0,0,57,172,0,218,0,257,0,0,0,308,358,450,0,0,485,534,627,654,0,
+ 716,874,902,1051,0,1199,0,0,0,0,0,0,0,1338,1358,1401,1427,1444,1462,
+ 1517,1548,0,0,1556,1577,1587,1617,1649,1663,0,1671,1752,0,0,1755,
+ 1803,1828,1851,1921,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,1944,2059,2174,2289,2404,2519,0,0,0,0,0,
+ 0,0,0,0,0,0,0,2634,2683,2732,2781,2830,0,2879,2928,2956,2984,3012,
+ 3040,0,0,3179,3199,3219,3239,3259,3279,0,3299,3325,3343,3361,3379,
+ 0,0,0,0,0,3397,3429,3443,3457,3471,3485,0,3499,0,0,0,0,3513,0,3583,
+ 3653,3768,3788,3903,3923,4038,0,4058,0,4084,4110,0,4149,4188,0,4205,
+ 0,4223,0,4241,0,4259,0,4277,0,4308,0,0,0,0,0,0,4339,4431,4452,4487,
+ 4497,4532,0,0,4542,4577,0,4587,0,4619,0,4651,4683,4732,4746,4795,
+ 0,0,4809,4871,4952,5014,5095,5157,0,5238,0,5241,0,5244,5247,0,5405,
+ 0,5563,0,5591,0,5619,0,5647,0,5675,0,5814,0,5837,0,5860,0,0,5883,
+ 0,0,0,0,0,0,0,0,0,0,5886,5891,0,0,5942,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0
+};
+static afm_cuint8 afm_Helvetica_Bold_kerning_data[] = { /* 5945 */
+ 42,11,85,240,87,244,88,244,90,237,222,237,0,100,240,0,102,240,0,
+ 122,237,1,32,24,247,1,32,28,244,4,33,250,1,32,25,237,1,32,29,237,
+ 4,33,250,1,32,25,237,1,32,29,237,2,33,250,2,33,250,49,68,250,72,
+ 249,80,250,82,250,85,242,86,249,87,244,88,247,90,239,118,252,119,
+ 250,120,252,122,252,200,250,211,250,212,250,213,250,214,250,215,
+ 250,217,250,218,249,219,249,220,249,221,249,222,239,250,252,251,
+ 252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,0,32,249,0,
+ 36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,0,109,252,
+ 0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,
+ 239,20,66,252,86,255,193,252,194,252,195,252,196,252,197,252,198,
+ 252,218,255,219,255,220,255,221,255,0,2,252,0,4,252,0,6,252,0,108,
+ 255,0,112,255,0,114,255,0,116,255,18,45,252,47,252,66,250,87,250,
+ 88,250,90,245,193,250,194,250,195,250,196,250,197,250,198,250,222,
+ 245,0,2,250,0,4,250,0,6,250,0,122,245,23,45,240,47,240,66,244,98,
+ 254,193,244,194,244,195,244,196,244,197,244,198,244,225,254,226,
+ 254,227,254,228,254,229,254,230,254,0,2,244,0,3,254,0,4,244,0,5,
+ 254,0,6,244,0,7,254,22,45,254,47,254,66,254,118,254,193,254,194,
+ 254,195,254,196,254,197,254,198,254,250,254,251,254,252,254,253,
+ 254,0,2,254,0,4,254,0,6,254,0,109,254,0,113,254,0,115,254,0,117,
+ 254,40,80,252,102,254,112,251,118,252,122,250,211,252,212,252,213,
+ 252,214,252,215,252,217,252,233,254,234,254,235,254,236,254,243,
+ 251,244,251,245,251,246,251,247,251,249,251,250,252,251,252,252,
+ 252,253,252,254,250,0,1,250,0,21,254,0,25,254,0,27,254,0,29,254,
+ 0,78,252,0,79,251,0,82,252,0,83,251,0,109,252,0,113,252,0,115,252,
+ 0,117,252,14,85,242,87,239,88,244,90,237,122,252,222,237,254,252,
+ 0,1,252,0,100,242,0,102,242,0,122,237,1,32,25,234,1,32,29,234,22,
+ 45,250,47,250,66,249,85,250,87,249,88,249,89,249,90,245,193,249,
+ 194,249,195,249,196,249,197,249,198,249,222,245,0,2,249,0,4,249,
+ 0,6,249,0,100,250,0,102,250,0,122,245,41,45,237,47,237,66,240,98,
+ 252,102,252,112,250,193,240,194,240,195,240,196,240,197,240,198,
+ 240,225,252,226,252,227,252,228,252,229,252,230,252,233,252,234,
+ 252,235,252,236,252,243,250,244,250,245,250,246,250,247,250,249,
+ 250,0,2,240,0,3,252,0,4,240,0,5,252,0,6,240,0,7,252,0,21,252,0,25,
+ 252,0,27,252,0,29,252,0,79,250,0,83,250,12,45,3,47,3,86,255,218,
+ 255,219,255,220,255,221,255,0,108,255,0,112,255,0,114,255,0,116,
+ 255,27,80,254,85,254,86,254,87,249,88,250,90,249,211,254,212,254,
+ 213,254,214,254,215,254,217,254,218,254,219,254,220,254,221,254,
+ 222,249,0,78,254,0,82,254,0,100,254,0,102,254,0,108,254,0,112,254,
+ 0,114,254,0,116,254,0,122,249,69,45,244,46,237,47,244,59,250,60,
+ 250,66,242,80,250,98,244,102,247,112,244,115,244,118,242,120,247,
+ 122,247,193,242,194,242,195,242,196,242,197,242,198,242,211,250,
+ 212,250,213,250,214,250,215,250,217,250,225,244,226,244,227,244,
+ 228,244,229,244,230,244,233,247,234,247,235,247,236,247,243,244,
+ 244,244,245,244,246,244,247,244,249,244,250,242,251,242,252,242,
+ 253,242,254,247,0,1,247,0,2,242,0,3,244,0,4,242,0,5,244,0,6,242,
+ 0,7,244,0,21,247,0,25,247,0,27,247,0,29,247,0,78,250,0,79,244,0,
+ 82,250,0,83,244,0,87,244,0,89,244,0,109,242,0,113,242,0,115,242,
+ 0,117,242,13,45,252,47,252,66,249,193,249,194,249,195,249,196,249,
+ 197,249,198,249,0,2,249,0,4,249,0,6,249,65,45,237,46,244,47,237,
+ 59,250,60,250,66,244,72,249,80,249,98,247,102,249,112,242,118,247,
+ 193,244,194,244,195,244,196,244,197,244,198,244,211,249,212,249,
+ 213,249,214,249,215,249,217,249,225,247,226,247,227,247,228,247,
+ 229,247,230,247,233,249,234,249,235,249,236,249,243,242,244,242,
+ 245,242,246,242,247,242,249,242,250,247,251,247,252,247,253,247,
+ 0,2,244,0,3,247,0,4,244,0,5,247,0,6,244,0,7,247,0,21,249,0,25,249,
+ 0,27,249,0,29,249,0,32,249,0,36,249,0,78,249,0,79,242,0,82,249,0,
+ 83,242,0,109,247,0,113,247,0,115,247,0,117,247,65,45,244,46,250,
+ 47,244,59,255,60,255,66,247,80,254,98,250,102,251,112,247,118,249,
+ 122,254,193,247,194,247,195,247,196,247,197,247,198,247,211,254,
+ 212,254,213,254,214,254,215,254,217,254,225,250,226,250,227,250,
+ 228,250,229,250,230,250,233,251,234,251,235,251,236,251,243,247,
+ 244,247,245,247,246,247,247,247,249,247,250,249,251,249,252,249,
+ 253,249,254,254,0,1,254,0,2,247,0,3,250,0,4,247,0,5,250,0,6,247,
+ 0,7,250,0,21,251,0,25,251,0,27,251,0,29,251,0,78,254,0,79,247,0,
+ 82,254,0,83,247,0,109,249,0,113,249,0,115,249,0,117,249,61,45,240,
+ 47,240,59,249,60,249,66,239,80,245,98,242,102,244,112,240,118,240,
+ 193,239,194,239,195,239,196,239,197,239,198,239,211,245,212,245,
+ 213,245,214,245,215,245,217,245,225,242,226,242,227,242,228,242,
+ 229,242,230,242,233,244,234,244,235,244,236,244,243,240,244,240,
+ 245,240,246,240,247,240,249,240,250,240,251,240,252,240,253,240,
+ 0,2,239,0,3,242,0,4,239,0,5,242,0,6,239,0,7,242,0,21,244,0,25,244,
+ 0,27,244,0,29,244,0,78,245,0,79,240,0,82,245,0,83,240,0,109,240,
+ 0,113,240,0,115,240,0,117,240,9,104,255,119,254,120,254,122,254,
+ 254,254,0,1,254,0,33,255,0,37,255,18,109,255,118,254,119,254,122,
+ 254,250,254,251,254,252,254,253,254,254,254,0,1,254,0,60,255,0,62,
+ 255,0,68,255,0,109,254,0,113,254,0,115,254,0,117,254,11,105,255,
+ 108,254,109,254,122,255,254,255,0,1,255,0,57,254,0,60,254,0,62,254,
+ 0,68,254,8,101,255,119,254,120,254,122,254,254,254,0,1,254,0,19,
+ 255,9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,254,
+ 23,45,255,47,255,102,255,112,254,233,255,234,255,235,255,236,255,
+ 243,254,244,254,245,254,246,254,247,254,249,254,0,21,255,0,25,255,
+ 0,27,255,0,29,255,0,79,254,0,83,254,1,32,25,5,1,32,29,5,13,102,2,
+ 104,255,233,2,234,2,235,2,236,2,0,21,2,0,25,2,0,27,2,0,29,2,0,33,
+ 255,0,37,255,4,122,254,254,254,0,1,254,10,112,254,243,254,244,254,
+ 245,254,246,254,247,254,249,254,0,79,254,0,83,254,5,120,254,122,
+ 254,254,254,0,1,254,13,118,254,122,252,250,254,251,254,252,254,253,
+ 254,254,252,0,1,252,0,109,254,0,113,254,0,115,254,0,117,254,14,118,
+ 255,119,250,122,254,250,255,251,255,252,255,253,255,254,254,0,1,
+ 254,0,109,255,0,113,255,0,115,255,0,117,255,7,119,254,120,254,121,
+ 252,122,254,254,254,0,1,254,4,122,254,254,254,0,1,254,34,45,247,
+ 46,254,47,247,100,254,101,254,104,254,112,254,114,254,116,254,117,
+ 3,119,2,122,2,232,254,243,254,244,254,245,254,246,254,247,254,249,
+ 254,254,2,0,1,2,0,9,254,0,15,254,0,19,254,0,33,254,0,37,254,0,79,
+ 254,0,83,254,0,93,254,0,97,254,0,99,254,0,101,3,1,2,25,254,2,120,
+ 254,22,45,244,47,244,98,254,112,252,225,254,226,254,227,254,228,
+ 254,229,254,230,254,243,252,244,252,245,252,246,252,247,252,249,
+ 252,0,3,254,0,5,254,0,7,254,0,79,252,0,83,252,12,45,250,47,250,112,
+ 254,243,254,244,254,245,254,246,254,247,254,249,254,0,79,254,0,83,
+ 254,10,102,255,233,255,234,255,235,255,236,255,0,21,255,0,25,255,
+ 0,27,255,0,29,255,31,45,244,47,244,98,252,102,255,112,253,225,252,
+ 226,252,227,252,228,252,229,252,230,252,233,255,234,255,235,255,
+ 236,255,243,253,244,253,245,253,246,253,247,253,249,253,0,3,252,
+ 0,5,252,0,7,252,0,21,255,0,25,255,0,27,255,0,29,255,0,79,253,0,83,
+ 253,10,102,2,233,2,234,2,235,2,236,2,0,21,2,0,25,2,0,27,2,0,29,2,
+ 49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,88,247,90,239,
+ 118,252,119,250,120,252,122,252,200,250,211,250,212,250,213,250,
+ 214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,239,
+ 250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,
+ 0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,
+ 0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,
+ 252,0,122,239,49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,
+ 88,247,90,239,118,252,119,250,120,252,122,252,200,250,211,250,212,
+ 250,213,250,214,250,215,250,217,250,218,249,219,249,220,249,221,
+ 249,222,239,250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,
+ 250,0,14,250,0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,
+ 242,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,
+ 0,116,249,0,117,252,0,122,239,49,68,250,72,249,80,250,82,250,85,
+ 242,86,249,87,244,88,247,90,239,118,252,119,250,120,252,122,252,
+ 200,250,211,250,212,250,213,250,214,250,215,250,217,250,218,249,
+ 219,249,220,249,221,249,222,239,250,252,251,252,252,252,253,252,
+ 254,252,0,1,252,0,8,250,0,14,250,0,32,249,0,36,249,0,78,250,0,82,
+ 250,0,100,242,0,102,242,0,108,249,0,109,252,0,112,249,0,113,252,
+ 0,114,249,0,115,252,0,116,249,0,117,252,0,122,239,49,68,250,72,249,
+ 80,250,82,250,85,242,86,249,87,244,88,247,90,239,118,252,119,250,
+ 120,252,122,252,200,250,211,250,212,250,213,250,214,250,215,250,
+ 217,250,218,249,219,249,220,249,221,249,222,239,250,252,251,252,
+ 252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,0,32,249,0,36,249,
+ 0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,0,109,252,0,112,
+ 249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,239,
+ 49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,88,247,90,239,
+ 118,252,119,250,120,252,122,252,200,250,211,250,212,250,213,250,
+ 214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,239,
+ 250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,
+ 0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,
+ 0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,
+ 252,0,122,239,49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,
+ 88,247,90,239,118,252,119,250,120,252,122,252,200,250,211,250,212,
+ 250,213,250,214,250,215,250,217,250,218,249,219,249,220,249,221,
+ 249,222,239,250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,
+ 250,0,14,250,0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,
+ 242,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,
+ 0,116,249,0,117,252,0,122,239,22,45,250,47,250,66,249,85,250,87,
+ 249,88,249,89,249,90,245,193,249,194,249,195,249,196,249,197,249,
+ 198,249,222,245,0,2,249,0,4,249,0,6,249,0,100,250,0,102,250,0,122,
+ 245,22,45,250,47,250,66,249,85,250,87,249,88,249,89,249,90,245,193,
+ 249,194,249,195,249,196,249,197,249,198,249,222,245,0,2,249,0,4,
+ 249,0,6,249,0,100,250,0,102,250,0,122,245,22,45,250,47,250,66,249,
+ 85,250,87,249,88,249,89,249,90,245,193,249,194,249,195,249,196,249,
+ 197,249,198,249,222,245,0,2,249,0,4,249,0,6,249,0,100,250,0,102,
+ 250,0,122,245,22,45,250,47,250,66,249,85,250,87,249,88,249,89,249,
+ 90,245,193,249,194,249,195,249,196,249,197,249,198,249,222,245,0,
+ 2,249,0,4,249,0,6,249,0,100,250,0,102,250,0,122,245,22,45,250,47,
+ 250,66,249,85,250,87,249,88,249,89,249,90,245,193,249,194,249,195,
+ 249,196,249,197,249,198,249,222,245,0,2,249,0,4,249,0,6,249,0,100,
+ 250,0,102,250,0,122,245,22,45,250,47,250,66,249,85,250,87,249,88,
+ 249,89,249,90,245,193,249,194,249,195,249,196,249,197,249,198,249,
+ 222,245,0,2,249,0,4,249,0,6,249,0,100,250,0,102,250,0,122,245,13,
+ 45,252,47,252,66,249,193,249,194,249,195,249,196,249,197,249,198,
+ 249,0,2,249,0,4,249,0,6,249,13,45,252,47,252,66,249,193,249,194,
+ 249,195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,13,45,
+ 252,47,252,66,249,193,249,194,249,195,249,196,249,197,249,198,249,
+ 0,2,249,0,4,249,0,6,249,13,45,252,47,252,66,249,193,249,194,249,
+ 195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,61,45,240,
+ 47,240,59,249,60,249,66,239,80,245,98,242,102,244,112,240,118,240,
+ 193,239,194,239,195,239,196,239,197,239,198,239,211,245,212,245,
+ 213,245,214,245,215,245,217,245,225,242,226,242,227,242,228,242,
+ 229,242,230,242,233,244,234,244,235,244,236,244,243,240,244,240,
+ 245,240,246,240,247,240,249,240,250,240,251,240,252,240,253,240,
+ 0,2,239,0,3,242,0,4,239,0,5,242,0,6,239,0,7,242,0,21,244,0,25,244,
+ 0,27,244,0,29,244,0,78,245,0,79,240,0,82,245,0,83,240,0,109,240,
+ 0,113,240,0,115,240,0,117,240,9,104,255,119,254,120,254,122,254,
+ 254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,254,120,254,122,
+ 254,254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,254,120,254,
+ 122,254,254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,254,120,
+ 254,122,254,254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,254,
+ 120,254,122,254,254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,
+ 254,120,254,122,254,254,254,0,1,254,0,33,255,0,37,255,11,105,255,
+ 108,254,109,254,122,255,254,255,0,1,255,0,57,254,0,60,254,0,62,254,
+ 0,68,254,9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,
+ 254,9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,254,
+ 9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,254,9,45,
+ 2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,254,14,118,255,
+ 119,250,122,254,250,255,251,255,252,255,253,255,254,254,0,1,254,
+ 0,109,255,0,113,255,0,115,255,0,117,255,7,119,254,120,254,121,252,
+ 122,254,254,254,0,1,254,7,119,254,120,254,121,252,122,254,254,254,
+ 0,1,254,7,119,254,120,254,121,252,122,254,254,254,0,1,254,7,119,
+ 254,120,254,121,252,122,254,254,254,0,1,254,7,119,254,120,254,121,
+ 252,122,254,254,254,0,1,254,7,119,254,120,254,121,252,122,254,254,
+ 254,0,1,254,31,45,244,47,244,98,252,102,255,112,253,225,252,226,
+ 252,227,252,228,252,229,252,230,252,233,255,234,255,235,255,236,
+ 255,243,253,244,253,245,253,246,253,247,253,249,253,0,3,252,0,5,
+ 252,0,7,252,0,21,255,0,25,255,0,27,255,0,29,255,0,79,253,0,83,253,
+ 31,45,244,47,244,98,252,102,255,112,253,225,252,226,252,227,252,
+ 228,252,229,252,230,252,233,255,234,255,235,255,236,255,243,253,
+ 244,253,245,253,246,253,247,253,249,253,0,3,252,0,5,252,0,7,252,
+ 0,21,255,0,25,255,0,27,255,0,29,255,0,79,253,0,83,253,49,68,250,
+ 72,249,80,250,82,250,85,242,86,249,87,244,88,247,90,239,118,252,
+ 119,250,120,252,122,252,200,250,211,250,212,250,213,250,214,250,
+ 215,250,217,250,218,249,219,249,220,249,221,249,222,239,250,252,
+ 251,252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,0,32,249,
+ 0,36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,0,109,252,
+ 0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,
+ 239,9,104,255,119,254,120,254,122,254,254,254,0,1,254,0,33,255,0,
+ 37,255,49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,88,247,
+ 90,239,118,252,119,250,120,252,122,252,200,250,211,250,212,250,213,
+ 250,214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,
+ 239,250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,
+ 250,0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,
+ 249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,
+ 0,117,252,0,122,239,9,104,255,119,254,120,254,122,254,254,254,0,
+ 1,254,0,33,255,0,37,255,49,68,250,72,249,80,250,82,250,85,242,86,
+ 249,87,244,88,247,90,239,118,252,119,250,120,252,122,252,200,250,
+ 211,250,212,250,213,250,214,250,215,250,217,250,218,249,219,249,
+ 220,249,221,249,222,239,250,252,251,252,252,252,253,252,254,252,
+ 0,1,252,0,8,250,0,14,250,0,32,249,0,36,249,0,78,250,0,82,250,0,100,
+ 242,0,102,242,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,
+ 0,115,252,0,116,249,0,117,252,0,122,239,9,104,255,119,254,120,254,
+ 122,254,254,254,0,1,254,0,33,255,0,37,255,11,105,255,108,254,109,
+ 254,122,255,254,255,0,1,255,0,57,254,0,60,254,0,62,254,0,68,254,
+ 11,105,255,108,254,109,254,122,255,254,255,0,1,255,0,57,254,0,60,
+ 254,0,62,254,0,68,254,18,45,252,47,252,66,250,87,250,88,250,90,245,
+ 193,250,194,250,195,250,196,250,197,250,198,250,222,245,0,2,250,
+ 0,4,250,0,6,250,0,122,245,18,45,252,47,252,66,250,87,250,88,250,
+ 90,245,193,250,194,250,195,250,196,250,197,250,198,250,222,245,0,
+ 2,250,0,4,250,0,6,250,0,122,245,8,101,255,119,254,120,254,122,254,
+ 254,254,0,1,254,0,19,255,9,45,2,47,3,119,254,120,254,121,254,122,
+ 254,254,254,0,1,254,9,45,2,47,3,119,254,120,254,121,254,122,254,
+ 254,254,0,1,254,9,45,2,47,3,119,254,120,254,121,254,122,254,254,
+ 254,0,1,254,9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,
+ 0,1,254,13,102,2,104,255,233,2,234,2,235,2,236,2,0,21,2,0,25,2,0,
+ 27,2,0,29,2,0,33,255,0,37,255,13,102,2,104,255,233,2,234,2,235,2,
+ 236,2,0,21,2,0,25,2,0,27,2,0,29,2,0,33,255,0,37,255,40,80,252,102,
+ 254,112,251,118,252,122,250,211,252,212,252,213,252,214,252,215,
+ 252,217,252,233,254,234,254,235,254,236,254,243,251,244,251,245,
+ 251,246,251,247,251,249,251,250,252,251,252,252,252,253,252,254,
+ 250,0,1,250,0,21,254,0,25,254,0,27,254,0,29,254,0,78,252,0,79,251,
+ 0,82,252,0,83,251,0,109,252,0,113,252,0,115,252,0,117,252,10,112,
+ 254,243,254,244,254,245,254,246,254,247,254,249,254,0,79,254,0,83,
+ 254,14,85,242,87,239,88,244,90,237,122,252,222,237,254,252,0,1,252,
+ 0,100,242,0,102,242,0,122,237,1,32,25,234,1,32,29,234,5,120,254,
+ 122,254,254,254,0,1,254,14,85,242,87,239,88,244,90,237,122,252,222,
+ 237,254,252,0,1,252,0,100,242,0,102,242,0,122,237,1,32,25,234,1,
+ 32,29,234,5,120,254,122,254,254,254,0,1,254,14,85,242,87,239,88,
+ 244,90,237,122,252,222,237,254,252,0,1,252,0,100,242,0,102,242,0,
+ 122,237,1,32,25,234,1,32,29,234,5,120,254,122,254,254,254,0,1,254,
+ 14,118,255,119,250,122,254,250,255,251,255,252,255,253,255,254,254,
+ 0,1,254,0,109,255,0,113,255,0,115,255,0,117,255,14,118,255,119,250,
+ 122,254,250,255,251,255,252,255,253,255,254,254,0,1,254,0,109,255,
+ 0,113,255,0,115,255,0,117,255,14,118,255,119,250,122,254,250,255,
+ 251,255,252,255,253,255,254,254,0,1,254,0,109,255,0,113,255,0,115,
+ 255,0,117,255,22,45,250,47,250,66,249,85,250,87,249,88,249,89,249,
+ 90,245,193,249,194,249,195,249,196,249,197,249,198,249,222,245,0,
+ 2,249,0,4,249,0,6,249,0,100,250,0,102,250,0,122,245,7,119,254,120,
+ 254,121,252,122,254,254,254,0,1,254,22,45,250,47,250,66,249,85,250,
+ 87,249,88,249,89,249,90,245,193,249,194,249,195,249,196,249,197,
+ 249,198,249,222,245,0,2,249,0,4,249,0,6,249,0,100,250,0,102,250,
+ 0,122,245,7,119,254,120,254,121,252,122,254,254,254,0,1,254,27,80,
+ 254,85,254,86,254,87,249,88,250,90,249,211,254,212,254,213,254,214,
+ 254,215,254,217,254,218,254,219,254,220,254,221,254,222,249,0,78,
+ 254,0,82,254,0,100,254,0,102,254,0,108,254,0,112,254,0,114,254,0,
+ 116,254,0,122,249,34,45,247,46,254,47,247,100,254,101,254,104,254,
+ 112,254,114,254,116,254,117,3,119,2,122,2,232,254,243,254,244,254,
+ 245,254,246,254,247,254,249,254,254,2,0,1,2,0,9,254,0,15,254,0,19,
+ 254,0,33,254,0,37,254,0,79,254,0,83,254,0,93,254,0,97,254,0,99,254,
+ 0,101,3,1,2,25,254,27,80,254,85,254,86,254,87,249,88,250,90,249,
+ 211,254,212,254,213,254,214,254,215,254,217,254,218,254,219,254,
+ 220,254,221,254,222,249,0,78,254,0,82,254,0,100,254,0,102,254,0,
+ 108,254,0,112,254,0,114,254,0,116,254,0,122,249,34,45,247,46,254,
+ 47,247,100,254,101,254,104,254,112,254,114,254,116,254,117,3,119,
+ 2,122,2,232,254,243,254,244,254,245,254,246,254,247,254,249,254,
+ 254,2,0,1,2,0,9,254,0,15,254,0,19,254,0,33,254,0,37,254,0,79,254,
+ 0,83,254,0,93,254,0,97,254,0,99,254,0,101,3,1,2,25,254,27,80,254,
+ 85,254,86,254,87,249,88,250,90,249,211,254,212,254,213,254,214,254,
+ 215,254,217,254,218,254,219,254,220,254,221,254,222,249,0,78,254,
+ 0,82,254,0,100,254,0,102,254,0,108,254,0,112,254,0,114,254,0,116,
+ 254,0,122,249,34,45,247,46,254,47,247,100,254,101,254,104,254,112,
+ 254,114,254,116,254,117,3,119,2,122,2,232,254,243,254,244,254,245,
+ 254,246,254,247,254,249,254,254,2,0,1,2,0,9,254,0,15,254,0,19,254,
+ 0,33,254,0,37,254,0,79,254,0,83,254,0,93,254,0,97,254,0,99,254,0,
+ 101,3,1,2,25,254,2,120,254,2,120,254,2,120,254,69,45,244,46,237,
+ 47,244,59,250,60,250,66,242,80,250,98,244,102,247,112,244,115,244,
+ 118,242,120,247,122,247,193,242,194,242,195,242,196,242,197,242,
+ 198,242,211,250,212,250,213,250,214,250,215,250,217,250,225,244,
+ 226,244,227,244,228,244,229,244,230,244,233,247,234,247,235,247,
+ 236,247,243,244,244,244,245,244,246,244,247,244,249,244,250,242,
+ 251,242,252,242,253,242,254,247,0,1,247,0,2,242,0,3,244,0,4,242,
+ 0,5,244,0,6,242,0,7,244,0,21,247,0,25,247,0,27,247,0,29,247,0,78,
+ 250,0,79,244,0,82,250,0,83,244,0,87,244,0,89,244,0,109,242,0,113,
+ 242,0,115,242,0,117,242,69,45,244,46,237,47,244,59,250,60,250,66,
+ 242,80,250,98,244,102,247,112,244,115,244,118,242,120,247,122,247,
+ 193,242,194,242,195,242,196,242,197,242,198,242,211,250,212,250,
+ 213,250,214,250,215,250,217,250,225,244,226,244,227,244,228,244,
+ 229,244,230,244,233,247,234,247,235,247,236,247,243,244,244,244,
+ 245,244,246,244,247,244,249,244,250,242,251,242,252,242,253,242,
+ 254,247,0,1,247,0,2,242,0,3,244,0,4,242,0,5,244,0,6,242,0,7,244,
+ 0,21,247,0,25,247,0,27,247,0,29,247,0,78,250,0,79,244,0,82,250,0,
+ 83,244,0,87,244,0,89,244,0,109,242,0,113,242,0,115,242,0,117,242,
+ 13,45,252,47,252,66,249,193,249,194,249,195,249,196,249,197,249,
+ 198,249,0,2,249,0,4,249,0,6,249,13,45,252,47,252,66,249,193,249,
+ 194,249,195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,
+ 13,45,252,47,252,66,249,193,249,194,249,195,249,196,249,197,249,
+ 198,249,0,2,249,0,4,249,0,6,249,13,45,252,47,252,66,249,193,249,
+ 194,249,195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,
+ 61,45,240,47,240,59,249,60,249,66,239,80,245,98,242,102,244,112,
+ 240,118,240,193,239,194,239,195,239,196,239,197,239,198,239,211,
+ 245,212,245,213,245,214,245,215,245,217,245,225,242,226,242,227,
+ 242,228,242,229,242,230,242,233,244,234,244,235,244,236,244,243,
+ 240,244,240,245,240,246,240,247,240,249,240,250,240,251,240,252,
+ 240,253,240,0,2,239,0,3,242,0,4,239,0,5,242,0,6,239,0,7,242,0,21,
+ 244,0,25,244,0,27,244,0,29,244,0,78,245,0,79,240,0,82,245,0,83,240,
+ 0,109,240,0,113,240,0,115,240,0,117,240,10,102,2,233,2,234,2,235,
+ 2,236,2,0,21,2,0,25,2,0,27,2,0,29,2,10,102,2,233,2,234,2,235,2,236,
+ 2,0,21,2,0,25,2,0,27,2,0,29,2,10,102,2,233,2,234,2,235,2,236,2,0,
+ 21,2,0,25,2,0,27,2,0,29,2,2,120,254,2,1,32,24,249,19,33,244,101,
+ 244,109,254,115,250,116,247,119,254,0,19,244,0,60,254,0,62,254,0,
+ 68,254,0,87,250,0,89,250,0,91,250,0,93,247,0,97,247,0,99,247,1,2,
+ 25,247,1,32,25,249,2,33,244
+};
+static afm_cuint16 afm_Helvetica_Bold_highchars_index[] = { /* 220 */
+ 161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+ 178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+ 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+ 210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+ 226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+ 242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+ 258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+ 280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+ 313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+ 336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+ 354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+ 380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+ 8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+ 8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+ 64257,64258
+};
+static afm_cunicode afm_Helvetica_Bold_ligatures[] = { /* 3 */
+ 102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Helvetica-BoldOblique */
+/* FullName: Helvetica Bold Oblique */
+/* FamilyName: Helvetica */
+static afm_cuint8 afm_Helvetica_BoldOblique_widths[] = { /* 315 */
+ 46,56,79,93,93,148,120,40,56,56,65,97,46,56,46,46,93,93,93,93,93,
+ 93,93,93,93,93,56,56,97,97,97,102,163,120,120,120,120,111,102,130,
+ 120,46,93,120,102,139,120,130,111,130,120,111,102,120,111,157,111,
+ 111,102,56,46,56,97,93,56,93,102,93,102,93,56,102,102,46,46,93,46,
+ 148,102,102,102,102,65,93,56,102,93,130,93,93,83,65,47,65,97,56,
+ 93,93,93,93,47,93,56,123,62,93,97,123,56,67,97,56,56,56,102,93,46,
+ 56,56,61,93,139,139,139,102,120,120,120,120,120,120,167,120,111,
+ 111,111,111,46,46,46,46,120,120,130,130,130,130,130,97,130,120,120,
+ 120,120,111,111,102,93,93,93,93,93,93,148,93,93,93,93,93,46,46,46,
+ 46,102,102,102,102,102,102,102,97,102,102,102,102,102,93,102,93,
+ 120,93,120,93,120,93,120,93,120,93,120,124,120,102,111,93,111,93,
+ 111,93,111,93,130,102,130,102,46,46,46,46,46,46,120,93,102,46,102,
+ 46,102,67,102,46,120,102,120,102,120,102,130,102,130,102,167,157,
+ 120,65,120,65,120,65,111,93,111,93,111,93,102,56,102,65,120,102,
+ 120,102,120,102,120,102,111,102,83,102,83,102,83,93,111,93,56,56,
+ 56,56,56,56,56,56,93,167,46,46,46,83,83,83,93,93,58,167,167,56,56,
+ 28,93,167,82,102,100,97,92,92,92,92,82,42,102,102
+};
+static afm_sint16 afm_Helvetica_BoldOblique_kerning_index[] = { /* 315 */
+ 1,0,0,0,0,0,0,0,0,0,0,0,29,0,40,0,0,0,0,0,0,0,0,0,0,0,51,54,0,0,
+ 0,0,0,57,172,0,218,0,257,0,0,0,308,358,450,0,0,485,534,627,654,0,
+ 716,874,902,1051,0,1199,0,0,0,0,0,0,0,1338,1358,1401,1427,1444,1462,
+ 1517,1548,0,0,1556,1577,1587,1617,1649,1663,0,1671,1752,0,0,1755,
+ 1803,1828,1851,1921,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,1944,2059,2174,2289,2404,2519,0,0,0,0,0,
+ 0,0,0,0,0,0,0,2634,2683,2732,2781,2830,0,2879,2928,2956,2984,3012,
+ 3040,0,0,3179,3199,3219,3239,3259,3279,0,3299,3325,3343,3361,3379,
+ 0,0,0,0,0,3397,3429,3443,3457,3471,3485,0,3499,0,0,0,0,3513,0,3583,
+ 3653,3768,3788,3903,3923,4038,0,4058,0,4084,4110,0,4149,4188,0,4205,
+ 0,4223,0,4241,0,4259,0,4277,0,4308,0,0,0,0,0,0,4339,4431,4452,4487,
+ 4497,4532,0,0,4542,4577,0,4587,0,4619,0,4651,4683,4732,4746,4795,
+ 0,0,4809,4871,4952,5014,5095,5157,0,5238,0,5241,0,5244,5247,0,5405,
+ 0,5563,0,5591,0,5619,0,5647,0,5675,0,5814,0,5837,0,5860,0,0,5883,
+ 0,0,0,0,0,0,0,0,0,0,5886,5891,0,0,5942,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0
+};
+static afm_cuint8 afm_Helvetica_BoldOblique_kerning_data[] = { /* 5945 */
+ 42,11,85,240,87,244,88,244,90,237,222,237,0,100,240,0,102,240,0,
+ 122,237,1,32,24,247,1,32,28,244,4,33,250,1,32,25,237,1,32,29,237,
+ 4,33,250,1,32,25,237,1,32,29,237,2,33,250,2,33,250,49,68,250,72,
+ 249,80,250,82,250,85,242,86,249,87,244,88,247,90,239,118,252,119,
+ 250,120,252,122,252,200,250,211,250,212,250,213,250,214,250,215,
+ 250,217,250,218,249,219,249,220,249,221,249,222,239,250,252,251,
+ 252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,0,32,249,0,
+ 36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,0,109,252,
+ 0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,
+ 239,20,66,252,86,255,193,252,194,252,195,252,196,252,197,252,198,
+ 252,218,255,219,255,220,255,221,255,0,2,252,0,4,252,0,6,252,0,108,
+ 255,0,112,255,0,114,255,0,116,255,18,45,252,47,252,66,250,87,250,
+ 88,250,90,245,193,250,194,250,195,250,196,250,197,250,198,250,222,
+ 245,0,2,250,0,4,250,0,6,250,0,122,245,23,45,240,47,240,66,244,98,
+ 254,193,244,194,244,195,244,196,244,197,244,198,244,225,254,226,
+ 254,227,254,228,254,229,254,230,254,0,2,244,0,3,254,0,4,244,0,5,
+ 254,0,6,244,0,7,254,22,45,254,47,254,66,254,118,254,193,254,194,
+ 254,195,254,196,254,197,254,198,254,250,254,251,254,252,254,253,
+ 254,0,2,254,0,4,254,0,6,254,0,109,254,0,113,254,0,115,254,0,117,
+ 254,40,80,252,102,254,112,251,118,252,122,250,211,252,212,252,213,
+ 252,214,252,215,252,217,252,233,254,234,254,235,254,236,254,243,
+ 251,244,251,245,251,246,251,247,251,249,251,250,252,251,252,252,
+ 252,253,252,254,250,0,1,250,0,21,254,0,25,254,0,27,254,0,29,254,
+ 0,78,252,0,79,251,0,82,252,0,83,251,0,109,252,0,113,252,0,115,252,
+ 0,117,252,14,85,242,87,239,88,244,90,237,122,252,222,237,254,252,
+ 0,1,252,0,100,242,0,102,242,0,122,237,1,32,25,234,1,32,29,234,22,
+ 45,250,47,250,66,249,85,250,87,249,88,249,89,249,90,245,193,249,
+ 194,249,195,249,196,249,197,249,198,249,222,245,0,2,249,0,4,249,
+ 0,6,249,0,100,250,0,102,250,0,122,245,41,45,237,47,237,66,240,98,
+ 252,102,252,112,250,193,240,194,240,195,240,196,240,197,240,198,
+ 240,225,252,226,252,227,252,228,252,229,252,230,252,233,252,234,
+ 252,235,252,236,252,243,250,244,250,245,250,246,250,247,250,249,
+ 250,0,2,240,0,3,252,0,4,240,0,5,252,0,6,240,0,7,252,0,21,252,0,25,
+ 252,0,27,252,0,29,252,0,79,250,0,83,250,12,45,3,47,3,86,255,218,
+ 255,219,255,220,255,221,255,0,108,255,0,112,255,0,114,255,0,116,
+ 255,27,80,254,85,254,86,254,87,249,88,250,90,249,211,254,212,254,
+ 213,254,214,254,215,254,217,254,218,254,219,254,220,254,221,254,
+ 222,249,0,78,254,0,82,254,0,100,254,0,102,254,0,108,254,0,112,254,
+ 0,114,254,0,116,254,0,122,249,69,45,244,46,237,47,244,59,250,60,
+ 250,66,242,80,250,98,244,102,247,112,244,115,244,118,242,120,247,
+ 122,247,193,242,194,242,195,242,196,242,197,242,198,242,211,250,
+ 212,250,213,250,214,250,215,250,217,250,225,244,226,244,227,244,
+ 228,244,229,244,230,244,233,247,234,247,235,247,236,247,243,244,
+ 244,244,245,244,246,244,247,244,249,244,250,242,251,242,252,242,
+ 253,242,254,247,0,1,247,0,2,242,0,3,244,0,4,242,0,5,244,0,6,242,
+ 0,7,244,0,21,247,0,25,247,0,27,247,0,29,247,0,78,250,0,79,244,0,
+ 82,250,0,83,244,0,87,244,0,89,244,0,109,242,0,113,242,0,115,242,
+ 0,117,242,13,45,252,47,252,66,249,193,249,194,249,195,249,196,249,
+ 197,249,198,249,0,2,249,0,4,249,0,6,249,65,45,237,46,244,47,237,
+ 59,250,60,250,66,244,72,249,80,249,98,247,102,249,112,242,118,247,
+ 193,244,194,244,195,244,196,244,197,244,198,244,211,249,212,249,
+ 213,249,214,249,215,249,217,249,225,247,226,247,227,247,228,247,
+ 229,247,230,247,233,249,234,249,235,249,236,249,243,242,244,242,
+ 245,242,246,242,247,242,249,242,250,247,251,247,252,247,253,247,
+ 0,2,244,0,3,247,0,4,244,0,5,247,0,6,244,0,7,247,0,21,249,0,25,249,
+ 0,27,249,0,29,249,0,32,249,0,36,249,0,78,249,0,79,242,0,82,249,0,
+ 83,242,0,109,247,0,113,247,0,115,247,0,117,247,65,45,244,46,250,
+ 47,244,59,255,60,255,66,247,80,254,98,250,102,251,112,247,118,249,
+ 122,254,193,247,194,247,195,247,196,247,197,247,198,247,211,254,
+ 212,254,213,254,214,254,215,254,217,254,225,250,226,250,227,250,
+ 228,250,229,250,230,250,233,251,234,251,235,251,236,251,243,247,
+ 244,247,245,247,246,247,247,247,249,247,250,249,251,249,252,249,
+ 253,249,254,254,0,1,254,0,2,247,0,3,250,0,4,247,0,5,250,0,6,247,
+ 0,7,250,0,21,251,0,25,251,0,27,251,0,29,251,0,78,254,0,79,247,0,
+ 82,254,0,83,247,0,109,249,0,113,249,0,115,249,0,117,249,61,45,240,
+ 47,240,59,249,60,249,66,239,80,245,98,242,102,244,112,240,118,240,
+ 193,239,194,239,195,239,196,239,197,239,198,239,211,245,212,245,
+ 213,245,214,245,215,245,217,245,225,242,226,242,227,242,228,242,
+ 229,242,230,242,233,244,234,244,235,244,236,244,243,240,244,240,
+ 245,240,246,240,247,240,249,240,250,240,251,240,252,240,253,240,
+ 0,2,239,0,3,242,0,4,239,0,5,242,0,6,239,0,7,242,0,21,244,0,25,244,
+ 0,27,244,0,29,244,0,78,245,0,79,240,0,82,245,0,83,240,0,109,240,
+ 0,113,240,0,115,240,0,117,240,9,104,255,119,254,120,254,122,254,
+ 254,254,0,1,254,0,33,255,0,37,255,18,109,255,118,254,119,254,122,
+ 254,250,254,251,254,252,254,253,254,254,254,0,1,254,0,60,255,0,62,
+ 255,0,68,255,0,109,254,0,113,254,0,115,254,0,117,254,11,105,255,
+ 108,254,109,254,122,255,254,255,0,1,255,0,57,254,0,60,254,0,62,254,
+ 0,68,254,8,101,255,119,254,120,254,122,254,254,254,0,1,254,0,19,
+ 255,9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,254,
+ 23,45,255,47,255,102,255,112,254,233,255,234,255,235,255,236,255,
+ 243,254,244,254,245,254,246,254,247,254,249,254,0,21,255,0,25,255,
+ 0,27,255,0,29,255,0,79,254,0,83,254,1,32,25,5,1,32,29,5,13,102,2,
+ 104,255,233,2,234,2,235,2,236,2,0,21,2,0,25,2,0,27,2,0,29,2,0,33,
+ 255,0,37,255,4,122,254,254,254,0,1,254,10,112,254,243,254,244,254,
+ 245,254,246,254,247,254,249,254,0,79,254,0,83,254,5,120,254,122,
+ 254,254,254,0,1,254,13,118,254,122,252,250,254,251,254,252,254,253,
+ 254,254,252,0,1,252,0,109,254,0,113,254,0,115,254,0,117,254,14,118,
+ 255,119,250,122,254,250,255,251,255,252,255,253,255,254,254,0,1,
+ 254,0,109,255,0,113,255,0,115,255,0,117,255,7,119,254,120,254,121,
+ 252,122,254,254,254,0,1,254,4,122,254,254,254,0,1,254,34,45,247,
+ 46,254,47,247,100,254,101,254,104,254,112,254,114,254,116,254,117,
+ 3,119,2,122,2,232,254,243,254,244,254,245,254,246,254,247,254,249,
+ 254,254,2,0,1,2,0,9,254,0,15,254,0,19,254,0,33,254,0,37,254,0,79,
+ 254,0,83,254,0,93,254,0,97,254,0,99,254,0,101,3,1,2,25,254,2,120,
+ 254,22,45,244,47,244,98,254,112,252,225,254,226,254,227,254,228,
+ 254,229,254,230,254,243,252,244,252,245,252,246,252,247,252,249,
+ 252,0,3,254,0,5,254,0,7,254,0,79,252,0,83,252,12,45,250,47,250,112,
+ 254,243,254,244,254,245,254,246,254,247,254,249,254,0,79,254,0,83,
+ 254,10,102,255,233,255,234,255,235,255,236,255,0,21,255,0,25,255,
+ 0,27,255,0,29,255,31,45,244,47,244,98,252,102,255,112,253,225,252,
+ 226,252,227,252,228,252,229,252,230,252,233,255,234,255,235,255,
+ 236,255,243,253,244,253,245,253,246,253,247,253,249,253,0,3,252,
+ 0,5,252,0,7,252,0,21,255,0,25,255,0,27,255,0,29,255,0,79,253,0,83,
+ 253,10,102,2,233,2,234,2,235,2,236,2,0,21,2,0,25,2,0,27,2,0,29,2,
+ 49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,88,247,90,239,
+ 118,252,119,250,120,252,122,252,200,250,211,250,212,250,213,250,
+ 214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,239,
+ 250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,
+ 0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,
+ 0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,
+ 252,0,122,239,49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,
+ 88,247,90,239,118,252,119,250,120,252,122,252,200,250,211,250,212,
+ 250,213,250,214,250,215,250,217,250,218,249,219,249,220,249,221,
+ 249,222,239,250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,
+ 250,0,14,250,0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,
+ 242,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,
+ 0,116,249,0,117,252,0,122,239,49,68,250,72,249,80,250,82,250,85,
+ 242,86,249,87,244,88,247,90,239,118,252,119,250,120,252,122,252,
+ 200,250,211,250,212,250,213,250,214,250,215,250,217,250,218,249,
+ 219,249,220,249,221,249,222,239,250,252,251,252,252,252,253,252,
+ 254,252,0,1,252,0,8,250,0,14,250,0,32,249,0,36,249,0,78,250,0,82,
+ 250,0,100,242,0,102,242,0,108,249,0,109,252,0,112,249,0,113,252,
+ 0,114,249,0,115,252,0,116,249,0,117,252,0,122,239,49,68,250,72,249,
+ 80,250,82,250,85,242,86,249,87,244,88,247,90,239,118,252,119,250,
+ 120,252,122,252,200,250,211,250,212,250,213,250,214,250,215,250,
+ 217,250,218,249,219,249,220,249,221,249,222,239,250,252,251,252,
+ 252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,0,32,249,0,36,249,
+ 0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,0,109,252,0,112,
+ 249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,239,
+ 49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,88,247,90,239,
+ 118,252,119,250,120,252,122,252,200,250,211,250,212,250,213,250,
+ 214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,239,
+ 250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,
+ 0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,
+ 0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,
+ 252,0,122,239,49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,
+ 88,247,90,239,118,252,119,250,120,252,122,252,200,250,211,250,212,
+ 250,213,250,214,250,215,250,217,250,218,249,219,249,220,249,221,
+ 249,222,239,250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,
+ 250,0,14,250,0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,
+ 242,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,
+ 0,116,249,0,117,252,0,122,239,22,45,250,47,250,66,249,85,250,87,
+ 249,88,249,89,249,90,245,193,249,194,249,195,249,196,249,197,249,
+ 198,249,222,245,0,2,249,0,4,249,0,6,249,0,100,250,0,102,250,0,122,
+ 245,22,45,250,47,250,66,249,85,250,87,249,88,249,89,249,90,245,193,
+ 249,194,249,195,249,196,249,197,249,198,249,222,245,0,2,249,0,4,
+ 249,0,6,249,0,100,250,0,102,250,0,122,245,22,45,250,47,250,66,249,
+ 85,250,87,249,88,249,89,249,90,245,193,249,194,249,195,249,196,249,
+ 197,249,198,249,222,245,0,2,249,0,4,249,0,6,249,0,100,250,0,102,
+ 250,0,122,245,22,45,250,47,250,66,249,85,250,87,249,88,249,89,249,
+ 90,245,193,249,194,249,195,249,196,249,197,249,198,249,222,245,0,
+ 2,249,0,4,249,0,6,249,0,100,250,0,102,250,0,122,245,22,45,250,47,
+ 250,66,249,85,250,87,249,88,249,89,249,90,245,193,249,194,249,195,
+ 249,196,249,197,249,198,249,222,245,0,2,249,0,4,249,0,6,249,0,100,
+ 250,0,102,250,0,122,245,22,45,250,47,250,66,249,85,250,87,249,88,
+ 249,89,249,90,245,193,249,194,249,195,249,196,249,197,249,198,249,
+ 222,245,0,2,249,0,4,249,0,6,249,0,100,250,0,102,250,0,122,245,13,
+ 45,252,47,252,66,249,193,249,194,249,195,249,196,249,197,249,198,
+ 249,0,2,249,0,4,249,0,6,249,13,45,252,47,252,66,249,193,249,194,
+ 249,195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,13,45,
+ 252,47,252,66,249,193,249,194,249,195,249,196,249,197,249,198,249,
+ 0,2,249,0,4,249,0,6,249,13,45,252,47,252,66,249,193,249,194,249,
+ 195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,61,45,240,
+ 47,240,59,249,60,249,66,239,80,245,98,242,102,244,112,240,118,240,
+ 193,239,194,239,195,239,196,239,197,239,198,239,211,245,212,245,
+ 213,245,214,245,215,245,217,245,225,242,226,242,227,242,228,242,
+ 229,242,230,242,233,244,234,244,235,244,236,244,243,240,244,240,
+ 245,240,246,240,247,240,249,240,250,240,251,240,252,240,253,240,
+ 0,2,239,0,3,242,0,4,239,0,5,242,0,6,239,0,7,242,0,21,244,0,25,244,
+ 0,27,244,0,29,244,0,78,245,0,79,240,0,82,245,0,83,240,0,109,240,
+ 0,113,240,0,115,240,0,117,240,9,104,255,119,254,120,254,122,254,
+ 254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,254,120,254,122,
+ 254,254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,254,120,254,
+ 122,254,254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,254,120,
+ 254,122,254,254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,254,
+ 120,254,122,254,254,254,0,1,254,0,33,255,0,37,255,9,104,255,119,
+ 254,120,254,122,254,254,254,0,1,254,0,33,255,0,37,255,11,105,255,
+ 108,254,109,254,122,255,254,255,0,1,255,0,57,254,0,60,254,0,62,254,
+ 0,68,254,9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,
+ 254,9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,254,
+ 9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,254,9,45,
+ 2,47,3,119,254,120,254,121,254,122,254,254,254,0,1,254,14,118,255,
+ 119,250,122,254,250,255,251,255,252,255,253,255,254,254,0,1,254,
+ 0,109,255,0,113,255,0,115,255,0,117,255,7,119,254,120,254,121,252,
+ 122,254,254,254,0,1,254,7,119,254,120,254,121,252,122,254,254,254,
+ 0,1,254,7,119,254,120,254,121,252,122,254,254,254,0,1,254,7,119,
+ 254,120,254,121,252,122,254,254,254,0,1,254,7,119,254,120,254,121,
+ 252,122,254,254,254,0,1,254,7,119,254,120,254,121,252,122,254,254,
+ 254,0,1,254,31,45,244,47,244,98,252,102,255,112,253,225,252,226,
+ 252,227,252,228,252,229,252,230,252,233,255,234,255,235,255,236,
+ 255,243,253,244,253,245,253,246,253,247,253,249,253,0,3,252,0,5,
+ 252,0,7,252,0,21,255,0,25,255,0,27,255,0,29,255,0,79,253,0,83,253,
+ 31,45,244,47,244,98,252,102,255,112,253,225,252,226,252,227,252,
+ 228,252,229,252,230,252,233,255,234,255,235,255,236,255,243,253,
+ 244,253,245,253,246,253,247,253,249,253,0,3,252,0,5,252,0,7,252,
+ 0,21,255,0,25,255,0,27,255,0,29,255,0,79,253,0,83,253,49,68,250,
+ 72,249,80,250,82,250,85,242,86,249,87,244,88,247,90,239,118,252,
+ 119,250,120,252,122,252,200,250,211,250,212,250,213,250,214,250,
+ 215,250,217,250,218,249,219,249,220,249,221,249,222,239,250,252,
+ 251,252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,250,0,32,249,
+ 0,36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,249,0,109,252,
+ 0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,
+ 239,9,104,255,119,254,120,254,122,254,254,254,0,1,254,0,33,255,0,
+ 37,255,49,68,250,72,249,80,250,82,250,85,242,86,249,87,244,88,247,
+ 90,239,118,252,119,250,120,252,122,252,200,250,211,250,212,250,213,
+ 250,214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,
+ 239,250,252,251,252,252,252,253,252,254,252,0,1,252,0,8,250,0,14,
+ 250,0,32,249,0,36,249,0,78,250,0,82,250,0,100,242,0,102,242,0,108,
+ 249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,
+ 0,117,252,0,122,239,9,104,255,119,254,120,254,122,254,254,254,0,
+ 1,254,0,33,255,0,37,255,49,68,250,72,249,80,250,82,250,85,242,86,
+ 249,87,244,88,247,90,239,118,252,119,250,120,252,122,252,200,250,
+ 211,250,212,250,213,250,214,250,215,250,217,250,218,249,219,249,
+ 220,249,221,249,222,239,250,252,251,252,252,252,253,252,254,252,
+ 0,1,252,0,8,250,0,14,250,0,32,249,0,36,249,0,78,250,0,82,250,0,100,
+ 242,0,102,242,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,
+ 0,115,252,0,116,249,0,117,252,0,122,239,9,104,255,119,254,120,254,
+ 122,254,254,254,0,1,254,0,33,255,0,37,255,11,105,255,108,254,109,
+ 254,122,255,254,255,0,1,255,0,57,254,0,60,254,0,62,254,0,68,254,
+ 11,105,255,108,254,109,254,122,255,254,255,0,1,255,0,57,254,0,60,
+ 254,0,62,254,0,68,254,18,45,252,47,252,66,250,87,250,88,250,90,245,
+ 193,250,194,250,195,250,196,250,197,250,198,250,222,245,0,2,250,
+ 0,4,250,0,6,250,0,122,245,18,45,252,47,252,66,250,87,250,88,250,
+ 90,245,193,250,194,250,195,250,196,250,197,250,198,250,222,245,0,
+ 2,250,0,4,250,0,6,250,0,122,245,8,101,255,119,254,120,254,122,254,
+ 254,254,0,1,254,0,19,255,9,45,2,47,3,119,254,120,254,121,254,122,
+ 254,254,254,0,1,254,9,45,2,47,3,119,254,120,254,121,254,122,254,
+ 254,254,0,1,254,9,45,2,47,3,119,254,120,254,121,254,122,254,254,
+ 254,0,1,254,9,45,2,47,3,119,254,120,254,121,254,122,254,254,254,
+ 0,1,254,13,102,2,104,255,233,2,234,2,235,2,236,2,0,21,2,0,25,2,0,
+ 27,2,0,29,2,0,33,255,0,37,255,13,102,2,104,255,233,2,234,2,235,2,
+ 236,2,0,21,2,0,25,2,0,27,2,0,29,2,0,33,255,0,37,255,40,80,252,102,
+ 254,112,251,118,252,122,250,211,252,212,252,213,252,214,252,215,
+ 252,217,252,233,254,234,254,235,254,236,254,243,251,244,251,245,
+ 251,246,251,247,251,249,251,250,252,251,252,252,252,253,252,254,
+ 250,0,1,250,0,21,254,0,25,254,0,27,254,0,29,254,0,78,252,0,79,251,
+ 0,82,252,0,83,251,0,109,252,0,113,252,0,115,252,0,117,252,10,112,
+ 254,243,254,244,254,245,254,246,254,247,254,249,254,0,79,254,0,83,
+ 254,14,85,242,87,239,88,244,90,237,122,252,222,237,254,252,0,1,252,
+ 0,100,242,0,102,242,0,122,237,1,32,25,234,1,32,29,234,5,120,254,
+ 122,254,254,254,0,1,254,14,85,242,87,239,88,244,90,237,122,252,222,
+ 237,254,252,0,1,252,0,100,242,0,102,242,0,122,237,1,32,25,234,1,
+ 32,29,234,5,120,254,122,254,254,254,0,1,254,14,85,242,87,239,88,
+ 244,90,237,122,252,222,237,254,252,0,1,252,0,100,242,0,102,242,0,
+ 122,237,1,32,25,234,1,32,29,234,5,120,254,122,254,254,254,0,1,254,
+ 14,118,255,119,250,122,254,250,255,251,255,252,255,253,255,254,254,
+ 0,1,254,0,109,255,0,113,255,0,115,255,0,117,255,14,118,255,119,250,
+ 122,254,250,255,251,255,252,255,253,255,254,254,0,1,254,0,109,255,
+ 0,113,255,0,115,255,0,117,255,14,118,255,119,250,122,254,250,255,
+ 251,255,252,255,253,255,254,254,0,1,254,0,109,255,0,113,255,0,115,
+ 255,0,117,255,22,45,250,47,250,66,249,85,250,87,249,88,249,89,249,
+ 90,245,193,249,194,249,195,249,196,249,197,249,198,249,222,245,0,
+ 2,249,0,4,249,0,6,249,0,100,250,0,102,250,0,122,245,7,119,254,120,
+ 254,121,252,122,254,254,254,0,1,254,22,45,250,47,250,66,249,85,250,
+ 87,249,88,249,89,249,90,245,193,249,194,249,195,249,196,249,197,
+ 249,198,249,222,245,0,2,249,0,4,249,0,6,249,0,100,250,0,102,250,
+ 0,122,245,7,119,254,120,254,121,252,122,254,254,254,0,1,254,27,80,
+ 254,85,254,86,254,87,249,88,250,90,249,211,254,212,254,213,254,214,
+ 254,215,254,217,254,218,254,219,254,220,254,221,254,222,249,0,78,
+ 254,0,82,254,0,100,254,0,102,254,0,108,254,0,112,254,0,114,254,0,
+ 116,254,0,122,249,34,45,247,46,254,47,247,100,254,101,254,104,254,
+ 112,254,114,254,116,254,117,3,119,2,122,2,232,254,243,254,244,254,
+ 245,254,246,254,247,254,249,254,254,2,0,1,2,0,9,254,0,15,254,0,19,
+ 254,0,33,254,0,37,254,0,79,254,0,83,254,0,93,254,0,97,254,0,99,254,
+ 0,101,3,1,2,25,254,27,80,254,85,254,86,254,87,249,88,250,90,249,
+ 211,254,212,254,213,254,214,254,215,254,217,254,218,254,219,254,
+ 220,254,221,254,222,249,0,78,254,0,82,254,0,100,254,0,102,254,0,
+ 108,254,0,112,254,0,114,254,0,116,254,0,122,249,34,45,247,46,254,
+ 47,247,100,254,101,254,104,254,112,254,114,254,116,254,117,3,119,
+ 2,122,2,232,254,243,254,244,254,245,254,246,254,247,254,249,254,
+ 254,2,0,1,2,0,9,254,0,15,254,0,19,254,0,33,254,0,37,254,0,79,254,
+ 0,83,254,0,93,254,0,97,254,0,99,254,0,101,3,1,2,25,254,27,80,254,
+ 85,254,86,254,87,249,88,250,90,249,211,254,212,254,213,254,214,254,
+ 215,254,217,254,218,254,219,254,220,254,221,254,222,249,0,78,254,
+ 0,82,254,0,100,254,0,102,254,0,108,254,0,112,254,0,114,254,0,116,
+ 254,0,122,249,34,45,247,46,254,47,247,100,254,101,254,104,254,112,
+ 254,114,254,116,254,117,3,119,2,122,2,232,254,243,254,244,254,245,
+ 254,246,254,247,254,249,254,254,2,0,1,2,0,9,254,0,15,254,0,19,254,
+ 0,33,254,0,37,254,0,79,254,0,83,254,0,93,254,0,97,254,0,99,254,0,
+ 101,3,1,2,25,254,2,120,254,2,120,254,2,120,254,69,45,244,46,237,
+ 47,244,59,250,60,250,66,242,80,250,98,244,102,247,112,244,115,244,
+ 118,242,120,247,122,247,193,242,194,242,195,242,196,242,197,242,
+ 198,242,211,250,212,250,213,250,214,250,215,250,217,250,225,244,
+ 226,244,227,244,228,244,229,244,230,244,233,247,234,247,235,247,
+ 236,247,243,244,244,244,245,244,246,244,247,244,249,244,250,242,
+ 251,242,252,242,253,242,254,247,0,1,247,0,2,242,0,3,244,0,4,242,
+ 0,5,244,0,6,242,0,7,244,0,21,247,0,25,247,0,27,247,0,29,247,0,78,
+ 250,0,79,244,0,82,250,0,83,244,0,87,244,0,89,244,0,109,242,0,113,
+ 242,0,115,242,0,117,242,69,45,244,46,237,47,244,59,250,60,250,66,
+ 242,80,250,98,244,102,247,112,244,115,244,118,242,120,247,122,247,
+ 193,242,194,242,195,242,196,242,197,242,198,242,211,250,212,250,
+ 213,250,214,250,215,250,217,250,225,244,226,244,227,244,228,244,
+ 229,244,230,244,233,247,234,247,235,247,236,247,243,244,244,244,
+ 245,244,246,244,247,244,249,244,250,242,251,242,252,242,253,242,
+ 254,247,0,1,247,0,2,242,0,3,244,0,4,242,0,5,244,0,6,242,0,7,244,
+ 0,21,247,0,25,247,0,27,247,0,29,247,0,78,250,0,79,244,0,82,250,0,
+ 83,244,0,87,244,0,89,244,0,109,242,0,113,242,0,115,242,0,117,242,
+ 13,45,252,47,252,66,249,193,249,194,249,195,249,196,249,197,249,
+ 198,249,0,2,249,0,4,249,0,6,249,13,45,252,47,252,66,249,193,249,
+ 194,249,195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,
+ 13,45,252,47,252,66,249,193,249,194,249,195,249,196,249,197,249,
+ 198,249,0,2,249,0,4,249,0,6,249,13,45,252,47,252,66,249,193,249,
+ 194,249,195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,
+ 61,45,240,47,240,59,249,60,249,66,239,80,245,98,242,102,244,112,
+ 240,118,240,193,239,194,239,195,239,196,239,197,239,198,239,211,
+ 245,212,245,213,245,214,245,215,245,217,245,225,242,226,242,227,
+ 242,228,242,229,242,230,242,233,244,234,244,235,244,236,244,243,
+ 240,244,240,245,240,246,240,247,240,249,240,250,240,251,240,252,
+ 240,253,240,0,2,239,0,3,242,0,4,239,0,5,242,0,6,239,0,7,242,0,21,
+ 244,0,25,244,0,27,244,0,29,244,0,78,245,0,79,240,0,82,245,0,83,240,
+ 0,109,240,0,113,240,0,115,240,0,117,240,10,102,2,233,2,234,2,235,
+ 2,236,2,0,21,2,0,25,2,0,27,2,0,29,2,10,102,2,233,2,234,2,235,2,236,
+ 2,0,21,2,0,25,2,0,27,2,0,29,2,10,102,2,233,2,234,2,235,2,236,2,0,
+ 21,2,0,25,2,0,27,2,0,29,2,2,120,254,2,1,32,24,249,19,33,244,101,
+ 244,109,254,115,250,116,247,119,254,0,19,244,0,60,254,0,62,254,0,
+ 68,254,0,87,250,0,89,250,0,91,250,0,93,247,0,97,247,0,99,247,1,2,
+ 25,247,1,32,25,249,2,33,244
+};
+static afm_cuint16 afm_Helvetica_BoldOblique_highchars_index[] = { /* 220 */
+ 161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+ 178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+ 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+ 210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+ 226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+ 242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+ 258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+ 280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+ 313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+ 336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+ 354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+ 380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+ 8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+ 8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+ 64257,64258
+};
+static afm_cunicode afm_Helvetica_BoldOblique_ligatures[] = { /* 3 */
+ 102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Helvetica-Oblique */
+/* FullName: Helvetica Oblique */
+/* FamilyName: Helvetica */
+static afm_cuint8 afm_Helvetica_Oblique_widths[] = { /* 315 */
+ 46,46,59,93,93,148,111,32,56,56,65,97,46,56,46,46,93,93,93,93,93,
+ 93,93,93,93,93,46,46,97,97,97,93,169,111,111,120,120,111,102,130,
+ 120,46,83,111,93,139,120,130,111,130,120,111,102,120,111,157,111,
+ 111,102,46,46,46,78,93,56,93,93,83,93,93,46,93,93,37,37,83,37,139,
+ 93,93,93,93,56,83,46,93,83,120,83,83,83,56,43,56,97,56,93,93,93,
+ 93,43,93,56,123,62,93,97,123,56,67,97,56,56,56,93,90,46,56,56,61,
+ 93,139,139,139,102,111,111,111,111,111,111,167,120,111,111,111,111,
+ 46,46,46,46,120,120,130,130,130,130,130,97,130,120,120,120,120,111,
+ 111,102,93,93,93,93,93,93,148,83,93,93,93,93,46,46,46,46,93,93,93,
+ 93,93,93,93,97,102,93,93,93,93,83,93,83,111,93,111,93,111,93,120,
+ 83,120,83,120,107,120,93,111,93,111,93,111,93,111,93,130,93,130,
+ 93,46,46,46,37,46,46,111,83,93,37,93,37,93,50,93,37,120,93,120,93,
+ 120,93,130,93,130,93,167,157,120,56,120,56,120,56,111,83,111,83,
+ 111,83,102,46,102,53,120,93,120,93,120,93,120,93,111,102,83,102,
+ 83,102,83,93,111,83,56,56,56,56,56,56,56,56,93,167,37,37,37,56,56,
+ 56,93,93,58,167,167,56,56,28,93,167,79,102,100,97,76,92,92,92,79,
+ 42,83,83
+};
+static afm_sint16 afm_Helvetica_Oblique_kerning_index[] = { /* 315 */
+ 1,0,0,0,0,0,0,0,0,0,0,0,29,0,38,0,0,0,0,0,0,0,0,0,0,0,49,52,0,0,
+ 0,0,0,55,170,197,202,0,241,0,0,0,345,418,510,0,0,545,594,687,710,
+ 772,777,938,966,1115,0,1259,0,0,0,0,0,0,0,1407,1419,1468,0,1476,
+ 1494,1575,1587,0,0,1595,0,1638,1668,1700,1718,0,1730,1847,0,0,1854,
+ 1924,1994,2017,2087,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,2130,2245,2360,2475,2590,2705,0,2820,0,0,
+ 0,0,0,0,0,0,0,0,2825,2874,2923,2972,3021,0,3070,3119,3147,3175,3203,
+ 3231,0,0,3379,3391,3403,3415,3427,3439,0,3451,3459,3477,3495,3513,
+ 0,0,0,0,0,3531,3563,3581,3599,3617,3635,0,3653,0,0,0,0,3882,0,3952,
+ 4022,4137,4149,4264,4276,4391,4403,4408,4416,4421,4429,0,4468,0,
+ 0,4507,0,4525,0,4543,0,4561,0,4579,0,4591,0,0,0,0,0,0,4603,4695,
+ 4738,0,4773,0,4808,0,4843,0,0,4878,0,4910,0,4942,4974,5023,5041,
+ 5090,0,0,5108,5170,5287,5349,5466,5528,5645,5650,5657,5662,5669,
+ 5674,5681,0,5842,0,6003,0,6031,0,6059,0,6087,0,6115,0,6263,0,6306,
+ 0,6349,0,6392,6397,0,0,0,0,0,0,0,0,0,0,6404,6409,0,0,6447,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+static afm_cuint8 afm_Helvetica_Oblique_kerning_data[] = { /* 6450 */
+ 42,11,85,249,87,249,88,250,90,242,222,242,0,100,249,0,102,249,0,
+ 122,242,1,32,24,247,1,32,28,252,3,1,32,25,240,1,32,29,240,4,33,247,
+ 1,32,25,240,1,32,29,240,2,33,249,2,33,249,49,68,252,72,252,80,252,
+ 82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,250,120,250,
+ 122,250,200,252,211,252,212,252,213,252,214,252,215,252,217,252,
+ 218,249,219,249,220,249,221,249,222,240,250,252,251,252,252,252,
+ 253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,0,78,
+ 252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,249,0,
+ 113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,12,45,
+ 254,47,254,86,255,218,255,219,255,220,255,221,255,0,108,255,0,112,
+ 255,0,114,255,0,116,255,3,45,252,47,252,18,45,245,47,245,66,250,
+ 87,245,88,250,90,242,193,250,194,250,195,250,196,250,197,250,198,
+ 250,222,242,0,2,250,0,4,250,0,6,250,0,122,242,45,45,232,47,232,66,
+ 244,98,249,102,252,112,252,115,249,193,244,194,244,195,244,196,244,
+ 197,244,198,244,225,249,226,249,227,249,228,249,229,249,230,249,
+ 233,252,234,252,235,252,236,252,243,252,244,252,245,252,246,252,
+ 247,252,249,252,0,2,244,0,3,249,0,4,244,0,5,249,0,6,244,0,7,249,
+ 0,21,252,0,25,252,0,27,252,0,29,252,0,79,252,0,83,252,0,87,249,0,
+ 89,249,0,91,249,32,45,252,47,252,66,254,98,254,118,254,193,254,194,
+ 254,195,254,196,254,197,254,198,254,225,254,226,254,227,254,228,
+ 254,229,254,230,254,250,254,251,254,252,254,253,254,0,2,254,0,3,
+ 254,0,4,254,0,5,254,0,6,254,0,7,254,0,109,254,0,113,254,0,115,254,
+ 0,117,254,40,80,249,102,250,112,250,118,252,122,249,211,249,212,
+ 249,213,249,214,249,215,249,217,249,233,250,234,250,235,250,236,
+ 250,243,250,244,250,245,250,246,250,247,250,249,250,250,252,251,
+ 252,252,252,253,252,254,249,0,1,249,0,21,250,0,25,250,0,27,250,0,
+ 29,250,0,78,249,0,79,250,0,82,249,0,83,250,0,109,252,0,113,252,0,
+ 115,252,0,117,252,14,85,239,87,239,88,245,90,234,122,252,222,234,
+ 254,252,0,1,252,0,100,239,0,102,239,0,122,234,1,32,25,230,1,32,29,
+ 234,22,45,250,47,250,66,254,85,250,87,249,88,252,89,247,90,245,193,
+ 254,194,254,195,254,196,254,197,254,198,254,222,245,0,2,254,0,4,
+ 254,0,6,254,0,100,250,0,102,250,0,122,245,41,45,227,47,227,66,237,
+ 98,250,102,249,112,249,193,237,194,237,195,237,196,237,197,237,198,
+ 237,225,250,226,250,227,250,228,250,229,250,230,250,233,249,234,
+ 249,235,249,236,249,243,249,244,249,245,249,246,249,247,249,249,
+ 249,0,2,237,0,3,250,0,4,237,0,5,250,0,6,237,0,7,250,0,21,249,0,25,
+ 249,0,27,249,0,29,249,0,79,249,0,83,249,10,86,255,218,255,219,255,
+ 220,255,221,255,0,108,255,0,112,255,0,114,255,0,116,255,27,80,254,
+ 85,252,86,250,87,249,88,252,90,249,211,254,212,254,213,254,214,254,
+ 215,254,217,254,218,250,219,250,220,250,221,250,222,249,0,78,254,
+ 0,82,254,0,100,252,0,102,252,0,108,250,0,112,250,0,114,250,0,116,
+ 250,0,122,249,3,45,254,47,254,70,45,237,46,234,47,237,59,254,60,
+ 254,66,237,80,250,98,237,102,237,112,237,115,237,118,237,120,237,
+ 122,237,193,237,194,237,195,237,196,237,197,237,198,237,211,250,
+ 212,250,213,250,214,250,215,250,217,250,225,237,226,237,227,237,
+ 228,247,229,237,230,237,233,247,234,237,235,237,236,237,243,237,
+ 244,237,245,237,246,247,247,237,249,237,250,237,251,237,252,237,
+ 253,237,254,237,0,1,247,0,2,237,0,3,247,0,4,237,0,5,247,0,6,237,
+ 0,7,237,0,21,247,0,25,237,0,27,237,0,29,237,0,78,250,0,79,247,0,
+ 82,250,0,83,237,0,87,237,0,89,237,0,91,237,0,109,247,0,113,237,0,
+ 115,237,0,117,237,13,45,250,47,250,66,250,193,250,194,250,195,250,
+ 196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,65,45,236,46,244,
+ 47,236,59,250,60,250,66,244,72,250,80,250,98,245,102,244,112,244,
+ 118,245,193,244,194,244,195,244,196,244,197,244,198,244,211,250,
+ 212,250,213,250,214,250,215,250,217,250,225,245,226,245,227,245,
+ 228,245,229,245,230,245,233,244,234,244,235,244,236,244,243,244,
+ 244,244,245,244,246,244,247,244,249,244,250,245,251,245,252,245,
+ 253,245,0,2,244,0,3,245,0,4,244,0,5,245,0,6,244,0,7,245,0,21,244,
+ 0,25,244,0,27,244,0,29,244,0,32,250,0,36,250,0,78,250,0,79,244,0,
+ 82,250,0,83,244,0,109,245,0,113,245,0,115,245,0,117,245,63,45,244,
+ 46,250,47,244,66,249,80,254,98,250,102,252,112,252,118,252,122,254,
+ 193,249,194,249,195,249,196,249,197,249,198,249,211,254,212,254,
+ 213,254,214,254,215,254,217,254,225,250,226,250,227,250,228,250,
+ 229,250,230,250,233,252,234,252,235,252,236,252,243,252,244,252,
+ 245,252,246,252,247,252,249,252,250,252,251,252,252,252,253,252,
+ 254,254,0,1,254,0,2,249,0,3,250,0,4,249,0,5,250,0,6,249,0,7,250,
+ 0,21,252,0,25,252,0,27,252,0,29,252,0,78,254,0,79,252,0,82,254,0,
+ 83,252,0,109,252,0,113,252,0,115,252,0,117,252,65,45,234,46,234,
+ 47,234,59,247,60,247,66,239,80,243,98,234,102,234,106,254,112,234,
+ 118,239,193,239,194,239,195,239,196,239,197,239,198,239,211,243,
+ 212,243,213,243,214,243,215,243,217,243,225,234,226,234,227,234,
+ 228,234,229,234,230,234,233,234,234,234,235,234,236,234,238,254,
+ 243,234,244,234,245,234,246,234,247,234,249,234,250,239,251,239,
+ 252,239,253,239,0,2,239,0,3,245,0,4,239,0,5,245,0,6,239,0,7,234,
+ 0,21,245,0,25,234,0,27,234,0,29,234,0,49,254,0,78,243,0,79,234,0,
+ 82,243,0,83,234,0,109,239,0,113,239,0,115,239,0,117,239,6,119,254,
+ 120,254,122,252,254,252,0,1,252,21,45,250,47,250,99,255,109,254,
+ 118,254,119,254,122,254,250,254,251,254,252,254,253,254,254,254,
+ 0,1,254,0,60,254,0,62,254,0,68,254,0,109,254,0,113,254,0,115,254,
+ 0,117,254,4,45,254,108,254,0,57,254,9,45,254,47,254,119,252,120,
+ 254,121,252,122,254,254,254,0,1,254,34,45,252,47,252,98,252,102,
+ 252,112,252,225,252,226,252,227,252,228,252,229,252,230,252,233,
+ 252,234,252,235,252,236,252,243,252,244,252,245,252,246,252,247,
+ 252,249,252,0,3,252,0,5,252,0,7,252,0,21,252,0,25,252,0,27,252,0,
+ 29,252,0,51,252,0,79,252,0,83,252,1,32,25,8,1,32,29,10,5,115,255,
+ 0,87,255,0,89,255,0,91,255,4,122,252,254,252,0,1,252,19,102,254,
+ 112,254,233,254,234,254,235,254,236,254,243,254,244,254,245,254,
+ 246,254,247,254,249,254,0,21,254,0,25,254,0,27,254,0,29,254,0,79,
+ 254,0,83,254,13,118,255,122,254,250,255,251,255,252,255,253,255,
+ 254,254,0,1,254,0,109,255,0,113,255,0,115,255,0,117,255,14,118,255,
+ 119,254,122,254,250,255,251,255,252,255,253,255,254,254,0,1,254,
+ 0,109,255,0,113,255,0,115,255,0,117,255,9,45,250,47,250,119,254,
+ 120,254,121,252,122,252,254,252,0,1,252,6,45,251,47,251,122,252,
+ 254,252,0,1,252,50,45,249,47,249,59,5,60,5,98,255,106,3,108,3,109,
+ 3,110,4,111,4,113,5,117,7,118,3,119,5,122,5,225,255,226,255,227,
+ 255,228,255,229,255,230,255,237,3,238,3,239,3,240,3,242,4,250,3,
+ 251,3,252,3,253,3,254,5,0,1,5,0,3,255,0,5,255,0,7,255,0,45,3,0,49,
+ 3,0,57,3,0,60,3,0,62,3,0,68,3,0,70,4,0,72,4,0,74,4,0,101,7,0,109,
+ 3,0,113,3,0,115,3,0,117,3,4,45,254,47,254,120,252,31,45,244,47,244,
+ 98,253,102,253,112,253,225,253,226,253,227,253,228,253,229,253,230,
+ 253,233,253,234,253,235,253,236,253,243,253,244,253,245,253,246,
+ 253,247,253,249,253,0,3,253,0,5,253,0,7,253,0,21,253,0,25,253,0,
+ 27,253,0,29,253,0,79,253,0,83,253,31,45,247,47,247,98,254,102,255,
+ 112,255,225,254,226,254,227,254,228,254,229,254,230,254,233,255,
+ 234,255,235,255,236,255,243,255,244,255,245,255,246,255,247,255,
+ 249,255,0,3,254,0,5,254,0,7,254,0,21,255,0,25,255,0,27,255,0,29,
+ 255,0,79,255,0,83,255,10,102,252,233,252,234,252,235,252,236,252,
+ 0,21,252,0,25,252,0,27,252,0,29,252,31,45,240,47,240,98,254,102,
+ 254,112,254,225,254,226,254,227,254,228,254,229,254,230,254,233,
+ 254,234,254,235,254,236,254,243,254,244,254,245,254,246,254,247,
+ 254,249,254,0,3,254,0,5,254,0,7,254,0,21,254,0,25,254,0,27,254,0,
+ 29,254,0,79,254,0,83,254,19,102,254,112,254,233,254,234,254,235,
+ 254,236,254,243,254,244,254,245,254,246,254,247,254,249,254,0,21,
+ 254,0,25,254,0,27,254,0,29,254,0,79,254,0,83,254,49,68,252,72,252,
+ 80,252,82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,250,
+ 120,250,122,250,200,252,211,252,212,252,213,252,214,252,215,252,
+ 217,252,218,249,219,249,220,249,221,249,222,240,250,252,251,252,
+ 252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,
+ 0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,
+ 249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,
+ 49,68,252,72,252,80,252,82,252,85,237,86,249,87,245,88,249,90,240,
+ 118,252,119,250,120,250,122,250,200,252,211,252,212,252,213,252,
+ 214,252,215,252,217,252,218,249,219,249,220,249,221,249,222,240,
+ 250,252,251,252,252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,
+ 0,32,252,0,36,252,0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,
+ 0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,
+ 252,0,122,240,49,68,252,72,252,80,252,82,252,85,237,86,249,87,245,
+ 88,249,90,240,118,252,119,250,120,250,122,250,200,252,211,252,212,
+ 252,213,252,214,252,215,252,217,252,218,249,219,249,220,249,221,
+ 249,222,240,250,252,251,252,252,252,253,252,254,250,0,1,250,0,8,
+ 252,0,14,252,0,32,252,0,36,252,0,78,252,0,82,252,0,100,237,0,102,
+ 237,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,
+ 0,116,249,0,117,252,0,122,240,49,68,252,72,252,80,252,82,252,85,
+ 237,86,249,87,245,88,249,90,240,118,252,119,250,120,250,122,250,
+ 200,252,211,252,212,252,213,252,214,252,215,252,217,252,218,249,
+ 219,249,220,249,221,249,222,240,250,252,251,252,252,252,253,252,
+ 254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,0,78,252,0,82,
+ 252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,249,0,113,252,
+ 0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,49,68,252,72,252,
+ 80,252,82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,250,
+ 120,250,122,250,200,252,211,252,212,252,213,252,214,252,215,252,
+ 217,252,218,249,219,249,220,249,221,249,222,240,250,252,251,252,
+ 252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,
+ 0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,
+ 249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,
+ 49,68,252,72,252,80,252,82,252,85,237,86,249,87,245,88,249,90,240,
+ 118,252,119,250,120,250,122,250,200,252,211,252,212,252,213,252,
+ 214,252,215,252,217,252,218,249,219,249,220,249,221,249,222,240,
+ 250,252,251,252,252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,
+ 0,32,252,0,36,252,0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,
+ 0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,
+ 252,0,122,240,3,45,252,47,252,22,45,250,47,250,66,254,85,250,87,
+ 249,88,252,89,247,90,245,193,254,194,254,195,254,196,254,197,254,
+ 198,254,222,245,0,2,254,0,4,254,0,6,254,0,100,250,0,102,250,0,122,
+ 245,22,45,250,47,250,66,254,85,250,87,249,88,252,89,247,90,245,193,
+ 254,194,254,195,254,196,254,197,254,198,254,222,245,0,2,254,0,4,
+ 254,0,6,254,0,100,250,0,102,250,0,122,245,22,45,250,47,250,66,254,
+ 85,250,87,249,88,252,89,247,90,245,193,254,194,254,195,254,196,254,
+ 197,254,198,254,222,245,0,2,254,0,4,254,0,6,254,0,100,250,0,102,
+ 250,0,122,245,22,45,250,47,250,66,254,85,250,87,249,88,252,89,247,
+ 90,245,193,254,194,254,195,254,196,254,197,254,198,254,222,245,0,
+ 2,254,0,4,254,0,6,254,0,100,250,0,102,250,0,122,245,22,45,250,47,
+ 250,66,254,85,250,87,249,88,252,89,247,90,245,193,254,194,254,195,
+ 254,196,254,197,254,198,254,222,245,0,2,254,0,4,254,0,6,254,0,100,
+ 250,0,102,250,0,122,245,22,45,250,47,250,66,254,85,250,87,249,88,
+ 252,89,247,90,245,193,254,194,254,195,254,196,254,197,254,198,254,
+ 222,245,0,2,254,0,4,254,0,6,254,0,100,250,0,102,250,0,122,245,13,
+ 45,250,47,250,66,250,193,250,194,250,195,250,196,250,197,250,198,
+ 250,0,2,250,0,4,250,0,6,250,13,45,250,47,250,66,250,193,250,194,
+ 250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,13,45,
+ 250,47,250,66,250,193,250,194,250,195,250,196,250,197,250,198,250,
+ 0,2,250,0,4,250,0,6,250,13,45,250,47,250,66,250,193,250,194,250,
+ 195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,65,45,234,
+ 46,234,47,234,59,247,60,247,66,239,80,243,98,234,102,234,106,254,
+ 112,234,118,239,193,239,194,239,195,239,196,239,197,239,198,239,
+ 211,243,212,243,213,243,214,243,215,243,217,243,225,234,226,234,
+ 227,234,228,245,229,234,230,234,233,234,234,234,235,234,236,234,
+ 238,254,243,234,244,234,245,234,246,234,247,234,249,234,250,239,
+ 251,239,252,239,253,239,0,2,239,0,3,245,0,4,239,0,5,245,0,6,239,
+ 0,7,234,0,21,245,0,25,234,0,27,234,0,29,234,0,49,254,0,78,243,0,
+ 79,245,0,82,243,0,83,234,0,109,239,0,113,239,0,115,239,0,117,239,
+ 6,119,254,120,254,122,252,254,252,0,1,252,6,119,254,120,254,122,
+ 252,254,252,0,1,252,6,119,254,120,254,122,252,254,252,0,1,252,6,
+ 119,254,120,254,122,252,254,252,0,1,252,6,119,254,120,254,122,252,
+ 254,252,0,1,252,6,119,254,120,254,122,252,254,252,0,1,252,4,45,254,
+ 108,254,0,57,254,9,45,254,47,254,119,252,120,254,121,252,122,254,
+ 254,254,0,1,254,9,45,254,47,254,119,252,120,254,121,252,122,254,
+ 254,254,0,1,254,9,45,254,47,254,119,252,120,254,121,252,122,254,
+ 254,254,0,1,254,9,45,254,47,254,119,252,120,254,121,252,122,254,
+ 254,254,0,1,254,14,118,255,119,254,122,254,250,255,251,255,252,255,
+ 253,255,254,254,0,1,254,0,109,255,0,113,255,0,115,255,0,117,255,
+ 9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+ 9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+ 9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+ 9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+ 9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,0,1,252,
+ 95,45,241,47,241,98,248,99,248,100,248,101,248,102,248,103,248,104,
+ 248,105,248,106,248,107,248,108,248,109,248,110,248,111,248,112,
+ 248,113,248,114,248,115,248,116,248,117,248,118,248,119,245,120,
+ 245,121,243,122,245,123,248,225,248,226,248,227,248,228,248,229,
+ 248,230,248,232,248,233,248,234,248,235,248,236,248,237,248,238,
+ 248,239,248,240,248,242,248,243,248,244,248,245,248,246,248,247,
+ 248,249,248,250,248,251,248,252,248,253,248,254,245,0,1,245,0,3,
+ 248,0,5,248,0,7,248,0,9,248,0,15,248,0,19,248,0,21,248,0,25,248,
+ 0,27,248,0,29,248,0,33,248,0,37,248,0,45,248,0,49,248,0,57,248,0,
+ 60,248,0,62,248,0,68,248,0,70,248,0,72,248,0,74,248,0,79,248,0,83,
+ 248,0,87,248,0,89,248,0,91,248,0,93,248,0,97,248,0,99,248,0,101,
+ 248,0,109,248,0,113,248,0,115,248,0,117,248,0,124,248,0,126,248,
+ 0,128,248,1,2,25,248,31,45,240,47,240,98,254,102,254,112,254,225,
+ 254,226,254,227,254,228,254,229,254,230,254,233,254,234,254,235,
+ 254,236,254,243,254,244,254,245,254,246,254,247,254,249,254,0,3,
+ 254,0,5,254,0,7,254,0,21,254,0,25,254,0,27,254,0,29,254,0,79,254,
+ 0,83,254,31,45,240,47,240,98,254,102,254,112,254,225,254,226,254,
+ 227,254,228,254,229,254,230,254,233,254,234,254,235,254,236,254,
+ 243,254,244,254,245,254,246,254,247,254,249,254,0,3,254,0,5,254,
+ 0,7,254,0,21,254,0,25,254,0,27,254,0,29,254,0,79,254,0,83,254,49,
+ 68,252,72,252,80,252,82,252,85,237,86,249,87,245,88,249,90,240,118,
+ 252,119,250,120,250,122,250,200,252,211,252,212,252,213,252,214,
+ 252,215,252,217,252,218,249,219,249,220,249,221,249,222,240,250,
+ 252,251,252,252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,
+ 252,0,36,252,0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,
+ 252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,
+ 0,122,240,6,119,254,120,254,122,252,254,252,0,1,252,49,68,252,72,
+ 252,80,252,82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,
+ 250,120,250,122,250,200,252,211,252,212,252,213,252,214,252,215,
+ 252,217,252,218,249,219,249,220,249,221,249,222,240,250,252,251,
+ 252,252,252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,
+ 36,252,0,78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,
+ 0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,
+ 240,6,119,254,120,254,122,252,254,252,0,1,252,49,68,252,72,252,80,
+ 252,82,252,85,237,86,249,87,245,88,249,90,240,118,252,119,250,120,
+ 250,122,250,200,252,211,252,212,252,213,252,214,252,215,252,217,
+ 252,218,249,219,249,220,249,221,249,222,240,250,252,251,252,252,
+ 252,253,252,254,250,0,1,250,0,8,252,0,14,252,0,32,252,0,36,252,0,
+ 78,252,0,82,252,0,100,237,0,102,237,0,108,249,0,109,252,0,112,249,
+ 0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,240,6,119,
+ 254,120,254,122,252,254,252,0,1,252,3,45,252,47,252,4,45,254,108,
+ 254,0,57,254,3,45,252,47,252,4,45,254,108,254,0,57,254,18,45,245,
+ 47,245,66,250,87,245,88,250,90,242,193,250,194,250,195,250,196,250,
+ 197,250,198,250,222,242,0,2,250,0,4,250,0,6,250,0,122,242,18,45,
+ 245,47,245,66,250,87,245,88,250,90,242,193,250,194,250,195,250,196,
+ 250,197,250,198,250,222,242,0,2,250,0,4,250,0,6,250,0,122,242,9,
+ 45,254,47,254,119,252,120,254,121,252,122,254,254,254,0,1,254,9,
+ 45,254,47,254,119,252,120,254,121,252,122,254,254,254,0,1,254,9,
+ 45,254,47,254,119,252,120,254,121,252,122,254,254,254,0,1,254,9,
+ 45,254,47,254,119,252,120,254,121,252,122,254,254,254,0,1,254,5,
+ 115,255,0,87,255,0,89,255,0,91,255,5,115,255,0,87,255,0,89,255,0,
+ 91,255,40,80,249,102,250,112,250,118,252,122,249,211,249,212,249,
+ 213,249,214,249,215,249,217,249,233,250,234,250,235,250,236,250,
+ 243,250,244,250,245,250,246,250,247,250,249,250,250,252,251,252,
+ 252,252,253,252,254,249,0,1,249,0,21,250,0,25,250,0,27,250,0,29,
+ 250,0,78,249,0,79,250,0,82,249,0,83,250,0,109,252,0,113,252,0,115,
+ 252,0,117,252,19,102,254,112,254,233,254,234,254,235,254,236,254,
+ 243,254,244,254,245,254,246,254,247,254,249,254,0,21,254,0,25,254,
+ 0,27,254,0,29,254,0,79,254,0,83,254,14,85,239,87,239,88,245,90,234,
+ 122,252,222,234,254,252,0,1,252,0,100,239,0,102,239,0,122,234,1,
+ 32,25,230,1,32,29,234,14,85,239,87,239,88,245,90,234,122,252,222,
+ 234,254,252,0,1,252,0,100,239,0,102,239,0,122,234,1,32,25,230,1,
+ 32,29,234,14,85,239,87,239,88,245,90,234,122,252,222,234,254,252,
+ 0,1,252,0,100,239,0,102,239,0,122,234,1,32,25,230,1,32,29,234,14,
+ 85,239,87,239,88,245,90,234,122,252,222,234,254,252,0,1,252,0,100,
+ 239,0,102,239,0,122,234,1,32,25,230,1,32,29,234,14,118,255,119,254,
+ 122,254,250,255,251,255,252,255,253,255,254,254,0,1,254,0,109,255,
+ 0,113,255,0,115,255,0,117,255,14,118,255,119,254,122,254,250,255,
+ 251,255,252,255,253,255,254,254,0,1,254,0,109,255,0,113,255,0,115,
+ 255,0,117,255,14,118,255,119,254,122,254,250,255,251,255,252,255,
+ 253,255,254,254,0,1,254,0,109,255,0,113,255,0,115,255,0,117,255,
+ 22,45,250,47,250,66,254,85,250,87,249,88,252,89,247,90,245,193,254,
+ 194,254,195,254,196,254,197,254,198,254,222,245,0,2,254,0,4,254,
+ 0,6,254,0,100,250,0,102,250,0,122,245,9,45,250,47,250,119,254,120,
+ 254,121,252,122,252,254,252,0,1,252,22,45,250,47,250,66,254,85,250,
+ 87,249,88,252,89,247,90,245,193,254,194,254,195,254,196,254,197,
+ 254,198,254,222,245,0,2,254,0,4,254,0,6,254,0,100,250,0,102,250,
+ 0,122,245,9,45,250,47,250,119,254,120,254,121,252,122,252,254,252,
+ 0,1,252,27,80,254,85,252,86,250,87,249,88,252,90,249,211,254,212,
+ 254,213,254,214,254,215,254,217,254,218,250,219,250,220,250,221,
+ 250,222,249,0,78,254,0,82,254,0,100,252,0,102,252,0,108,250,0,112,
+ 250,0,114,250,0,116,250,0,122,249,50,45,249,47,249,59,5,60,5,98,
+ 255,106,3,108,3,109,3,110,4,111,4,113,5,117,7,118,3,119,5,122,5,
+ 225,255,226,255,227,255,228,255,229,255,230,255,237,3,238,3,239,
+ 3,240,3,242,4,250,3,251,3,252,3,253,3,254,5,0,1,5,0,3,255,0,5,255,
+ 0,7,255,0,45,3,0,49,3,0,57,3,0,60,3,0,62,3,0,68,3,0,70,4,0,72,4,
+ 0,74,4,0,101,7,0,109,3,0,113,3,0,115,3,0,117,3,27,80,254,85,252,
+ 86,250,87,249,88,252,90,249,211,254,212,254,213,254,214,254,215,
+ 254,217,254,218,250,219,250,220,250,221,250,222,249,0,78,254,0,82,
+ 254,0,100,252,0,102,252,0,108,250,0,112,250,0,114,250,0,116,250,
+ 0,122,249,50,45,249,47,249,59,5,60,5,98,255,106,3,108,3,109,3,110,
+ 4,111,4,113,5,117,7,118,3,119,5,122,5,225,255,226,255,227,255,228,
+ 255,229,255,230,255,237,3,238,3,239,3,240,3,242,4,250,3,251,3,252,
+ 3,253,3,254,5,0,1,5,0,3,255,0,5,255,0,7,255,0,45,3,0,49,3,0,57,3,
+ 0,60,3,0,62,3,0,68,3,0,70,4,0,72,4,0,74,4,0,101,7,0,109,3,0,113,
+ 3,0,115,3,0,117,3,27,80,254,85,252,86,250,87,249,88,252,90,249,211,
+ 254,212,254,213,254,214,254,215,254,217,254,218,250,219,250,220,
+ 250,221,250,222,249,0,78,254,0,82,254,0,100,252,0,102,252,0,108,
+ 250,0,112,250,0,114,250,0,116,250,0,122,249,50,45,249,47,249,59,
+ 5,60,5,98,255,106,3,108,3,109,3,110,4,111,4,113,5,117,7,118,3,119,
+ 5,122,5,225,255,226,255,227,255,228,255,229,255,230,255,237,3,238,
+ 3,239,3,240,3,242,4,250,3,251,3,252,3,253,3,254,5,0,1,5,0,3,255,
+ 0,5,255,0,7,255,0,45,3,0,49,3,0,57,3,0,60,3,0,62,3,0,68,3,0,70,4,
+ 0,72,4,0,74,4,0,101,7,0,109,3,0,113,3,0,115,3,0,117,3,3,45,254,47,
+ 254,4,45,254,47,254,120,252,3,45,254,47,254,4,45,254,47,254,120,
+ 252,3,45,254,47,254,4,45,254,47,254,120,252,70,45,237,46,234,47,
+ 237,59,254,60,254,66,237,80,250,98,237,102,237,112,237,115,237,118,
+ 237,120,237,122,237,193,237,194,237,195,237,196,237,197,237,198,
+ 237,211,250,212,250,213,250,214,250,215,250,217,250,225,237,226,
+ 237,227,237,228,247,229,237,230,237,233,247,234,237,235,237,236,
+ 237,243,237,244,237,245,237,246,247,247,237,249,237,250,237,251,
+ 237,252,237,253,237,254,237,0,1,247,0,2,237,0,3,247,0,4,237,0,5,
+ 247,0,6,237,0,7,237,0,21,247,0,25,237,0,27,237,0,29,237,0,78,250,
+ 0,79,247,0,82,250,0,83,237,0,87,237,0,89,237,0,91,237,0,109,247,
+ 0,113,237,0,115,237,0,117,237,70,45,237,46,234,47,237,59,254,60,
+ 254,66,237,80,250,98,237,102,237,112,237,115,237,118,237,120,237,
+ 122,237,193,237,194,237,195,237,196,237,197,237,198,237,211,250,
+ 212,250,213,250,214,250,215,250,217,250,225,237,226,237,227,237,
+ 228,247,229,237,230,237,233,247,234,237,235,237,236,237,243,237,
+ 244,237,245,237,246,247,247,237,249,237,250,237,251,237,252,237,
+ 253,237,254,237,0,1,247,0,2,237,0,3,247,0,4,237,0,5,247,0,6,237,
+ 0,7,237,0,21,247,0,25,237,0,27,237,0,29,237,0,78,250,0,79,247,0,
+ 82,250,0,83,237,0,87,237,0,89,237,0,91,237,0,109,247,0,113,237,0,
+ 115,237,0,117,237,13,45,250,47,250,66,250,193,250,194,250,195,250,
+ 196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,13,45,250,47,250,
+ 66,250,193,250,194,250,195,250,196,250,197,250,198,250,0,2,250,0,
+ 4,250,0,6,250,13,45,250,47,250,66,250,193,250,194,250,195,250,196,
+ 250,197,250,198,250,0,2,250,0,4,250,0,6,250,13,45,250,47,250,66,
+ 250,193,250,194,250,195,250,196,250,197,250,198,250,0,2,250,0,4,
+ 250,0,6,250,65,45,234,46,234,47,234,59,247,60,247,66,239,80,243,
+ 98,234,102,234,106,254,112,234,118,239,193,239,194,239,195,239,196,
+ 239,197,239,198,239,211,243,212,243,213,243,214,243,215,243,217,
+ 243,225,234,226,234,227,234,228,245,229,234,230,234,233,234,234,
+ 234,235,234,236,234,238,254,243,234,244,234,245,234,246,234,247,
+ 234,249,234,250,239,251,239,252,239,253,239,0,2,239,0,3,245,0,4,
+ 239,0,5,245,0,6,239,0,7,234,0,21,245,0,25,234,0,27,234,0,29,234,
+ 0,49,254,0,78,243,0,79,234,0,82,243,0,83,234,0,109,239,0,113,239,
+ 0,115,239,0,117,239,19,102,254,112,254,233,254,234,254,235,254,236,
+ 254,243,254,244,254,245,254,246,254,247,254,249,254,0,21,254,0,25,
+ 254,0,27,254,0,29,254,0,79,254,0,83,254,19,102,254,112,254,233,254,
+ 234,254,235,254,236,254,243,254,244,254,245,254,246,254,247,254,
+ 249,254,0,21,254,0,25,254,0,27,254,0,29,254,0,79,254,0,83,254,19,
+ 102,254,112,254,233,254,234,254,235,254,236,254,243,254,244,254,
+ 245,254,246,254,247,254,249,254,0,21,254,0,25,254,0,27,254,0,29,
+ 254,0,79,254,0,83,254,3,45,254,47,254,4,45,254,47,254,120,252,2,
+ 1,32,24,247,14,33,245,101,249,115,249,116,249,0,19,249,0,87,249,
+ 0,89,249,0,91,249,0,93,249,0,97,249,0,99,249,1,2,25,249,1,32,25,
+ 247,2,33,250
+};
+static afm_cuint16 afm_Helvetica_Oblique_highchars_index[] = { /* 220 */
+ 161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+ 178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+ 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+ 210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+ 226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+ 242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+ 258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+ 280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+ 313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+ 336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+ 354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+ 380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+ 8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+ 8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+ 64257,64258
+};
+static afm_cunicode afm_Helvetica_Oblique_ligatures[] = { /* 3 */
+ 102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: ZapfDingbats */
+/* FullName: ITC Zapf Dingbats */
+/* FamilyName: ZapfDingbats */
+static afm_cuint8 afm_ZapfDingbats_widths[] = { /* 202 */
+ 46,162,160,162,163,120,132,132,132,115,160,157,92,143,152,156,152,
+ 158,162,126,141,127,127,95,113,127,127,127,126,82,92,90,96,115,131,
+ 131,131,132,132,132,136,137,132,140,137,139,136,139,154,124,121,
+ 125,132,132,116,129,128,132,127,118,118,114,117,138,136,132,132,
+ 118,115,116,115,131,131,119,132,131,132,146,127,127,127,127,127,
+ 149,149,131,131,73,23,46,69,65,65,111,111,65,65,53,53,46,46,85,85,
+ 68,68,39,39,56,56,122,91,91,152,111,127,127,129,99,116,104,131,131,
+ 131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,
+ 131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,131,
+ 131,131,131,131,131,131,149,140,169,76,125,154,125,153,155,155,155,
+ 139,146,138,154,154,153,155,155,77,147,139,139,145,145,116,116,146,
+ 146,127,158,129,144,129,148,161,148,139,146,155,162,153
+};
+static afm_cuint16 afm_ZapfDingbats_highchars_index[] = { /* 107 */
+ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,161,162,
+ 163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,
+ 179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,
+ 195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,
+ 211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,
+ 227,228,229,230,231,232,233,234,235,236,237,238,239,241,242,243,
+ 244,245,246,247,248,249,250,251,252,253,254
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Symbol */
+/* FullName: Symbol */
+/* FamilyName: Symbol */
+static afm_cuint8 afm_Symbol_widths[] = { /* 251 */
+ 42,56,0,83,0,139,130,0,56,56,0,92,42,0,42,46,83,83,83,83,83,83,83,
+ 83,83,83,46,46,92,92,92,74,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,56,0,56,0,83,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,80,33,80,0,119,67,92,96,92,92,83,120,111,101,102,
+ 102,120,124,56,120,114,148,120,108,120,128,93,99,102,115,127,120,
+ 133,105,92,69,82,73,82,101,87,55,92,92,87,82,92,92,92,73,101,73,
+ 96,87,92,114,114,105,103,101,119,77,167,41,69,28,125,114,165,133,
+ 128,137,165,101,165,101,174,110,165,101,165,101,174,119,82,92,137,
+ 102,119,119,119,73,137,119,92,83,92,119,119,128,101,101,128,128,
+ 46,144,92,92,92,92,92,92,92,119,119,119,119,119,128,128,110,42,114,
+ 114,55,55,82,126,126,126,126,132,132,148,83,101,167,132,132,131,
+ 64,64,64,64,64,64,82,82,82,82,114,64,64,64,64,64,64,82,82,82
+};
+static afm_cuint16 afm_Symbol_highchars_index[] = { /* 156 */
+ 172,176,177,181,215,247,402,913,914,915,917,918,919,920,921,922,
+ 923,924,925,926,927,928,929,931,932,933,934,935,936,945,946,947,
+ 948,949,950,951,952,953,954,955,957,958,959,960,961,962,963,964,
+ 965,966,967,968,969,977,978,981,982,8226,8230,8242,8243,8260,8364,
+ 8465,8472,8476,8486,8501,8592,8593,8594,8595,8596,8629,8656,8657,
+ 8658,8659,8660,8704,8706,8707,8709,8710,8711,8712,8713,8715,8719,
+ 8721,8722,8727,8730,8733,8734,8736,8743,8744,8745,8746,8747,8756,
+ 8764,8773,8776,8800,8801,8804,8805,8834,8835,8836,8838,8839,8853,
+ 8855,8869,8901,8992,8993,9001,9002,9674,9824,9827,9829,9830,63193,
+ 63194,63195,63717,63718,63719,63720,63721,63722,63723,63724,63725,
+ 63726,63727,63728,63729,63730,63731,63732,63733,63734,63735,63736,
+ 63737,63738,63739,63740,63741,63742
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Times-Bold */
+/* FullName: Times Bold */
+/* FamilyName: Times */
+static afm_cuint8 afm_Times_Bold_widths[] = { /* 315 */
+ 42,56,93,83,83,167,139,46,56,56,83,95,42,56,42,46,83,83,83,83,83,
+ 83,83,83,83,83,56,56,95,95,95,83,155,120,111,120,120,111,102,130,
+ 130,65,83,130,111,157,120,130,102,130,120,93,111,120,120,167,120,
+ 120,111,56,46,56,97,83,56,83,93,74,93,74,56,83,93,46,56,93,46,139,
+ 93,83,93,93,74,65,56,93,83,120,83,83,74,66,37,66,87,56,83,83,83,
+ 83,37,83,56,125,50,83,95,125,56,67,95,50,50,56,93,90,42,56,50,55,
+ 83,125,125,125,83,120,120,120,120,120,120,167,120,111,111,111,111,
+ 65,65,65,65,120,120,130,130,130,130,130,95,130,120,120,120,120,120,
+ 102,93,83,83,83,83,83,83,120,74,74,74,74,74,46,46,46,46,83,93,83,
+ 83,83,83,83,95,83,93,93,93,93,83,93,83,120,83,120,83,120,83,120,
+ 74,120,74,120,112,120,93,111,74,111,74,111,74,111,74,130,83,130,
+ 83,65,46,65,46,65,46,130,93,111,46,111,46,111,66,111,46,120,93,120,
+ 93,120,93,130,83,130,83,167,120,120,74,120,74,120,74,93,65,93,65,
+ 93,65,111,56,111,69,120,93,120,93,120,93,120,93,120,111,74,111,74,
+ 111,74,83,93,65,56,56,56,56,56,56,56,56,83,167,56,56,56,83,83,83,
+ 83,83,58,167,167,56,56,28,83,167,82,102,100,95,92,92,92,92,82,42,
+ 93,93
+};
+static afm_sint16 afm_Times_Bold_kerning_index[] = { /* 315 */
+ 1,0,0,0,0,0,0,0,0,0,0,0,44,0,53,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,62,183,0,229,0,266,0,0,0,359,472,564,0,599,623,668,761,786,0,
+ 848,1016,1044,1209,0,1364,0,0,0,0,0,0,0,1512,1515,0,1544,1547,1550,
+ 1588,1591,1599,0,1602,0,0,1652,1655,0,0,1660,0,0,0,1746,1816,0,1841,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,1888,2009,2130,2251,2372,2493,0,0,0,0,0,0,0,0,0,0,0,2614,2638,
+ 2683,2728,2773,2818,0,2863,2908,2936,2964,2992,3020,0,0,3168,3171,
+ 3174,3177,3180,3183,0,0,3186,3189,3192,3195,3198,3201,3204,3207,
+ 0,3210,3213,3218,3223,3228,3233,0,3238,0,0,0,0,3243,0,3290,3337,
+ 3458,3461,3582,3585,3706,0,0,0,0,3709,0,3746,3783,0,3786,0,3789,
+ 0,3792,0,3795,0,3798,0,3801,0,3804,0,3807,0,0,3810,3902,3952,0,3987,
+ 0,0,0,4022,0,4057,4081,4084,4108,4111,4135,4138,4183,4188,4233,0,
+ 0,4238,4300,4386,4448,4534,4596,0,0,0,0,0,0,4682,0,4850,0,5018,0,
+ 5046,0,5074,0,5102,0,5130,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 5278,5306,0,5346,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+static afm_cuint8 afm_Times_Bold_kerning_data[] = { /* 5370 */
+ 42,19,66,248,85,252,87,249,88,252,90,248,193,248,194,248,195,248,
+ 196,248,197,248,198,248,222,248,0,2,248,0,4,248,0,6,248,0,100,252,
+ 0,102,252,0,122,248,3,1,32,25,248,1,32,29,249,3,1,32,25,248,1,32,
+ 29,248,51,68,248,72,248,80,249,82,249,85,241,86,249,87,233,88,235,
+ 90,240,113,253,118,249,119,240,120,242,122,245,200,248,211,249,212,
+ 249,213,249,214,249,215,249,217,249,218,249,219,249,220,249,221,
+ 249,222,240,250,249,251,249,252,249,253,249,254,245,0,1,245,0,8,
+ 248,0,14,248,0,32,248,0,36,248,0,78,249,0,82,249,0,100,241,0,102,
+ 241,0,108,249,0,109,249,0,112,249,0,113,249,0,114,249,0,115,249,
+ 0,116,249,0,117,249,0,122,240,1,32,25,245,20,66,252,86,255,193,252,
+ 194,252,195,252,196,252,197,252,198,252,218,255,219,255,220,255,
+ 221,255,0,2,252,0,4,252,0,6,252,0,108,255,0,112,255,0,114,255,0,
+ 116,255,17,47,254,66,251,87,250,88,250,90,250,193,251,194,251,195,
+ 251,196,251,197,251,198,251,222,250,0,2,251,0,4,251,0,6,251,0,122,
+ 250,41,45,242,47,239,66,242,98,253,102,253,112,253,193,242,194,242,
+ 195,242,196,242,197,242,198,242,225,253,226,253,227,253,228,253,
+ 229,253,230,253,233,253,234,253,235,253,236,253,243,253,244,253,
+ 245,253,246,253,247,253,249,253,0,2,242,0,3,253,0,4,242,0,5,253,
+ 0,6,242,0,7,253,0,21,253,0,25,253,0,27,253,0,29,253,0,79,253,0,83,
+ 253,49,47,254,66,252,98,254,102,254,112,254,118,254,193,252,194,
+ 252,195,252,196,252,197,252,198,252,225,254,226,254,227,254,228,
+ 254,229,254,230,254,233,254,234,254,235,254,236,254,243,254,244,
+ 254,245,254,246,254,247,254,249,254,250,254,251,254,252,254,253,
+ 254,0,2,252,0,3,254,0,4,252,0,5,254,0,6,252,0,7,254,0,21,254,0,25,
+ 254,0,27,254,0,29,254,0,79,254,0,83,254,0,109,254,0,113,254,0,115,
+ 254,0,117,254,40,80,252,102,253,112,253,118,254,122,249,211,252,
+ 212,252,213,252,214,252,215,252,217,252,233,253,234,253,235,253,
+ 236,253,243,253,244,253,245,253,246,253,247,253,249,253,250,254,
+ 251,254,252,254,253,254,254,249,0,1,249,0,21,253,0,25,253,0,27,253,
+ 0,29,253,0,78,252,0,79,253,0,82,252,0,83,253,0,109,254,0,113,254,
+ 0,115,254,0,117,254,14,85,242,87,242,88,242,90,242,122,248,222,242,
+ 254,248,0,1,248,0,100,242,0,102,242,0,122,242,1,32,25,239,1,32,29,
+ 254,11,66,254,193,254,194,254,195,254,196,254,197,254,198,254,0,
+ 2,254,0,4,254,0,6,254,20,66,250,85,250,87,249,88,249,89,250,90,249,
+ 193,250,194,250,195,250,196,250,197,250,198,250,222,249,0,2,250,
+ 0,4,250,0,6,250,0,100,250,0,102,250,0,122,249,41,45,242,47,239,66,
+ 245,98,255,102,254,112,254,193,245,194,245,195,245,196,245,197,245,
+ 198,245,225,255,226,255,227,255,228,255,229,255,230,255,233,254,
+ 234,254,235,254,236,254,243,254,244,254,245,254,246,254,247,254,
+ 249,254,0,2,245,0,3,255,0,4,245,0,5,255,0,6,245,0,7,255,0,21,254,
+ 0,25,254,0,27,254,0,29,254,0,79,254,0,83,254,11,47,254,86,255,218,
+ 255,219,255,220,255,221,255,0,108,255,0,112,255,0,114,255,0,116,
+ 255,27,80,252,85,250,86,252,87,248,88,251,90,251,211,252,212,252,
+ 213,252,214,252,215,252,217,252,218,252,219,252,220,252,221,252,
+ 222,251,0,78,252,0,82,252,0,100,250,0,102,250,0,108,252,0,112,252,
+ 0,114,252,0,116,252,0,122,251,73,45,245,46,242,47,242,59,245,60,
+ 245,66,242,80,254,98,242,102,242,106,254,112,242,115,245,118,242,
+ 120,245,122,251,193,242,194,242,195,242,196,242,197,242,198,242,
+ 211,254,212,254,213,254,214,254,215,254,217,254,225,248,226,242,
+ 227,248,228,248,229,248,230,242,233,248,234,242,235,242,236,248,
+ 238,254,243,242,244,242,245,242,246,242,247,242,249,242,250,242,
+ 251,242,252,242,253,242,254,251,0,1,251,0,2,242,0,3,248,0,4,242,
+ 0,5,248,0,6,242,0,7,242,0,21,248,0,25,242,0,27,242,0,29,242,0,49,
+ 254,0,78,254,0,79,242,0,82,254,0,83,242,0,87,245,0,89,245,0,91,245,
+ 0,109,242,0,113,242,0,115,242,0,117,242,13,45,249,47,249,66,247,
+ 193,247,194,247,195,247,196,247,197,247,198,247,0,2,247,0,4,247,
+ 0,6,247,72,45,235,46,245,47,233,59,242,60,242,66,234,72,252,80,249,
+ 98,242,102,240,106,251,112,240,118,242,193,234,194,234,195,234,196,
+ 234,197,234,198,234,211,249,212,249,213,249,214,249,215,249,217,
+ 249,225,242,226,242,227,242,228,242,229,242,230,242,233,240,234,
+ 240,235,240,236,240,237,251,238,251,239,251,240,251,243,240,244,
+ 240,245,240,246,240,247,240,249,240,250,242,251,242,252,242,253,
+ 242,0,2,234,0,3,242,0,4,234,0,5,242,0,6,234,0,7,242,0,21,240,0,25,
+ 240,0,27,240,0,29,240,0,32,252,0,36,252,0,45,251,0,49,251,0,78,249,
+ 0,79,240,0,82,249,0,83,240,0,109,242,0,113,242,0,115,242,0,117,242,
+ 68,45,242,46,251,47,242,59,248,60,248,66,237,80,255,98,246,102,246,
+ 106,254,112,244,118,249,122,247,193,237,194,237,195,237,196,237,
+ 197,237,198,237,211,255,212,255,213,255,214,255,215,255,217,255,
+ 225,246,226,246,227,246,228,246,229,246,230,246,233,246,234,246,
+ 235,246,236,246,238,254,243,244,244,244,245,244,246,244,247,244,
+ 249,244,250,249,251,249,252,249,253,249,254,247,0,1,247,0,2,237,
+ 0,3,246,0,4,237,0,5,246,0,6,237,0,7,246,0,21,246,0,25,246,0,27,246,
+ 0,29,246,0,49,254,0,78,255,0,79,244,0,82,255,0,83,244,0,109,249,
+ 0,113,249,0,115,249,0,117,249,65,45,242,46,242,47,242,59,242,60,
+ 242,66,239,80,251,98,243,102,238,106,251,112,238,118,242,193,239,
+ 194,239,195,239,196,239,197,239,198,239,211,251,212,251,213,251,
+ 214,251,215,251,217,251,225,243,226,243,227,243,228,243,229,243,
+ 230,243,233,245,234,238,235,238,236,245,238,251,243,238,244,238,
+ 245,238,246,238,247,238,249,238,250,242,251,242,252,242,253,242,
+ 0,2,239,0,3,243,0,4,239,0,5,243,0,6,239,0,7,243,0,21,245,0,25,238,
+ 0,27,238,0,29,238,0,49,251,0,78,251,0,79,238,0,82,251,0,83,238,0,
+ 109,242,0,113,242,0,115,242,0,117,242,2,119,253,13,47,250,99,255,
+ 118,254,119,254,250,254,251,254,252,254,253,254,0,109,254,0,113,
+ 254,0,115,254,0,117,254,2,120,254,2,119,254,16,45,254,47,254,106,
+ 253,112,253,243,253,244,253,245,253,246,253,247,253,249,253,0,51,
+ 251,0,79,253,0,83,253,1,32,25,9,1,32,29,8,2,47,254,4,122,254,254,
+ 254,0,1,254,2,119,255,22,102,255,112,254,122,254,233,255,234,255,
+ 235,255,236,255,243,254,244,254,245,254,246,254,247,254,249,254,
+ 254,254,0,1,254,0,21,255,0,25,255,0,27,255,0,29,255,0,79,254,0,83,
+ 254,2,119,250,3,119,255,120,255,37,45,242,46,251,47,240,100,254,
+ 102,254,104,255,111,254,112,254,113,255,114,254,119,255,232,254,
+ 233,254,234,254,235,254,236,254,242,254,243,254,244,254,245,254,
+ 246,254,247,254,249,254,0,9,254,0,15,254,0,21,254,0,25,254,0,27,
+ 254,0,29,254,0,33,255,0,37,255,0,70,254,0,72,254,0,74,254,0,79,254,
+ 0,83,254,31,45,248,47,245,98,255,102,255,112,255,225,255,226,255,
+ 227,255,228,255,229,255,230,255,233,255,234,255,235,255,236,255,
+ 243,255,244,255,245,255,246,255,247,255,249,255,0,3,255,0,5,255,
+ 0,7,255,0,21,255,0,25,255,0,27,255,0,29,255,0,79,255,0,83,255,12,
+ 45,248,47,245,112,255,243,255,244,255,245,255,246,255,247,255,249,
+ 255,0,79,255,0,83,255,21,45,248,47,245,102,255,112,253,233,255,234,
+ 255,235,255,236,255,243,253,244,253,245,253,246,253,247,253,249,
+ 253,0,21,255,0,25,255,0,27,255,0,29,255,0,79,253,0,83,253,51,68,
+ 248,72,248,80,249,82,249,85,241,86,249,87,233,88,235,90,240,113,
+ 253,118,249,119,240,120,242,122,245,200,248,211,249,212,249,213,
+ 249,214,249,215,249,217,249,218,249,219,249,220,249,221,249,222,
+ 240,250,249,251,249,252,249,253,249,254,245,0,1,245,0,8,248,0,14,
+ 248,0,32,248,0,36,248,0,78,249,0,82,249,0,100,241,0,102,241,0,108,
+ 249,0,109,249,0,112,249,0,113,249,0,114,249,0,115,249,0,116,249,
+ 0,117,249,0,122,240,1,32,25,245,51,68,248,72,248,80,249,82,249,85,
+ 241,86,249,87,233,88,235,90,240,113,253,118,249,119,240,120,242,
+ 122,245,200,248,211,249,212,249,213,249,214,249,215,249,217,249,
+ 218,249,219,249,220,249,221,249,222,240,250,249,251,249,252,249,
+ 253,249,254,245,0,1,245,0,8,248,0,14,248,0,32,248,0,36,248,0,78,
+ 249,0,82,249,0,100,241,0,102,241,0,108,249,0,109,249,0,112,249,0,
+ 113,249,0,114,249,0,115,249,0,116,249,0,117,249,0,122,240,1,32,25,
+ 245,51,68,248,72,248,80,249,82,249,85,241,86,249,87,233,88,235,90,
+ 240,113,253,118,249,119,240,120,242,122,245,200,248,211,249,212,
+ 249,213,249,214,249,215,249,217,249,218,249,219,249,220,249,221,
+ 249,222,240,250,249,251,249,252,249,253,249,254,245,0,1,245,0,8,
+ 248,0,14,248,0,32,248,0,36,248,0,78,249,0,82,249,0,100,241,0,102,
+ 241,0,108,249,0,109,249,0,112,249,0,113,249,0,114,249,0,115,249,
+ 0,116,249,0,117,249,0,122,240,1,32,25,245,51,68,248,72,248,80,249,
+ 82,249,85,241,86,249,87,233,88,235,90,240,113,253,118,249,119,240,
+ 120,242,122,245,200,248,211,249,212,249,213,249,214,249,215,249,
+ 217,249,218,249,219,249,220,249,221,249,222,240,250,249,251,249,
+ 252,249,253,249,254,245,0,1,245,0,8,248,0,14,248,0,32,248,0,36,248,
+ 0,78,249,0,82,249,0,100,241,0,102,241,0,108,249,0,109,249,0,112,
+ 249,0,113,249,0,114,249,0,115,249,0,116,249,0,117,249,0,122,240,
+ 1,32,25,245,51,68,248,72,248,80,249,82,249,85,241,86,249,87,233,
+ 88,235,90,240,113,253,118,249,119,240,120,242,122,245,200,248,211,
+ 249,212,249,213,249,214,249,215,249,217,249,218,249,219,249,220,
+ 249,221,249,222,240,250,249,251,249,252,249,253,249,254,245,0,1,
+ 245,0,8,248,0,14,248,0,32,248,0,36,248,0,78,249,0,82,249,0,100,241,
+ 0,102,241,0,108,249,0,109,249,0,112,249,0,113,249,0,114,249,0,115,
+ 249,0,116,249,0,117,249,0,122,240,1,32,25,245,51,68,248,72,248,80,
+ 249,82,249,85,241,86,249,87,233,88,235,90,240,113,253,118,249,119,
+ 240,120,242,122,245,200,248,211,249,212,249,213,249,214,249,215,
+ 249,217,249,218,249,219,249,220,249,221,249,222,240,250,249,251,
+ 249,252,249,253,249,254,245,0,1,245,0,8,248,0,14,248,0,32,248,0,
+ 36,248,0,78,249,0,82,249,0,100,241,0,102,241,0,108,249,0,109,249,
+ 0,112,249,0,113,249,0,114,249,0,115,249,0,116,249,0,117,249,0,122,
+ 240,1,32,25,245,11,66,254,193,254,194,254,195,254,196,254,197,254,
+ 198,254,0,2,254,0,4,254,0,6,254,20,66,250,85,250,87,249,88,249,89,
+ 250,90,249,193,250,194,250,195,250,196,250,197,250,198,250,222,249,
+ 0,2,250,0,4,250,0,6,250,0,100,250,0,102,250,0,122,249,20,66,250,
+ 85,250,87,249,88,249,89,250,90,249,193,250,194,250,195,250,196,250,
+ 197,250,198,250,222,249,0,2,250,0,4,250,0,6,250,0,100,250,0,102,
+ 250,0,122,249,20,66,250,85,250,87,249,88,249,89,250,90,249,193,250,
+ 194,250,195,250,196,250,197,250,198,250,222,249,0,2,250,0,4,250,
+ 0,6,250,0,100,250,0,102,250,0,122,249,20,66,250,85,250,87,249,88,
+ 249,89,250,90,249,193,250,194,250,195,250,196,250,197,250,198,250,
+ 222,249,0,2,250,0,4,250,0,6,250,0,100,250,0,102,250,0,122,249,20,
+ 66,250,85,250,87,249,88,249,89,250,90,249,193,250,194,250,195,250,
+ 196,250,197,250,198,250,222,249,0,2,250,0,4,250,0,6,250,0,100,250,
+ 0,102,250,0,122,249,20,66,250,85,250,87,249,88,249,89,250,90,249,
+ 193,250,194,250,195,250,196,250,197,250,198,250,222,249,0,2,250,
+ 0,4,250,0,6,250,0,100,250,0,102,250,0,122,249,13,45,249,47,249,66,
+ 247,193,247,194,247,195,247,196,247,197,247,198,247,0,2,247,0,4,
+ 247,0,6,247,13,45,249,47,249,66,247,193,247,194,247,195,247,196,
+ 247,197,247,198,247,0,2,247,0,4,247,0,6,247,13,45,249,47,249,66,
+ 247,193,247,194,247,195,247,196,247,197,247,198,247,0,2,247,0,4,
+ 247,0,6,247,13,45,249,47,249,66,247,193,247,194,247,195,247,196,
+ 247,197,247,198,247,0,2,247,0,4,247,0,6,247,65,45,242,46,242,47,
+ 242,59,242,60,242,66,239,80,251,98,243,102,238,106,251,112,238,118,
+ 242,193,239,194,239,195,239,196,239,197,239,198,239,211,251,212,
+ 251,213,251,214,251,215,251,217,251,225,243,226,243,227,243,228,
+ 243,229,243,230,243,233,245,234,238,235,238,236,245,238,251,243,
+ 238,244,238,245,238,246,238,247,238,249,238,250,242,251,242,252,
+ 242,253,242,0,2,239,0,3,243,0,4,239,0,5,243,0,6,239,0,7,243,0,21,
+ 245,0,25,238,0,27,238,0,29,238,0,49,251,0,78,251,0,79,238,0,82,251,
+ 0,83,238,0,109,242,0,113,242,0,115,242,0,117,242,2,119,253,2,119,
+ 253,2,119,253,2,119,253,2,119,253,2,119,253,2,119,254,2,119,254,
+ 2,119,254,2,119,254,2,119,255,2,119,255,2,119,255,2,119,255,2,119,
+ 250,3,119,255,120,255,3,119,255,120,255,3,119,255,120,255,3,119,
+ 255,120,255,3,119,255,120,255,3,119,255,120,255,21,45,248,47,245,
+ 102,255,112,253,233,255,234,255,235,255,236,255,243,253,244,253,
+ 245,253,246,253,247,253,249,253,0,21,255,0,25,255,0,27,255,0,29,
+ 255,0,79,253,0,83,253,21,45,248,47,245,102,255,112,253,233,255,234,
+ 255,235,255,236,255,243,253,244,253,245,253,246,253,247,253,249,
+ 253,0,21,255,0,25,255,0,27,255,0,29,255,0,79,253,0,83,253,51,68,
+ 248,72,248,80,249,82,249,85,241,86,249,87,233,88,235,90,240,113,
+ 253,118,249,119,240,120,242,122,245,200,248,211,249,212,249,213,
+ 249,214,249,215,249,217,249,218,249,219,249,220,249,221,249,222,
+ 240,250,249,251,249,252,249,253,249,254,245,0,1,245,0,8,248,0,14,
+ 248,0,32,248,0,36,248,0,78,249,0,82,249,0,100,241,0,102,241,0,108,
+ 249,0,109,249,0,112,249,0,113,249,0,114,249,0,115,249,0,116,249,
+ 0,117,249,0,122,240,1,32,25,245,2,119,253,51,68,248,72,248,80,249,
+ 82,249,85,241,86,249,87,233,88,235,90,240,113,253,118,249,119,240,
+ 120,242,122,245,200,248,211,249,212,249,213,249,214,249,215,249,
+ 217,249,218,249,219,249,220,249,221,249,222,240,250,249,251,249,
+ 252,249,253,249,254,245,0,1,245,0,8,248,0,14,248,0,32,248,0,36,248,
+ 0,78,249,0,82,249,0,100,241,0,102,241,0,108,249,0,109,249,0,112,
+ 249,0,113,249,0,114,249,0,115,249,0,116,249,0,117,249,0,122,240,
+ 1,32,25,245,2,119,253,51,68,248,72,248,80,249,82,249,85,241,86,249,
+ 87,233,88,235,90,240,113,253,118,249,119,240,120,242,122,251,200,
+ 248,211,249,212,249,213,249,214,249,215,249,217,249,218,249,219,
+ 249,220,249,221,249,222,240,250,249,251,249,252,249,253,249,254,
+ 251,0,1,251,0,8,248,0,14,248,0,32,248,0,36,248,0,78,249,0,82,249,
+ 0,100,241,0,102,241,0,108,249,0,109,249,0,112,249,0,113,249,0,114,
+ 249,0,115,249,0,116,249,0,117,249,0,122,240,1,32,25,245,2,119,253,
+ 17,47,254,66,251,87,250,88,250,90,250,193,251,194,251,195,251,196,
+ 251,197,251,198,251,222,250,0,2,251,0,4,251,0,6,251,0,122,250,17,
+ 47,254,66,251,87,250,88,250,90,250,193,251,194,251,195,251,196,251,
+ 197,251,198,251,222,250,0,2,251,0,4,251,0,6,251,0,122,250,2,120,
+ 254,2,119,254,2,119,254,2,119,254,2,119,254,2,47,254,2,47,254,2,
+ 119,255,2,119,255,40,80,252,102,253,112,253,118,254,122,249,211,
+ 252,212,252,213,252,214,252,215,252,217,252,233,253,234,253,235,
+ 253,236,253,243,253,244,253,245,253,246,253,247,253,249,253,250,
+ 254,251,254,252,254,253,254,254,249,0,1,249,0,21,253,0,25,253,0,
+ 27,253,0,29,253,0,78,252,0,79,253,0,82,252,0,83,253,0,109,254,0,
+ 113,254,0,115,254,0,117,254,22,102,255,112,254,122,254,233,255,234,
+ 255,235,255,236,255,243,254,244,254,245,254,246,254,247,254,249,
+ 254,254,254,0,1,254,0,21,255,0,25,255,0,27,255,0,29,255,0,79,254,
+ 0,83,254,14,85,242,87,242,88,242,90,242,122,248,222,242,254,248,
+ 0,1,248,0,100,242,0,102,242,0,122,242,1,32,25,239,1,32,29,254,14,
+ 85,242,87,242,88,242,90,242,122,248,222,242,254,248,0,1,248,0,100,
+ 242,0,102,242,0,122,242,1,32,25,239,1,32,29,254,14,85,242,87,242,
+ 88,242,90,242,122,248,222,242,254,248,0,1,248,0,100,242,0,102,242,
+ 0,122,242,1,32,25,239,1,32,29,254,11,66,254,193,254,194,254,195,
+ 254,196,254,197,254,198,254,0,2,254,0,4,254,0,6,254,2,119,250,11,
+ 66,254,193,254,194,254,195,254,196,254,197,254,198,254,0,2,254,0,
+ 4,254,0,6,254,2,119,250,11,66,254,193,254,194,254,195,254,196,254,
+ 197,254,198,254,0,2,254,0,4,254,0,6,254,2,119,250,20,66,250,85,250,
+ 87,249,88,249,89,250,90,249,193,250,194,250,195,250,196,250,197,
+ 250,198,250,222,249,0,2,250,0,4,250,0,6,250,0,100,250,0,102,250,
+ 0,122,249,3,119,255,120,255,20,66,250,85,250,87,249,88,249,89,250,
+ 90,249,193,250,194,250,195,250,196,250,197,250,198,250,222,249,0,
+ 2,250,0,4,250,0,6,250,0,100,250,0,102,250,0,122,249,3,119,255,120,
+ 255,27,80,252,85,250,86,252,87,248,88,251,90,251,211,252,212,252,
+ 213,252,214,252,215,252,217,252,218,252,219,252,220,252,221,252,
+ 222,251,0,78,252,0,82,252,0,100,250,0,102,250,0,108,252,0,112,252,
+ 0,114,252,0,116,252,0,122,251,37,45,242,46,251,47,240,100,254,102,
+ 254,104,255,111,254,112,254,113,255,114,254,119,255,232,254,233,
+ 254,234,254,235,254,236,254,242,254,243,254,244,254,245,254,246,
+ 254,247,254,249,254,0,9,254,0,15,254,0,21,254,0,25,254,0,27,254,
+ 0,29,254,0,33,255,0,37,255,0,70,254,0,72,254,0,74,254,0,79,254,0,
+ 83,254,27,80,252,85,250,86,252,87,248,88,251,90,251,211,252,212,
+ 252,213,252,214,252,215,252,217,252,218,252,219,252,220,252,221,
+ 252,222,251,0,78,252,0,82,252,0,100,250,0,102,250,0,108,252,0,112,
+ 252,0,114,252,0,116,252,0,122,251,37,45,242,46,251,47,240,100,254,
+ 102,254,104,255,111,254,112,254,113,255,114,254,119,255,232,254,
+ 233,254,234,254,235,254,236,254,242,254,243,254,244,254,245,254,
+ 246,254,247,254,249,254,0,9,254,0,15,254,0,21,254,0,25,254,0,27,
+ 254,0,29,254,0,33,255,0,37,255,0,70,254,0,72,254,0,74,254,0,79,254,
+ 0,83,254,27,80,252,85,250,86,252,87,248,88,251,90,251,211,252,212,
+ 252,213,252,214,252,215,252,217,252,218,252,219,252,220,252,221,
+ 252,222,251,0,78,252,0,82,252,0,100,250,0,102,250,0,108,252,0,112,
+ 252,0,114,252,0,116,252,0,122,251,37,45,242,46,251,47,240,100,254,
+ 102,254,104,255,111,254,112,254,113,255,114,254,119,255,232,254,
+ 233,254,234,254,235,254,236,254,242,254,243,254,244,254,245,254,
+ 246,254,247,254,249,254,0,9,254,0,15,254,0,21,254,0,25,254,0,27,
+ 254,0,29,254,0,33,255,0,37,255,0,70,254,0,72,254,0,74,254,0,79,254,
+ 0,83,254,73,45,245,46,242,47,242,59,245,60,245,66,242,80,254,98,
+ 242,102,242,106,254,112,242,115,245,118,242,120,245,122,251,193,
+ 242,194,242,195,242,196,242,197,242,198,242,211,254,212,254,213,
+ 254,214,254,215,254,217,254,225,248,226,242,227,248,228,248,229,
+ 248,230,242,233,248,234,242,235,242,236,248,238,254,243,242,244,
+ 242,245,242,246,242,247,242,249,242,250,242,251,242,252,242,253,
+ 242,254,251,0,1,251,0,2,242,0,3,248,0,4,242,0,5,248,0,6,242,0,7,
+ 242,0,21,248,0,25,242,0,27,242,0,29,242,0,49,254,0,78,254,0,79,242,
+ 0,82,254,0,83,242,0,87,245,0,89,245,0,91,245,0,109,242,0,113,242,
+ 0,115,242,0,117,242,73,45,245,46,242,47,242,59,245,60,245,66,242,
+ 80,254,98,242,102,242,106,254,112,242,115,245,118,242,120,245,122,
+ 251,193,242,194,242,195,242,196,242,197,242,198,242,211,254,212,
+ 254,213,254,214,254,215,254,217,254,225,248,226,242,227,248,228,
+ 248,229,248,230,242,233,248,234,242,235,242,236,248,238,254,243,
+ 242,244,242,245,242,246,242,247,242,249,242,250,242,251,242,252,
+ 242,253,242,254,251,0,1,251,0,2,242,0,3,248,0,4,242,0,5,248,0,6,
+ 242,0,7,242,0,21,248,0,25,242,0,27,242,0,29,242,0,49,254,0,78,254,
+ 0,79,242,0,82,254,0,83,242,0,87,245,0,89,245,0,91,245,0,109,242,
+ 0,113,242,0,115,242,0,117,242,13,45,249,47,249,66,247,193,247,194,
+ 247,195,247,196,247,197,247,198,247,0,2,247,0,4,247,0,6,247,13,45,
+ 249,47,249,66,247,193,247,194,247,195,247,196,247,197,247,198,247,
+ 0,2,247,0,4,247,0,6,247,13,45,249,47,249,66,247,193,247,194,247,
+ 195,247,196,247,197,247,198,247,0,2,247,0,4,247,0,6,247,13,45,249,
+ 47,249,66,247,193,247,194,247,195,247,196,247,197,247,198,247,0,
+ 2,247,0,4,247,0,6,247,65,45,242,46,242,47,242,59,242,60,242,66,239,
+ 80,251,98,243,102,238,106,251,112,238,118,242,193,239,194,239,195,
+ 239,196,239,197,239,198,239,211,251,212,251,213,251,214,251,215,
+ 251,217,251,225,243,226,243,227,243,228,243,229,243,230,243,233,
+ 245,234,238,235,238,236,245,238,251,243,238,244,238,245,238,246,
+ 238,247,238,249,238,250,242,251,242,252,242,253,242,0,2,239,0,3,
+ 243,0,4,239,0,5,243,0,6,239,0,7,243,0,21,245,0,25,238,0,27,238,0,
+ 29,238,0,49,251,0,78,251,0,79,238,0,82,251,0,83,238,0,109,242,0,
+ 113,242,0,115,242,0,117,242,12,66,255,193,255,194,255,195,255,196,
+ 255,197,255,198,255,0,2,255,0,4,255,0,6,255,1,32,24,246,15,33,245,
+ 101,254,115,254,116,251,119,254,0,19,254,0,87,254,0,89,254,0,91,
+ 254,0,93,251,0,97,251,0,99,251,1,2,25,251,1,32,25,246,11,66,255,
+ 193,255,194,255,195,255,196,255,197,255,198,255,0,2,255,0,4,255,
+ 0,6,255
+};
+static afm_cuint16 afm_Times_Bold_highchars_index[] = { /* 220 */
+ 161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+ 178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+ 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+ 210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+ 226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+ 242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+ 258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+ 280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+ 313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+ 336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+ 354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+ 380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+ 8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+ 8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+ 64257,64258
+};
+static afm_cunicode afm_Times_Bold_ligatures[] = { /* 3 */
+ 102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Times-BoldItalic */
+/* FullName: Times Bold Italic */
+/* FamilyName: Times */
+static afm_cuint8 afm_Times_BoldItalic_widths[] = { /* 315 */
+ 42,65,93,83,83,139,130,46,56,56,83,95,42,56,42,46,83,83,83,83,83,
+ 83,83,83,83,83,56,56,95,95,95,83,139,111,111,111,120,111,111,120,
+ 130,65,83,111,102,148,120,120,102,120,111,93,102,120,111,148,111,
+ 102,102,56,46,56,95,83,56,83,83,74,83,74,56,83,93,46,46,83,46,130,
+ 93,83,83,83,65,65,46,93,74,111,83,74,65,58,37,58,95,65,83,83,83,
+ 83,37,83,56,125,44,83,101,125,56,67,95,50,50,56,96,83,42,56,50,50,
+ 83,125,125,125,83,111,111,111,111,111,111,157,111,111,111,111,111,
+ 65,65,65,65,120,120,120,120,120,120,120,95,120,120,120,120,120,102,
+ 102,83,83,83,83,83,83,83,120,74,74,74,74,74,46,46,46,46,83,93,83,
+ 83,83,83,83,95,83,93,93,93,93,74,83,74,111,83,111,83,111,83,111,
+ 74,111,74,120,101,120,83,111,74,111,74,111,74,111,74,120,83,120,
+ 83,65,46,65,46,65,46,111,83,102,46,102,46,102,64,102,46,120,93,120,
+ 93,120,93,120,83,120,83,157,120,111,65,111,65,111,65,93,65,93,65,
+ 93,65,102,46,102,61,120,93,120,93,120,93,120,93,102,102,65,102,65,
+ 102,65,83,93,65,56,56,56,56,56,56,56,56,83,167,56,56,56,83,83,83,
+ 83,83,58,167,167,56,56,28,83,167,82,102,100,101,92,92,92,92,82,42,
+ 93,93
+};
+static afm_sint16 afm_Times_BoldItalic_kerning_index[] = { /* 315 */
+ 1,0,0,0,0,0,0,0,0,0,0,0,36,0,45,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,54,173,0,219,0,254,0,0,0,374,489,581,0,612,636,681,774,797,0,
+ 859,1027,1051,1207,0,1362,0,0,0,0,0,0,0,0,1510,1537,0,1545,1548,
+ 0,0,0,0,1587,0,0,1630,1633,0,0,1647,0,0,0,1652,1699,1769,1792,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,1797,1916,2035,2154,2273,2392,0,0,0,0,0,0,0,0,0,0,0,2511,2535,
+ 2580,2625,2670,2715,0,2760,2805,2829,2853,2877,2901,0,0,0,0,0,0,
+ 0,0,0,3049,3057,3060,3063,3066,0,0,0,0,0,3069,3072,3086,3100,3114,
+ 3128,0,3142,0,0,0,0,3156,0,3161,3166,0,3285,0,3404,0,0,3523,0,3531,
+ 3539,0,3574,0,0,3609,0,3612,0,3615,0,3618,0,0,0,0,0,0,0,0,0,0,3621,
+ 3713,3756,0,3787,0,0,0,3818,0,3849,3873,3876,3900,3903,3927,3930,
+ 3975,3989,4034,0,0,4048,4110,4115,4177,4182,4244,0,0,0,0,0,0,4249,
+ 0,4417,0,4585,0,4609,0,4633,0,4657,0,4681,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,4829,4834,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0
+};
+static afm_cuint8 afm_Times_BoldItalic_kerning_data[] = { /* 4879 */
+ 42,16,66,251,87,245,88,245,90,245,193,251,194,251,195,251,196,251,
+ 197,251,198,251,222,245,0,2,251,0,4,251,0,6,251,0,122,245,3,1,32,
+ 25,241,1,32,29,241,3,1,32,25,241,1,32,29,241,50,68,246,72,247,80,
+ 249,82,248,85,248,86,249,87,241,88,240,90,245,118,252,119,245,120,
+ 245,122,245,200,246,211,249,212,249,213,249,214,249,215,249,217,
+ 249,218,249,219,249,220,249,221,249,222,245,250,252,251,252,252,
+ 252,253,252,254,245,0,1,245,0,8,246,0,14,246,0,32,247,0,36,247,0,
+ 78,249,0,82,249,0,100,248,0,102,248,0,108,249,0,109,252,0,112,249,
+ 0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,245,1,32,
+ 25,245,20,66,253,86,255,193,253,194,253,195,253,196,253,197,253,
+ 198,253,218,255,219,255,220,255,221,255,0,2,253,0,4,253,0,6,253,
+ 0,108,255,0,112,255,0,114,255,0,116,255,16,66,253,87,249,88,250,
+ 90,249,193,253,194,253,195,253,196,253,197,253,198,253,222,249,0,
+ 2,253,0,4,253,0,6,253,0,122,249,52,45,235,47,235,66,240,98,241,102,
+ 240,106,250,112,245,115,249,193,240,194,240,195,240,196,240,197,
+ 240,198,240,225,241,226,241,227,241,228,241,229,241,230,241,233,
+ 240,234,240,235,240,236,240,237,250,238,250,239,250,240,250,243,
+ 245,244,245,245,245,246,245,247,245,249,245,0,2,240,0,3,241,0,4,
+ 240,0,5,241,0,6,240,0,7,241,0,21,240,0,25,240,0,27,240,0,29,240,
+ 0,45,250,0,49,250,0,79,245,0,83,245,0,87,249,0,89,249,0,91,249,50,
+ 45,255,47,255,66,253,98,250,102,250,112,250,118,250,193,253,194,
+ 253,195,253,196,253,197,253,198,253,225,250,226,250,227,250,228,
+ 250,229,250,230,250,233,250,234,250,235,250,236,250,243,250,244,
+ 250,245,250,246,250,247,250,249,250,250,250,251,250,252,250,253,
+ 250,0,2,253,0,3,250,0,4,253,0,5,250,0,6,253,0,7,250,0,21,250,0,25,
+ 250,0,27,250,0,29,250,0,79,250,0,83,250,0,109,250,0,113,250,0,115,
+ 250,0,117,250,40,80,252,102,253,112,253,118,254,122,254,211,252,
+ 212,252,213,252,214,252,215,252,217,252,233,253,234,253,235,253,
+ 236,253,243,253,244,253,245,253,246,253,247,253,249,253,250,254,
+ 251,254,252,254,253,254,254,254,0,1,254,0,21,253,0,25,253,0,27,253,
+ 0,29,253,0,78,252,0,79,253,0,82,252,0,83,253,0,109,254,0,113,254,
+ 0,115,254,0,117,254,13,85,254,87,251,88,251,90,251,122,251,222,251,
+ 254,251,0,1,251,0,100,254,0,102,254,0,122,251,1,32,25,248,11,66,
+ 252,193,252,194,252,195,252,196,252,197,252,198,252,0,2,252,0,4,
+ 252,0,6,252,20,66,250,85,250,87,249,88,249,89,250,90,249,193,250,
+ 194,250,195,250,196,250,197,250,198,250,222,249,0,2,250,0,4,250,
+ 0,6,250,0,100,250,0,102,250,0,122,249,41,45,235,47,235,66,243,98,
+ 250,102,249,112,248,193,243,194,243,195,243,196,243,197,243,198,
+ 243,225,250,226,250,227,250,228,250,229,250,230,250,233,249,234,
+ 249,235,249,236,249,243,248,244,248,245,248,246,248,247,248,249,
+ 248,0,2,243,0,3,250,0,4,243,0,5,250,0,6,243,0,7,250,0,21,249,0,25,
+ 249,0,27,249,0,29,249,0,79,248,0,83,248,10,86,255,218,255,219,255,
+ 220,255,221,255,0,108,255,0,112,255,0,114,255,0,116,255,27,80,250,
+ 85,252,86,250,87,254,88,254,90,254,211,250,212,250,213,250,214,250,
+ 215,250,217,250,218,250,219,250,220,250,221,250,222,254,0,78,250,
+ 0,82,250,0,100,252,0,102,252,0,108,250,0,112,250,0,114,250,0,116,
+ 250,0,122,254,73,45,242,46,242,47,242,59,245,60,245,66,248,80,254,
+ 98,242,102,242,106,251,112,241,115,251,118,251,120,251,122,251,193,
+ 248,194,248,195,248,196,248,197,248,198,248,211,254,212,254,213,
+ 254,214,254,215,254,217,254,225,242,226,242,227,242,228,242,229,
+ 242,230,242,233,248,234,242,235,242,236,248,238,251,243,241,244,
+ 241,245,241,246,241,247,241,249,241,250,251,251,251,252,251,253,
+ 251,254,251,0,1,251,0,2,248,0,3,242,0,4,248,0,5,242,0,6,248,0,7,
+ 242,0,21,248,0,25,242,0,27,242,0,29,242,0,49,251,0,78,254,0,79,241,
+ 0,82,254,0,83,241,0,87,251,0,89,251,0,91,251,0,109,251,0,113,251,
+ 0,115,251,0,117,251,11,66,249,193,249,194,249,195,249,196,249,197,
+ 249,198,249,0,2,249,0,4,249,0,6,249,68,45,235,46,245,47,235,59,245,
+ 60,245,66,243,72,255,80,252,98,238,102,238,106,248,112,238,118,248,
+ 193,243,194,243,195,243,196,243,197,243,198,243,211,252,212,252,
+ 213,252,214,252,215,252,217,252,225,238,226,238,227,238,228,238,
+ 229,238,230,238,233,245,234,238,235,238,236,245,238,248,243,238,
+ 244,238,245,238,246,238,247,238,249,238,250,248,251,248,252,248,
+ 253,248,0,2,243,0,3,238,0,4,243,0,5,238,0,6,243,0,7,238,0,21,245,
+ 0,25,238,0,27,238,0,29,238,0,32,255,0,36,255,0,49,248,0,78,252,0,
+ 79,238,0,82,252,0,83,238,0,109,248,0,113,248,0,115,248,0,117,248,
+ 68,45,245,46,249,47,245,59,248,60,248,66,245,80,254,98,243,102,242,
+ 106,251,112,244,118,248,122,248,193,245,194,245,195,245,196,245,
+ 197,245,198,245,211,254,212,254,213,254,214,254,215,254,217,254,
+ 225,243,226,243,227,243,228,243,229,243,230,243,233,249,234,242,
+ 235,242,236,249,238,251,243,244,244,244,245,244,246,244,247,244,
+ 249,244,250,248,251,248,252,248,253,248,254,248,0,1,248,0,2,245,
+ 0,3,243,0,4,245,0,5,243,0,6,245,0,7,243,0,21,249,0,25,242,0,27,242,
+ 0,29,242,0,49,251,0,78,254,0,79,244,0,82,254,0,83,244,0,109,248,
+ 0,113,248,0,115,248,0,117,248,65,45,242,46,242,47,245,59,242,60,
+ 242,66,245,80,253,98,242,102,238,106,248,112,238,118,242,193,245,
+ 194,245,195,245,196,245,197,245,198,245,211,253,212,253,213,253,
+ 214,253,215,253,217,253,225,242,226,242,227,242,228,242,229,242,
+ 230,242,233,245,234,238,235,245,236,245,238,248,243,238,244,238,
+ 245,238,246,238,247,238,249,238,250,242,251,242,252,242,253,242,
+ 0,2,245,0,3,242,0,4,245,0,5,242,0,6,245,0,7,242,0,21,245,0,25,238,
+ 0,27,238,0,29,238,0,49,248,0,78,253,0,79,238,0,82,253,0,83,238,0,
+ 109,242,0,113,242,0,115,242,0,117,242,12,47,250,99,255,118,254,250,
+ 254,251,254,252,254,253,254,0,109,254,0,113,254,0,115,254,0,117,
+ 254,4,105,255,108,255,0,57,255,2,99,255,17,45,255,47,255,102,255,
+ 103,254,112,255,234,255,243,255,244,255,245,255,246,255,249,255,
+ 0,25,255,0,27,255,0,51,252,0,83,255,1,32,25,9,19,102,252,112,255,
+ 233,252,234,252,235,252,236,252,243,255,244,255,245,255,246,255,
+ 247,255,249,255,0,21,252,0,25,252,0,27,252,0,29,252,0,79,255,0,83,
+ 255,2,119,250,7,119,254,120,253,121,255,122,255,254,255,0,1,255,
+ 3,45,246,47,246,21,45,251,47,251,102,254,112,254,233,254,234,254,
+ 235,254,236,254,243,254,244,254,245,254,246,254,247,254,249,254,
+ 0,21,254,0,25,254,0,27,254,0,29,254,0,79,254,0,83,254,31,45,251,
+ 47,251,98,255,102,255,112,254,225,255,226,255,227,255,228,255,229,
+ 255,230,255,233,255,234,255,235,255,236,255,243,254,244,254,245,
+ 254,246,254,247,254,249,254,0,3,255,0,5,255,0,7,255,0,21,255,0,25,
+ 255,0,27,255,0,29,255,0,79,254,0,83,254,10,102,255,233,255,234,255,
+ 235,255,236,255,0,21,255,0,25,255,0,27,255,0,29,255,3,45,251,47,
+ 251,50,68,246,72,247,80,249,82,248,85,248,86,249,87,241,88,240,90,
+ 245,118,252,119,245,120,245,122,245,200,246,211,249,212,249,213,
+ 249,214,249,215,249,217,249,218,249,219,249,220,249,221,249,222,
+ 245,250,252,251,252,252,252,253,252,254,245,0,1,245,0,8,246,0,14,
+ 246,0,32,247,0,36,247,0,78,249,0,82,249,0,100,248,0,102,248,0,108,
+ 249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,
+ 0,117,252,0,122,245,1,32,25,245,50,68,246,72,247,80,249,82,248,85,
+ 248,86,249,87,241,88,240,90,245,118,252,119,245,120,245,122,245,
+ 200,246,211,249,212,249,213,249,214,249,215,249,217,249,218,249,
+ 219,249,220,249,221,249,222,245,250,252,251,252,252,252,253,252,
+ 254,245,0,1,245,0,8,246,0,14,246,0,32,247,0,36,247,0,78,249,0,82,
+ 249,0,100,248,0,102,248,0,108,249,0,109,252,0,112,249,0,113,252,
+ 0,114,249,0,115,252,0,116,249,0,117,252,0,122,245,1,32,25,245,50,
+ 68,246,72,247,80,249,82,248,85,248,86,249,87,241,88,240,90,245,118,
+ 252,119,245,120,245,122,245,200,246,211,249,212,249,213,249,214,
+ 249,215,249,217,249,218,249,219,249,220,249,221,249,222,245,250,
+ 252,251,252,252,252,253,252,254,245,0,1,245,0,8,246,0,14,246,0,32,
+ 247,0,36,247,0,78,249,0,82,249,0,100,248,0,102,248,0,108,249,0,109,
+ 252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,
+ 0,122,245,1,32,25,245,50,68,246,72,247,80,249,82,248,85,248,86,249,
+ 87,241,88,240,90,245,118,252,119,245,120,245,122,245,200,246,211,
+ 249,212,249,213,249,214,249,215,249,217,249,218,249,219,249,220,
+ 249,221,249,222,245,250,252,251,252,252,252,253,252,254,245,0,1,
+ 245,0,8,246,0,14,246,0,32,247,0,36,247,0,78,249,0,82,249,0,100,248,
+ 0,102,248,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,
+ 252,0,116,249,0,117,252,0,122,245,1,32,25,245,50,68,246,72,247,80,
+ 249,82,248,85,248,86,249,87,241,88,240,90,245,118,252,119,245,120,
+ 245,122,245,200,246,211,249,212,249,213,249,214,249,215,249,217,
+ 249,218,249,219,249,220,249,221,249,222,245,250,252,251,252,252,
+ 252,253,252,254,245,0,1,245,0,8,246,0,14,246,0,32,247,0,36,247,0,
+ 78,249,0,82,249,0,100,248,0,102,248,0,108,249,0,109,252,0,112,249,
+ 0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,245,1,32,
+ 25,245,50,68,246,72,247,80,249,82,248,85,248,86,249,87,241,88,240,
+ 90,245,118,252,119,245,120,245,122,245,200,246,211,249,212,249,213,
+ 249,214,249,215,249,217,249,218,249,219,249,220,249,221,249,222,
+ 245,250,252,251,252,252,252,253,252,254,245,0,1,245,0,8,246,0,14,
+ 246,0,32,247,0,36,247,0,78,249,0,82,249,0,100,248,0,102,248,0,108,
+ 249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,
+ 0,117,252,0,122,245,1,32,25,245,11,66,252,193,252,194,252,195,252,
+ 196,252,197,252,198,252,0,2,252,0,4,252,0,6,252,20,66,250,85,250,
+ 87,249,88,249,89,250,90,249,193,250,194,250,195,250,196,250,197,
+ 250,198,250,222,249,0,2,250,0,4,250,0,6,250,0,100,250,0,102,250,
+ 0,122,249,20,66,250,85,250,87,249,88,249,89,250,90,249,193,250,194,
+ 250,195,250,196,250,197,250,198,250,222,249,0,2,250,0,4,250,0,6,
+ 250,0,100,250,0,102,250,0,122,249,20,66,250,85,250,87,249,88,249,
+ 89,250,90,249,193,250,194,250,195,250,196,250,197,250,198,250,222,
+ 249,0,2,250,0,4,250,0,6,250,0,100,250,0,102,250,0,122,249,20,66,
+ 250,85,250,87,249,88,249,89,250,90,249,193,250,194,250,195,250,196,
+ 250,197,250,198,250,222,249,0,2,250,0,4,250,0,6,250,0,100,250,0,
+ 102,250,0,122,249,20,66,250,85,250,87,249,88,249,89,250,90,249,193,
+ 250,194,250,195,250,196,250,197,250,198,250,222,249,0,2,250,0,4,
+ 250,0,6,250,0,100,250,0,102,250,0,122,249,20,66,250,85,250,87,249,
+ 88,249,89,250,90,249,193,250,194,250,195,250,196,250,197,250,198,
+ 250,222,249,0,2,250,0,4,250,0,6,250,0,100,250,0,102,250,0,122,249,
+ 11,66,249,193,249,194,249,195,249,196,249,197,249,198,249,0,2,249,
+ 0,4,249,0,6,249,11,66,249,193,249,194,249,195,249,196,249,197,249,
+ 198,249,0,2,249,0,4,249,0,6,249,11,66,249,193,249,194,249,195,249,
+ 196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,11,66,249,193,249,
+ 194,249,195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,
+ 65,45,242,46,242,47,245,59,242,60,242,66,245,80,253,98,242,102,238,
+ 106,248,112,238,118,242,193,245,194,245,195,245,196,245,197,245,
+ 198,245,211,253,212,253,213,253,214,253,215,253,217,253,225,242,
+ 226,242,227,242,228,242,229,242,230,242,233,245,234,238,235,245,
+ 236,245,238,248,243,238,244,238,245,238,246,238,247,238,249,238,
+ 250,242,251,242,252,242,253,242,0,2,245,0,3,242,0,4,245,0,5,242,
+ 0,6,245,0,7,242,0,21,245,0,25,238,0,27,238,0,29,238,0,49,248,0,78,
+ 253,0,79,238,0,82,253,0,83,238,0,109,242,0,113,242,0,115,242,0,117,
+ 242,4,105,255,108,255,0,57,255,2,99,255,2,99,255,2,99,255,2,99,255,
+ 2,119,250,7,119,254,120,253,121,255,122,255,254,255,0,1,255,7,119,
+ 254,120,253,121,255,122,255,254,255,0,1,255,7,119,254,120,253,121,
+ 255,122,255,254,255,0,1,255,7,119,254,120,253,121,255,122,255,254,
+ 255,0,1,255,7,119,254,120,253,121,255,122,255,254,255,0,1,255,7,
+ 119,254,120,253,121,255,122,255,254,255,0,1,255,3,45,251,47,251,
+ 3,45,251,47,251,50,68,246,72,247,80,249,82,248,85,248,86,249,87,
+ 241,88,240,90,245,118,252,119,245,120,245,122,245,200,246,211,249,
+ 212,249,213,249,214,249,215,249,217,249,218,249,219,249,220,249,
+ 221,249,222,245,250,252,251,252,252,252,253,252,254,245,0,1,245,
+ 0,8,246,0,14,246,0,32,247,0,36,247,0,78,249,0,82,249,0,100,248,0,
+ 102,248,0,108,249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,
+ 252,0,116,249,0,117,252,0,122,245,1,32,25,245,50,68,246,72,247,80,
+ 249,82,248,85,248,86,249,87,241,88,240,90,245,118,252,119,245,120,
+ 245,122,245,200,246,211,249,212,249,213,249,214,249,215,249,217,
+ 249,218,249,219,249,220,249,221,249,222,245,250,252,251,252,252,
+ 252,253,252,254,245,0,1,245,0,8,246,0,14,246,0,32,247,0,36,247,0,
+ 78,249,0,82,249,0,100,248,0,102,248,0,108,249,0,109,252,0,112,249,
+ 0,113,252,0,114,249,0,115,252,0,116,249,0,117,252,0,122,245,1,32,
+ 25,245,50,68,246,72,247,80,249,82,248,85,248,86,249,87,241,88,240,
+ 90,245,118,252,119,245,120,245,122,251,200,246,211,249,212,249,213,
+ 249,214,249,215,249,217,249,218,249,219,249,220,249,221,249,222,
+ 245,250,252,251,252,252,252,253,252,254,251,0,1,251,0,8,246,0,14,
+ 246,0,32,247,0,36,247,0,78,249,0,82,249,0,100,248,0,102,248,0,108,
+ 249,0,109,252,0,112,249,0,113,252,0,114,249,0,115,252,0,116,249,
+ 0,117,252,0,122,245,1,32,25,245,4,105,255,108,255,0,57,255,4,105,
+ 255,108,255,0,57,255,16,66,253,87,249,88,250,90,249,193,253,194,
+ 253,195,253,196,253,197,253,198,253,222,249,0,2,253,0,4,253,0,6,
+ 253,0,122,249,16,66,253,87,249,88,250,90,249,193,253,194,253,195,
+ 253,196,253,197,253,198,253,222,249,0,2,253,0,4,253,0,6,253,0,122,
+ 249,2,99,255,2,99,255,2,99,255,2,99,255,40,80,252,102,253,112,253,
+ 118,254,122,254,211,252,212,252,213,252,214,252,215,252,217,252,
+ 233,253,234,253,235,253,236,253,243,253,244,253,245,253,246,253,
+ 247,253,249,253,250,254,251,254,252,254,253,254,254,254,0,1,254,
+ 0,21,253,0,25,253,0,27,253,0,29,253,0,78,252,0,79,253,0,82,252,0,
+ 83,253,0,109,254,0,113,254,0,115,254,0,117,254,19,102,252,112,255,
+ 233,252,234,252,235,252,236,252,243,255,244,255,245,255,246,255,
+ 247,255,249,255,0,21,252,0,25,252,0,27,252,0,29,252,0,79,255,0,83,
+ 255,13,85,254,87,251,88,251,90,251,122,251,222,251,254,251,0,1,251,
+ 0,100,254,0,102,254,0,122,251,1,32,25,248,13,85,254,87,251,88,251,
+ 90,251,122,251,222,251,254,251,0,1,251,0,100,254,0,102,254,0,122,
+ 251,1,32,25,248,13,85,254,87,251,88,251,90,251,122,251,222,251,254,
+ 251,0,1,251,0,100,254,0,102,254,0,122,251,1,32,25,248,11,66,252,
+ 193,252,194,252,195,252,196,252,197,252,198,252,0,2,252,0,4,252,
+ 0,6,252,2,119,250,11,66,252,193,252,194,252,195,252,196,252,197,
+ 252,198,252,0,2,252,0,4,252,0,6,252,2,119,250,11,66,252,193,252,
+ 194,252,195,252,196,252,197,252,198,252,0,2,252,0,4,252,0,6,252,
+ 2,119,250,20,66,250,85,250,87,249,88,249,89,250,90,249,193,250,194,
+ 250,195,250,196,250,197,250,198,250,222,249,0,2,250,0,4,250,0,6,
+ 250,0,100,250,0,102,250,0,122,249,7,119,254,120,253,121,255,122,
+ 255,254,255,0,1,255,20,66,250,85,250,87,249,88,249,89,250,90,249,
+ 193,250,194,250,195,250,196,250,197,250,198,250,222,249,0,2,250,
+ 0,4,250,0,6,250,0,100,250,0,102,250,0,122,249,7,119,254,120,253,
+ 121,255,122,255,254,255,0,1,255,27,80,250,85,252,86,250,87,254,88,
+ 254,90,254,211,250,212,250,213,250,214,250,215,250,217,250,218,250,
+ 219,250,220,250,221,250,222,254,0,78,250,0,82,250,0,100,252,0,102,
+ 252,0,108,250,0,112,250,0,114,250,0,116,250,0,122,254,3,45,246,47,
+ 246,27,80,250,85,252,86,250,87,254,88,254,90,254,211,250,212,250,
+ 213,250,214,250,215,250,217,250,218,250,219,250,220,250,221,250,
+ 222,254,0,78,250,0,82,250,0,100,252,0,102,252,0,108,250,0,112,250,
+ 0,114,250,0,116,250,0,122,254,3,45,246,47,246,27,80,250,85,252,86,
+ 250,87,254,88,254,90,254,211,250,212,250,213,250,214,250,215,250,
+ 217,250,218,250,219,250,220,250,221,250,222,254,0,78,250,0,82,250,
+ 0,100,252,0,102,252,0,108,250,0,112,250,0,114,250,0,116,250,0,122,
+ 254,3,45,246,47,246,73,45,242,46,242,47,242,59,245,60,245,66,248,
+ 80,254,98,242,102,242,106,251,112,241,115,251,118,251,120,251,122,
+ 251,193,248,194,248,195,248,196,248,197,248,198,248,211,254,212,
+ 254,213,254,214,254,215,254,217,254,225,242,226,242,227,242,228,
+ 242,229,242,230,242,233,248,234,242,235,242,236,248,238,251,243,
+ 241,244,241,245,241,246,241,247,241,249,241,250,251,251,251,252,
+ 251,253,251,254,251,0,1,251,0,2,248,0,3,242,0,4,248,0,5,242,0,6,
+ 248,0,7,242,0,21,248,0,25,242,0,27,242,0,29,242,0,49,251,0,78,254,
+ 0,79,241,0,82,254,0,83,241,0,87,251,0,89,251,0,91,251,0,109,251,
+ 0,113,251,0,115,251,0,117,251,73,45,242,46,242,47,242,59,245,60,
+ 245,66,248,80,254,98,242,102,242,106,251,112,241,115,251,118,251,
+ 120,251,122,251,193,248,194,248,195,248,196,248,197,248,198,248,
+ 211,254,212,254,213,254,214,254,215,254,217,254,225,242,226,242,
+ 227,242,228,242,229,242,230,242,233,248,234,242,235,242,236,248,
+ 238,251,243,241,244,241,245,241,246,241,247,241,249,241,250,251,
+ 251,251,252,251,253,251,254,251,0,1,251,0,2,248,0,3,242,0,4,248,
+ 0,5,242,0,6,248,0,7,242,0,21,248,0,25,242,0,27,242,0,29,242,0,49,
+ 251,0,78,254,0,79,241,0,82,254,0,83,241,0,87,251,0,89,251,0,91,251,
+ 0,109,251,0,113,251,0,115,251,0,117,251,11,66,249,193,249,194,249,
+ 195,249,196,249,197,249,198,249,0,2,249,0,4,249,0,6,249,11,66,249,
+ 193,249,194,249,195,249,196,249,197,249,198,249,0,2,249,0,4,249,
+ 0,6,249,11,66,249,193,249,194,249,195,249,196,249,197,249,198,249,
+ 0,2,249,0,4,249,0,6,249,11,66,249,193,249,194,249,195,249,196,249,
+ 197,249,198,249,0,2,249,0,4,249,0,6,249,65,45,242,46,242,47,245,
+ 59,242,60,242,66,245,80,253,98,242,102,238,106,248,112,238,118,242,
+ 193,245,194,245,195,245,196,245,197,245,198,245,211,253,212,253,
+ 213,253,214,253,215,253,217,253,225,242,226,242,227,242,228,242,
+ 229,242,230,242,233,245,234,238,235,245,236,245,238,248,243,238,
+ 244,238,245,238,246,238,247,238,249,238,250,242,251,242,252,242,
+ 253,242,0,2,245,0,3,242,0,4,245,0,5,242,0,6,245,0,7,242,0,21,245,
+ 0,25,238,0,27,238,0,29,238,0,49,248,0,78,253,0,79,238,0,82,253,0,
+ 83,238,0,109,242,0,113,242,0,115,242,0,117,242,2,1,32,24,245,17,
+ 33,245,101,254,115,254,116,245,117,251,119,254,0,19,254,0,87,254,
+ 0,89,254,0,91,254,0,93,245,0,97,245,0,99,245,0,101,251,1,2,25,245,
+ 1,32,25,245
+};
+static afm_cuint16 afm_Times_BoldItalic_highchars_index[] = { /* 220 */
+ 161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+ 178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+ 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+ 210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+ 226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+ 242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+ 258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+ 280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+ 313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+ 336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+ 354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+ 380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+ 8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+ 8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+ 64257,64258
+};
+static afm_cunicode afm_Times_BoldItalic_ligatures[] = { /* 3 */
+ 102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Times-Italic */
+/* FullName: Times Italic */
+/* FamilyName: Times */
+static afm_cuint8 afm_Times_Italic_widths[] = { /* 315 */
+ 42,56,70,83,83,139,130,36,56,56,83,113,42,56,42,46,83,83,83,83,83,
+ 83,83,83,83,83,56,56,113,113,113,83,153,102,102,111,120,102,102,
+ 120,120,56,74,111,93,139,111,120,102,120,102,83,93,120,102,139,102,
+ 93,93,65,46,65,70,83,56,83,83,74,83,74,46,83,83,46,46,74,46,120,
+ 83,83,83,83,65,65,46,83,74,111,74,74,65,67,46,67,90,65,83,83,83,
+ 83,46,83,56,127,46,83,113,127,56,67,113,50,50,56,83,87,42,56,50,
+ 52,83,125,125,125,83,102,102,102,102,102,102,148,111,102,102,102,
+ 102,56,56,56,56,120,111,120,120,120,120,120,113,120,120,120,120,
+ 120,93,102,83,83,83,83,83,83,83,111,74,74,74,74,74,46,46,46,46,83,
+ 83,83,83,83,83,83,113,83,83,83,83,83,74,83,74,102,83,102,83,102,
+ 83,111,74,111,74,120,91,120,83,102,74,102,74,102,74,102,74,120,83,
+ 120,83,56,46,56,46,56,46,111,74,93,46,93,46,102,50,93,46,111,83,
+ 111,83,111,83,120,83,120,83,157,111,102,65,102,65,102,65,83,65,83,
+ 65,83,65,93,46,93,50,120,83,120,83,120,83,120,83,93,93,65,93,65,
+ 93,65,83,83,65,56,56,56,56,56,56,56,56,83,148,56,56,56,93,93,93,
+ 83,83,58,148,167,56,56,28,83,163,79,102,100,113,76,92,92,92,79,42,
+ 83,83
+};
+static afm_sint16 afm_Times_Italic_kerning_index[] = { /* 315 */
+ 1,0,0,0,0,0,0,0,0,0,0,0,44,0,53,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,62,181,0,227,0,262,0,0,0,382,497,589,0,620,644,689,782,805,0,
+ 859,1027,1055,1212,0,1367,0,0,0,0,0,0,0,1524,1533,1558,0,1566,1592,
+ 1611,0,0,0,1646,0,0,1696,1699,0,0,1710,0,0,0,1822,1827,0,1832,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,1837,1956,2075,2194,2313,2432,0,0,0,0,0,0,0,0,0,0,0,2551,2575,
+ 2620,2665,2710,2755,0,2800,2845,2873,2901,2929,2957,0,0,3114,3123,
+ 3132,3141,3150,3159,0,3168,3176,3202,3228,3254,0,0,0,0,0,3280,3283,
+ 3294,3305,3316,3327,0,3338,0,0,0,0,3349,0,3354,3359,3478,3487,3606,
+ 3615,3734,0,3743,0,3751,3759,0,3794,0,0,3829,0,3855,0,3881,0,3907,
+ 0,3933,0,3968,0,0,0,0,0,0,4003,4095,4145,0,4176,0,0,0,4207,0,4238,
+ 4262,4265,4289,4292,4316,4319,4364,4375,4420,0,0,4431,4485,4597,
+ 4651,4763,4817,0,0,0,0,0,0,4929,0,5097,0,5265,0,5293,0,5321,0,5349,
+ 0,5377,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5534,5539,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+};
+static afm_cuint8 afm_Times_Italic_kerning_data[] = { /* 5584 */
+ 42,19,66,254,85,254,87,251,88,250,90,244,193,254,194,254,195,254,
+ 196,254,197,254,198,254,222,244,0,2,254,0,4,254,0,6,254,0,100,254,
+ 0,102,254,0,122,244,3,1,32,25,234,1,32,29,234,3,1,32,25,234,1,32,
+ 29,234,50,68,252,72,251,80,250,82,250,85,251,86,249,87,239,88,241,
+ 90,248,118,254,119,248,120,248,122,248,200,252,211,250,212,250,213,
+ 250,214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,
+ 248,250,254,251,254,252,254,253,254,254,248,0,1,248,0,8,252,0,14,
+ 252,0,32,251,0,36,251,0,78,250,0,82,250,0,100,251,0,102,251,0,108,
+ 249,0,109,254,0,112,249,0,113,254,0,114,249,0,115,254,0,116,249,
+ 0,117,254,0,122,248,1,32,25,251,20,66,253,86,255,193,253,194,253,
+ 195,253,196,253,197,253,198,253,218,255,219,255,220,255,221,255,
+ 0,2,253,0,4,253,0,6,253,0,108,255,0,112,255,0,114,255,0,116,255,
+ 16,66,251,87,250,88,250,90,250,193,251,194,251,195,251,196,251,197,
+ 251,198,251,222,250,0,2,251,0,4,251,0,6,251,0,122,250,52,45,234,
+ 47,234,66,238,98,244,102,244,106,249,112,239,115,248,193,238,194,
+ 238,195,238,196,238,197,238,198,238,225,244,226,244,227,244,228,
+ 244,229,244,230,244,233,244,234,244,235,244,236,244,237,249,238,
+ 249,239,249,240,249,243,239,244,239,245,239,246,239,247,239,249,
+ 239,0,2,238,0,3,244,0,4,238,0,5,244,0,6,238,0,7,244,0,21,244,0,25,
+ 244,0,27,244,0,29,244,0,45,249,0,49,249,0,79,239,0,83,239,0,87,248,
+ 0,89,248,0,91,248,50,45,253,47,253,66,250,98,251,102,253,112,253,
+ 118,251,193,250,194,250,195,250,196,250,197,250,198,250,225,251,
+ 226,251,227,251,228,251,229,251,230,251,233,253,234,253,235,253,
+ 236,253,243,253,244,253,245,253,246,253,247,253,249,253,250,251,
+ 251,251,252,251,253,251,0,2,250,0,3,251,0,4,250,0,5,251,0,6,250,
+ 0,7,251,0,21,253,0,25,253,0,27,253,0,29,253,0,79,253,0,83,253,0,
+ 109,251,0,113,251,0,115,251,0,117,251,40,80,249,102,251,112,250,
+ 118,250,122,250,211,249,212,249,213,249,214,249,215,249,217,249,
+ 233,251,234,251,235,251,236,251,243,250,244,250,245,250,246,250,
+ 247,250,249,250,250,250,251,250,252,250,253,250,254,250,0,1,250,
+ 0,21,251,0,25,251,0,27,251,0,29,251,0,78,249,0,79,250,0,82,249,0,
+ 83,250,0,109,250,0,113,250,0,115,250,0,117,250,13,85,254,87,248,
+ 88,248,90,254,122,252,222,254,254,252,0,1,252,0,100,254,0,102,254,
+ 0,122,254,1,32,25,251,11,66,252,193,252,194,252,195,252,196,252,
+ 197,252,198,252,0,2,252,0,4,252,0,6,252,20,66,248,85,250,87,249,
+ 88,249,89,250,90,249,193,248,194,248,195,248,196,248,197,248,198,
+ 248,222,249,0,2,248,0,4,248,0,6,248,0,100,250,0,102,250,0,122,249,
+ 41,45,234,47,234,66,242,98,244,102,244,112,244,193,242,194,242,195,
+ 242,196,242,197,242,198,242,225,244,226,244,227,244,228,244,229,
+ 244,230,244,233,244,234,244,235,244,236,244,243,244,244,244,245,
+ 244,246,244,247,244,249,244,0,2,242,0,3,244,0,4,242,0,5,244,0,6,
+ 242,0,7,244,0,21,244,0,25,244,0,27,244,0,29,244,0,79,244,0,83,244,
+ 10,86,255,218,255,219,255,220,255,221,255,0,108,255,0,112,255,0,
+ 114,255,0,116,255,24,80,250,86,250,87,254,88,254,90,254,211,250,
+ 212,250,213,250,214,250,215,250,217,250,218,250,219,250,220,250,
+ 221,250,222,254,0,78,250,0,82,250,0,108,250,0,112,250,0,114,250,
+ 0,116,250,0,122,254,73,45,245,46,245,47,245,59,248,60,246,66,249,
+ 80,254,98,242,102,242,106,248,112,242,115,248,118,248,120,245,122,
+ 245,193,249,194,249,195,249,196,249,197,249,198,249,211,254,212,
+ 254,213,254,214,254,215,254,217,254,225,242,226,242,227,242,228,
+ 242,229,242,230,242,233,248,234,242,235,248,236,248,238,248,243,
+ 242,244,242,245,242,246,242,247,242,249,242,250,248,251,248,252,
+ 248,253,248,254,245,0,1,251,0,2,249,0,3,242,0,4,249,0,5,242,0,6,
+ 249,0,7,242,0,21,248,0,25,242,0,27,242,0,29,242,0,49,248,0,78,254,
+ 0,79,242,0,82,254,0,83,242,0,87,248,0,89,248,0,91,248,0,109,248,
+ 0,113,248,0,115,248,0,117,248,13,45,253,47,253,66,250,193,250,194,
+ 250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,69,45,
+ 235,46,248,47,235,59,246,60,245,66,247,80,252,98,238,102,238,106,
+ 245,112,238,118,245,193,247,194,247,195,247,196,247,197,247,198,
+ 247,211,252,212,252,213,252,214,252,215,252,217,252,225,238,226,
+ 238,227,238,228,238,229,238,230,238,233,245,234,238,235,238,236,
+ 245,237,251,238,245,239,251,240,251,243,238,244,238,245,238,246,
+ 238,247,238,249,238,250,245,251,245,252,245,253,245,0,2,247,0,3,
+ 238,0,4,247,0,5,238,0,6,247,0,7,238,0,21,245,0,25,238,0,27,238,0,
+ 29,238,0,45,251,0,49,245,0,78,252,0,79,238,0,82,252,0,83,238,0,109,
+ 245,0,113,245,0,115,245,0,117,245,68,45,242,46,251,47,242,59,246,
+ 60,246,66,247,80,253,98,242,102,242,106,248,112,242,118,248,122,
+ 245,193,247,194,247,195,247,196,247,197,247,198,247,211,253,212,
+ 253,213,253,214,253,215,253,217,253,225,242,226,242,227,242,228,
+ 242,229,242,230,242,233,248,234,242,235,242,236,248,238,248,243,
+ 242,244,242,245,242,246,242,247,242,249,242,250,248,251,248,252,
+ 248,253,248,254,245,0,1,245,0,2,247,0,3,242,0,4,247,0,5,242,0,6,
+ 247,0,7,242,0,21,248,0,25,242,0,27,242,0,29,242,0,49,248,0,78,253,
+ 0,79,242,0,82,253,0,83,242,0,109,248,0,113,248,0,115,248,0,117,248,
+ 69,45,242,46,245,47,242,59,246,60,246,66,249,80,254,98,242,102,242,
+ 106,245,112,242,118,242,193,249,194,249,195,249,196,249,197,249,
+ 198,249,211,254,212,254,213,254,214,254,215,254,217,254,225,242,
+ 226,242,227,242,228,242,229,242,230,242,233,248,234,242,235,242,
+ 236,248,237,251,238,245,239,251,240,251,243,242,244,242,245,242,
+ 246,242,247,242,249,242,250,242,251,242,252,242,253,242,0,2,249,
+ 0,3,242,0,4,249,0,5,242,0,6,249,0,7,242,0,21,248,0,25,242,0,27,242,
+ 0,29,242,0,45,251,0,49,245,0,78,254,0,79,242,0,82,254,0,83,242,0,
+ 109,242,0,113,242,0,115,242,0,117,242,4,104,255,0,33,255,0,37,255,
+ 11,47,250,118,254,250,254,251,254,252,254,253,254,0,109,254,0,113,
+ 254,0,115,254,0,117,254,4,105,254,108,254,0,57,254,12,45,255,47,
+ 254,104,250,119,254,120,254,121,254,122,252,254,252,0,1,252,0,33,
+ 250,0,37,250,8,45,255,47,254,103,254,106,254,0,49,254,0,51,247,1,
+ 32,25,15,15,45,255,47,254,102,255,104,255,233,255,234,255,235,255,
+ 236,255,0,21,255,0,25,255,0,27,255,0,29,255,0,33,255,0,37,255,22,
+ 102,255,112,255,122,255,233,255,234,255,235,255,236,255,243,255,
+ 244,255,245,255,246,255,247,255,249,255,254,255,0,1,255,0,21,255,
+ 0,25,255,0,27,255,0,29,255,0,79,255,0,83,255,2,119,250,5,104,255,
+ 119,255,0,33,255,0,37,255,47,45,238,46,254,47,238,98,254,100,251,
+ 101,251,102,251,104,251,112,249,114,251,116,255,225,254,226,254,
+ 227,254,228,254,229,254,230,254,232,251,233,251,234,251,235,251,
+ 236,251,243,249,244,249,245,249,246,249,247,249,249,249,0,3,254,
+ 0,5,254,0,7,254,0,9,251,0,15,251,0,19,251,0,21,251,0,25,251,0,27,
+ 251,0,29,251,0,33,251,0,37,251,0,79,249,0,83,249,0,93,255,0,97,255,
+ 0,99,255,1,2,25,255,3,45,245,47,245,3,45,245,47,245,3,45,248,47,
+ 248,50,68,252,72,251,80,250,82,250,85,251,86,249,87,239,88,241,90,
+ 248,118,254,119,248,120,248,122,248,200,252,211,250,212,250,213,
+ 250,214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,
+ 248,250,254,251,254,252,254,253,254,254,248,0,1,248,0,8,252,0,14,
+ 252,0,32,251,0,36,251,0,78,250,0,82,250,0,100,251,0,102,251,0,108,
+ 249,0,109,254,0,112,249,0,113,254,0,114,249,0,115,254,0,116,249,
+ 0,117,254,0,122,248,1,32,25,251,50,68,252,72,251,80,250,82,250,85,
+ 251,86,249,87,239,88,241,90,248,118,254,119,248,120,248,122,248,
+ 200,252,211,250,212,250,213,250,214,250,215,250,217,250,218,249,
+ 219,249,220,249,221,249,222,248,250,254,251,254,252,254,253,254,
+ 254,248,0,1,248,0,8,252,0,14,252,0,32,251,0,36,251,0,78,250,0,82,
+ 250,0,100,251,0,102,251,0,108,249,0,109,254,0,112,249,0,113,254,
+ 0,114,249,0,115,254,0,116,249,0,117,254,0,122,248,1,32,25,251,50,
+ 68,252,72,251,80,250,82,250,85,251,86,249,87,239,88,241,90,248,118,
+ 254,119,248,120,248,122,248,200,252,211,250,212,250,213,250,214,
+ 250,215,250,217,250,218,249,219,249,220,249,221,249,222,248,250,
+ 254,251,254,252,254,253,254,254,248,0,1,248,0,8,252,0,14,252,0,32,
+ 251,0,36,251,0,78,250,0,82,250,0,100,251,0,102,251,0,108,249,0,109,
+ 254,0,112,249,0,113,254,0,114,249,0,115,254,0,116,249,0,117,254,
+ 0,122,248,1,32,25,251,50,68,252,72,251,80,250,82,250,85,251,86,249,
+ 87,239,88,241,90,248,118,254,119,248,120,248,122,248,200,252,211,
+ 250,212,250,213,250,214,250,215,250,217,250,218,249,219,249,220,
+ 249,221,249,222,248,250,254,251,254,252,254,253,254,254,248,0,1,
+ 248,0,8,252,0,14,252,0,32,251,0,36,251,0,78,250,0,82,250,0,100,251,
+ 0,102,251,0,108,249,0,109,254,0,112,249,0,113,254,0,114,249,0,115,
+ 254,0,116,249,0,117,254,0,122,248,1,32,25,251,50,68,252,72,251,80,
+ 250,82,250,85,251,86,249,87,239,88,241,90,248,118,254,119,248,120,
+ 248,122,248,200,252,211,250,212,250,213,250,214,250,215,250,217,
+ 250,218,249,219,249,220,249,221,249,222,248,250,254,251,254,252,
+ 254,253,254,254,248,0,1,248,0,8,252,0,14,252,0,32,251,0,36,251,0,
+ 78,250,0,82,250,0,100,251,0,102,251,0,108,249,0,109,254,0,112,249,
+ 0,113,254,0,114,249,0,115,254,0,116,249,0,117,254,0,122,248,1,32,
+ 25,251,50,68,252,72,251,80,250,82,250,85,251,86,249,87,239,88,241,
+ 90,248,118,254,119,248,120,248,122,248,200,252,211,250,212,250,213,
+ 250,214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,
+ 248,250,254,251,254,252,254,253,254,254,248,0,1,248,0,8,252,0,14,
+ 252,0,32,251,0,36,251,0,78,250,0,82,250,0,100,251,0,102,251,0,108,
+ 249,0,109,254,0,112,249,0,113,254,0,114,249,0,115,254,0,116,249,
+ 0,117,254,0,122,248,1,32,25,251,11,66,252,193,252,194,252,195,252,
+ 196,252,197,252,198,252,0,2,252,0,4,252,0,6,252,20,66,248,85,250,
+ 87,249,88,249,89,250,90,249,193,248,194,248,195,248,196,248,197,
+ 248,198,248,222,249,0,2,248,0,4,248,0,6,248,0,100,250,0,102,250,
+ 0,122,249,20,66,248,85,250,87,249,88,249,89,250,90,249,193,248,194,
+ 248,195,248,196,248,197,248,198,248,222,249,0,2,248,0,4,248,0,6,
+ 248,0,100,250,0,102,250,0,122,249,20,66,248,85,250,87,249,88,249,
+ 89,250,90,249,193,248,194,248,195,248,196,248,197,248,198,248,222,
+ 249,0,2,248,0,4,248,0,6,248,0,100,250,0,102,250,0,122,249,20,66,
+ 248,85,250,87,249,88,249,89,250,90,249,193,248,194,248,195,248,196,
+ 248,197,248,198,248,222,249,0,2,248,0,4,248,0,6,248,0,100,250,0,
+ 102,250,0,122,249,20,66,248,85,250,87,249,88,249,89,250,90,249,193,
+ 248,194,248,195,248,196,248,197,248,198,248,222,249,0,2,248,0,4,
+ 248,0,6,248,0,100,250,0,102,250,0,122,249,20,66,248,85,250,87,249,
+ 88,249,89,250,90,249,193,248,194,248,195,248,196,248,197,248,198,
+ 248,222,249,0,2,248,0,4,248,0,6,248,0,100,250,0,102,250,0,122,249,
+ 13,45,253,47,253,66,250,193,250,194,250,195,250,196,250,197,250,
+ 198,250,0,2,250,0,4,250,0,6,250,13,45,253,47,253,66,250,193,250,
+ 194,250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,
+ 13,45,253,47,253,66,250,193,250,194,250,195,250,196,250,197,250,
+ 198,250,0,2,250,0,4,250,0,6,250,13,45,253,47,253,66,250,193,250,
+ 194,250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,
+ 69,45,242,46,245,47,242,59,246,60,246,66,249,80,254,98,242,102,242,
+ 106,245,112,242,118,242,193,249,194,249,195,249,196,249,197,249,
+ 198,249,211,254,212,254,213,254,214,254,215,254,217,254,225,242,
+ 226,242,227,242,228,242,229,242,230,242,233,248,234,242,235,242,
+ 236,248,237,251,238,245,239,251,240,251,243,242,244,242,245,242,
+ 246,242,247,242,249,242,250,242,251,242,252,242,253,242,0,2,249,
+ 0,3,242,0,4,249,0,5,242,0,6,249,0,7,242,0,21,248,0,25,242,0,27,242,
+ 0,29,242,0,45,251,0,49,245,0,78,254,0,79,242,0,82,254,0,83,242,0,
+ 109,242,0,113,242,0,115,242,0,117,242,4,104,255,0,33,255,0,37,255,
+ 4,104,255,0,33,255,0,37,255,4,104,255,0,33,255,0,37,255,4,104,255,
+ 0,33,255,0,37,255,4,104,255,0,33,255,0,37,255,4,104,255,0,33,255,
+ 0,37,255,4,105,254,108,254,0,57,254,12,45,255,47,254,104,250,119,
+ 254,120,254,121,254,122,252,254,252,0,1,252,0,33,250,0,37,250,12,
+ 45,255,47,254,104,250,119,254,120,254,121,254,122,252,254,252,0,
+ 1,252,0,33,250,0,37,250,12,45,255,47,254,104,250,119,254,120,254,
+ 121,254,122,252,254,252,0,1,252,0,33,250,0,37,250,12,45,255,47,254,
+ 104,250,119,254,120,254,121,254,122,252,254,252,0,1,252,0,33,250,
+ 0,37,250,2,119,250,5,104,255,119,255,0,33,255,0,37,255,5,104,255,
+ 119,255,0,33,255,0,37,255,5,104,255,119,255,0,33,255,0,37,255,5,
+ 104,255,119,255,0,33,255,0,37,255,5,104,255,119,255,0,33,255,0,37,
+ 255,5,104,255,119,255,0,33,255,0,37,255,3,45,248,47,248,3,45,248,
+ 47,248,50,68,252,72,251,80,250,82,250,85,251,86,249,87,239,88,241,
+ 90,248,118,254,119,248,120,248,122,248,200,252,211,250,212,250,213,
+ 250,214,250,215,250,217,250,218,249,219,249,220,249,221,249,222,
+ 248,250,254,251,254,252,254,253,254,254,248,0,1,248,0,8,252,0,14,
+ 252,0,32,251,0,36,251,0,78,250,0,82,250,0,100,251,0,102,251,0,108,
+ 249,0,109,254,0,112,249,0,113,254,0,114,249,0,115,254,0,116,249,
+ 0,117,254,0,122,248,1,32,25,251,4,104,255,0,33,255,0,37,255,50,68,
+ 252,72,251,80,250,82,250,85,251,86,249,87,239,88,241,90,248,118,
+ 254,119,248,120,248,122,248,200,252,211,250,212,250,213,250,214,
+ 250,215,250,217,250,218,249,219,249,220,249,221,249,222,248,250,
+ 254,251,254,252,254,253,254,254,248,0,1,248,0,8,252,0,14,252,0,32,
+ 251,0,36,251,0,78,250,0,82,250,0,100,251,0,102,251,0,108,249,0,109,
+ 254,0,112,249,0,113,254,0,114,249,0,115,254,0,116,249,0,117,254,
+ 0,122,248,1,32,25,251,4,104,255,0,33,255,0,37,255,50,68,252,72,251,
+ 80,250,82,250,85,251,86,249,87,239,88,241,90,248,118,254,119,248,
+ 120,248,122,248,200,252,211,250,212,250,213,250,214,250,215,250,
+ 217,250,218,249,219,249,220,249,221,249,222,248,250,254,251,254,
+ 252,254,253,254,254,248,0,1,248,0,8,252,0,14,252,0,32,251,0,36,251,
+ 0,78,250,0,82,250,0,100,251,0,102,251,0,108,249,0,109,254,0,112,
+ 249,0,113,254,0,114,249,0,115,254,0,116,249,0,117,254,0,122,248,
+ 1,32,25,251,4,104,255,0,33,255,0,37,255,4,105,254,108,254,0,57,254,
+ 4,105,254,108,254,0,57,254,16,66,251,87,250,88,250,90,250,193,251,
+ 194,251,195,251,196,251,197,251,198,251,222,250,0,2,251,0,4,251,
+ 0,6,251,0,122,250,16,66,251,87,250,88,250,90,250,193,251,194,251,
+ 195,251,196,251,197,251,198,251,222,250,0,2,251,0,4,251,0,6,251,
+ 0,122,250,12,45,255,47,254,104,250,119,254,120,254,121,254,122,252,
+ 254,252,0,1,252,0,33,250,0,37,250,12,45,255,47,254,104,250,119,254,
+ 120,254,121,254,122,252,254,252,0,1,252,0,33,250,0,37,250,12,45,
+ 255,47,254,104,250,119,254,120,254,121,254,122,252,254,252,0,1,252,
+ 0,33,250,0,37,250,12,45,255,47,254,104,250,119,254,120,254,121,254,
+ 122,252,254,252,0,1,252,0,33,250,0,37,250,15,45,255,47,254,102,255,
+ 104,255,233,255,234,255,235,255,236,255,0,21,255,0,25,255,0,27,255,
+ 0,29,255,0,33,255,0,37,255,15,45,255,47,254,102,255,104,255,233,
+ 255,234,255,235,255,236,255,0,21,255,0,25,255,0,27,255,0,29,255,
+ 0,33,255,0,37,255,40,80,249,102,251,112,250,118,250,122,250,211,
+ 249,212,249,213,249,214,249,215,249,217,249,233,251,234,251,235,
+ 251,236,251,243,250,244,250,245,250,246,250,247,250,249,250,250,
+ 250,251,250,252,250,253,250,254,250,0,1,250,0,21,251,0,25,251,0,
+ 27,251,0,29,251,0,78,249,0,79,250,0,82,249,0,83,250,0,109,250,0,
+ 113,250,0,115,250,0,117,250,22,102,255,112,255,122,255,233,255,234,
+ 255,235,255,236,255,243,255,244,255,245,255,246,255,247,255,249,
+ 255,254,255,0,1,255,0,21,255,0,25,255,0,27,255,0,29,255,0,79,255,
+ 0,83,255,13,85,254,87,248,88,248,90,254,122,252,222,254,254,252,
+ 0,1,252,0,100,254,0,102,254,0,122,254,1,32,25,251,13,85,254,87,248,
+ 88,248,90,254,122,252,222,254,254,252,0,1,252,0,100,254,0,102,254,
+ 0,122,254,1,32,25,251,13,85,254,87,248,88,248,90,254,122,252,222,
+ 254,254,252,0,1,252,0,100,254,0,102,254,0,122,254,1,32,25,251,11,
+ 66,252,193,252,194,252,195,252,196,252,197,252,198,252,0,2,252,0,
+ 4,252,0,6,252,2,119,250,11,66,252,193,252,194,252,195,252,196,252,
+ 197,252,198,252,0,2,252,0,4,252,0,6,252,2,119,250,11,66,252,193,
+ 252,194,252,195,252,196,252,197,252,198,252,0,2,252,0,4,252,0,6,
+ 252,2,119,250,20,66,248,85,250,87,249,88,249,89,250,90,249,193,248,
+ 194,248,195,248,196,248,197,248,198,248,222,249,0,2,248,0,4,248,
+ 0,6,248,0,100,250,0,102,250,0,122,249,5,104,255,119,255,0,33,255,
+ 0,37,255,20,66,248,85,250,87,249,88,249,89,250,90,249,193,248,194,
+ 248,195,248,196,248,197,248,198,248,222,249,0,2,248,0,4,248,0,6,
+ 248,0,100,250,0,102,250,0,122,249,5,104,255,119,255,0,33,255,0,37,
+ 255,24,80,250,86,250,87,254,88,254,90,254,211,250,212,250,213,250,
+ 214,250,215,250,217,250,218,250,219,250,220,250,221,250,222,254,
+ 0,78,250,0,82,250,0,108,250,0,112,250,0,114,250,0,116,250,0,122,
+ 254,47,45,238,46,254,47,238,98,254,100,251,101,251,102,251,104,251,
+ 112,249,114,251,116,255,225,254,226,254,227,254,228,254,229,254,
+ 230,254,232,251,233,251,234,251,235,251,236,251,243,249,244,249,
+ 245,249,246,249,247,249,249,249,0,3,254,0,5,254,0,7,254,0,9,251,
+ 0,15,251,0,19,251,0,21,251,0,25,251,0,27,251,0,29,251,0,33,251,0,
+ 37,251,0,79,249,0,83,249,0,93,255,0,97,255,0,99,255,1,2,25,255,24,
+ 80,250,86,250,87,254,88,254,90,254,211,250,212,250,213,250,214,250,
+ 215,250,217,250,218,250,219,250,220,250,221,250,222,254,0,78,250,
+ 0,82,250,0,108,250,0,112,250,0,114,250,0,116,250,0,122,254,47,45,
+ 238,46,254,47,238,98,254,100,251,101,251,102,251,104,251,112,249,
+ 114,251,116,255,225,254,226,254,227,254,228,254,229,254,230,254,
+ 232,251,233,251,234,251,235,251,236,251,243,249,244,249,245,249,
+ 246,249,247,249,249,249,0,3,254,0,5,254,0,7,254,0,9,251,0,15,251,
+ 0,19,251,0,21,251,0,25,251,0,27,251,0,29,251,0,33,251,0,37,251,0,
+ 79,249,0,83,249,0,93,255,0,97,255,0,99,255,1,2,25,255,24,80,250,
+ 86,250,87,254,88,254,90,254,211,250,212,250,213,250,214,250,215,
+ 250,217,250,218,250,219,250,220,250,221,250,222,254,0,78,250,0,82,
+ 250,0,108,250,0,112,250,0,114,250,0,116,250,0,122,254,47,45,238,
+ 46,254,47,238,98,254,100,251,101,251,102,251,104,251,112,249,114,
+ 251,116,255,225,254,226,254,227,254,228,254,229,254,230,254,232,
+ 251,233,251,234,251,235,251,236,251,243,249,244,249,245,249,246,
+ 249,247,249,249,249,0,3,254,0,5,254,0,7,254,0,9,251,0,15,251,0,19,
+ 251,0,21,251,0,25,251,0,27,251,0,29,251,0,33,251,0,37,251,0,79,249,
+ 0,83,249,0,93,255,0,97,255,0,99,255,1,2,25,255,73,45,245,46,245,
+ 47,245,59,248,60,246,66,249,80,254,98,242,102,242,106,248,112,242,
+ 115,248,118,248,120,245,122,245,193,249,194,249,195,249,196,249,
+ 197,249,198,249,211,254,212,254,213,254,214,254,215,254,217,254,
+ 225,242,226,242,227,242,228,242,229,242,230,242,233,248,234,242,
+ 235,248,236,248,238,248,243,242,244,242,245,242,246,242,247,242,
+ 249,242,250,248,251,248,252,248,253,248,254,245,0,1,251,0,2,249,
+ 0,3,242,0,4,249,0,5,242,0,6,249,0,7,242,0,21,248,0,25,242,0,27,242,
+ 0,29,242,0,49,248,0,78,254,0,79,242,0,82,254,0,83,242,0,87,248,0,
+ 89,248,0,91,248,0,109,248,0,113,248,0,115,248,0,117,248,73,45,245,
+ 46,245,47,245,59,248,60,246,66,249,80,254,98,242,102,242,106,248,
+ 112,242,115,248,118,248,120,245,122,245,193,249,194,249,195,249,
+ 196,249,197,249,198,249,211,254,212,254,213,254,214,254,215,254,
+ 217,254,225,242,226,242,227,242,228,242,229,242,230,242,233,248,
+ 234,242,235,248,236,248,238,248,243,242,244,242,245,242,246,242,
+ 247,242,249,242,250,248,251,248,252,248,253,248,254,245,0,1,251,
+ 0,2,249,0,3,242,0,4,249,0,5,242,0,6,249,0,7,242,0,21,248,0,25,242,
+ 0,27,242,0,29,242,0,49,248,0,78,254,0,79,242,0,82,254,0,83,242,0,
+ 87,248,0,89,248,0,91,248,0,109,248,0,113,248,0,115,248,0,117,248,
+ 13,45,253,47,253,66,250,193,250,194,250,195,250,196,250,197,250,
+ 198,250,0,2,250,0,4,250,0,6,250,13,45,253,47,253,66,250,193,250,
+ 194,250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,
+ 13,45,253,47,253,66,250,193,250,194,250,195,250,196,250,197,250,
+ 198,250,0,2,250,0,4,250,0,6,250,13,45,253,47,253,66,250,193,250,
+ 194,250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,
+ 69,45,242,46,245,47,242,59,246,60,246,66,249,80,254,98,242,102,242,
+ 106,245,112,242,118,242,193,249,194,249,195,249,196,249,197,249,
+ 198,249,211,254,212,254,213,254,214,254,215,254,217,254,225,242,
+ 226,242,227,242,228,242,229,242,230,242,233,248,234,242,235,242,
+ 236,248,237,251,238,245,239,251,240,251,243,242,244,242,245,242,
+ 246,242,247,242,249,242,250,242,251,242,252,242,253,242,0,2,249,
+ 0,3,242,0,4,249,0,5,242,0,6,249,0,7,242,0,21,248,0,25,242,0,27,242,
+ 0,29,242,0,45,251,0,49,245,0,78,254,0,79,242,0,82,254,0,83,242,0,
+ 109,242,0,113,242,0,115,242,0,117,242,2,1,32,24,238,17,33,238,101,
+ 253,115,253,116,250,117,252,119,255,0,19,253,0,87,253,0,89,253,0,
+ 91,253,0,93,250,0,97,250,0,99,250,0,101,252,1,2,25,250,1,32,25,238
+};
+static afm_cuint16 afm_Times_Italic_highchars_index[] = { /* 220 */
+ 161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+ 178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+ 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+ 210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+ 226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+ 242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+ 258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+ 280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+ 313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+ 336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+ 354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+ 380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+ 8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+ 8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+ 64257,64258
+};
+static afm_cunicode afm_Times_Italic_ligatures[] = { /* 3 */
+ 102,105,64257
+};
+
+
+/* ------------------------------------------------------------------*/
+/* FontName: Times-Roman */
+/* FullName: Times Roman */
+/* FamilyName: Times */
+static afm_cuint8 afm_Times_Roman_widths[] = { /* 315 */
+ 42,56,68,83,83,139,130,30,56,56,83,94,42,56,42,46,83,83,83,83,83,
+ 83,83,83,83,83,46,46,94,94,94,74,154,120,111,111,120,102,93,120,
+ 120,56,65,120,102,148,120,120,93,120,111,93,102,120,120,157,120,
+ 120,102,56,46,56,78,83,56,74,83,74,83,74,56,83,83,46,46,83,46,130,
+ 83,83,83,83,56,65,46,83,83,120,83,83,74,80,33,80,90,56,83,83,83,
+ 83,33,83,56,127,46,83,94,127,56,67,94,50,50,56,83,76,42,56,50,52,
+ 83,125,125,125,74,120,120,120,120,120,120,148,111,102,102,102,102,
+ 56,56,56,56,120,120,120,120,120,120,120,94,120,120,120,120,120,120,
+ 93,83,74,74,74,74,74,74,111,74,74,74,74,74,46,46,46,46,83,83,83,
+ 83,83,83,83,94,83,83,83,83,83,83,83,83,120,74,120,74,120,74,111,
+ 74,111,74,120,98,120,83,102,74,102,74,102,74,102,74,120,83,120,83,
+ 56,46,56,46,56,46,120,83,102,46,102,46,102,57,102,46,120,83,120,
+ 83,120,83,120,83,120,83,148,120,111,56,111,56,111,56,93,65,93,65,
+ 93,65,102,46,102,54,120,83,120,83,120,83,120,83,120,102,74,102,74,
+ 102,74,83,93,65,56,56,56,56,56,56,56,56,83,167,56,56,56,74,74,74,
+ 83,83,58,167,167,56,56,28,83,163,79,102,100,94,76,92,92,92,79,42,
+ 93,93
+};
+static afm_sint16 afm_Times_Roman_kerning_index[] = { /* 315 */
+ 1,0,0,0,0,0,0,0,0,0,0,0,44,0,53,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,62,159,0,205,0,240,0,0,0,311,335,427,0,458,482,527,578,601,0,
+ 663,831,855,1020,0,1175,0,0,0,0,0,0,0,1323,1328,1355,0,1363,1385,
+ 0,0,1422,0,1425,1475,0,1478,1488,1500,0,1508,0,0,0,1523,1593,1641,
+ 1664,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,1669,1766,1863,1960,2057,2154,0,0,0,0,0,0,0,0,0,0,0,2251,
+ 2275,2320,2365,2410,2455,0,2500,2545,2569,2593,2617,2641,0,0,2789,
+ 2794,2799,2804,2809,2814,0,2819,2827,2849,2871,2893,2915,2918,2921,
+ 2924,0,2927,2937,2949,2961,2973,2985,0,2997,0,0,0,0,3009,0,3014,
+ 3019,3116,3121,3218,3223,3320,0,3325,0,3333,3341,0,3376,0,0,3411,
+ 0,3433,0,3455,0,3477,0,0,0,0,0,3499,0,3502,0,0,3505,3597,3647,3678,
+ 3681,3712,3715,0,3727,3758,3761,3785,3795,3819,3829,3853,3863,3908,
+ 3920,3965,0,0,3977,4039,4054,4116,4131,4193,0,0,0,0,0,0,4208,0,4376,
+ 0,4544,0,4568,0,4592,0,4616,0,4640,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,4788,4816,0,4872,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0
+};
+static afm_cuint8 afm_Times_Roman_kerning_data[] = { /* 4896 */
+ 42,19,66,248,85,254,87,249,88,252,90,242,193,248,194,248,195,248,
+ 196,248,197,248,198,248,222,242,0,2,248,0,4,248,0,6,248,0,100,254,
+ 0,102,254,0,122,242,3,1,32,25,245,1,32,29,245,3,1,32,25,245,1,32,
+ 29,245,41,68,250,72,250,80,248,82,248,85,238,86,248,87,234,88,242,
+ 90,239,119,245,120,242,122,242,200,250,211,248,212,248,213,248,214,
+ 248,215,248,217,248,218,248,219,248,220,248,221,248,222,239,254,
+ 242,0,1,242,0,8,250,0,14,250,0,32,250,0,36,250,0,78,248,0,82,248,
+ 0,100,238,0,102,238,0,108,248,0,112,248,0,114,248,0,116,248,0,122,
+ 239,1,32,25,238,20,66,251,86,255,193,251,194,251,195,251,196,251,
+ 197,251,198,251,218,255,219,255,220,255,221,255,0,2,251,0,4,251,
+ 0,6,251,0,108,255,0,112,255,0,114,255,0,116,255,16,66,250,87,250,
+ 88,252,90,248,193,250,194,250,195,250,196,250,197,250,198,250,222,
+ 248,0,2,250,0,4,250,0,6,250,0,122,248,32,45,244,47,244,66,245,98,
+ 254,112,254,193,245,194,245,195,245,196,245,197,245,198,245,225,
+ 254,226,254,227,254,228,254,229,254,230,254,243,254,244,254,245,
+ 254,246,254,247,254,249,254,0,2,245,0,3,254,0,4,245,0,5,254,0,6,
+ 245,0,7,254,0,79,254,0,83,254,11,66,247,193,247,194,247,195,247,
+ 196,247,197,247,198,247,0,2,247,0,4,247,0,6,247,40,80,252,102,253,
+ 112,251,118,254,122,253,211,252,212,252,213,252,214,252,215,252,
+ 217,252,233,253,234,253,235,253,236,253,243,251,244,251,245,251,
+ 246,251,247,251,249,251,250,254,251,254,252,254,253,254,254,253,
+ 0,1,253,0,21,253,0,25,253,0,27,253,0,29,253,0,78,252,0,79,251,0,
+ 82,252,0,83,251,0,109,254,0,113,254,0,115,254,0,117,254,13,85,242,
+ 87,240,88,245,90,240,122,248,222,240,254,248,0,1,248,0,100,242,0,
+ 102,242,0,122,240,1,32,25,242,11,66,251,193,251,194,251,195,251,
+ 196,251,197,251,198,251,0,2,251,0,4,251,0,6,251,20,66,251,85,250,
+ 87,249,88,251,89,250,90,249,193,251,194,251,195,251,196,251,197,
+ 251,198,251,222,249,0,2,251,0,4,251,0,6,251,0,100,250,0,102,250,
+ 0,122,249,23,45,238,47,238,66,242,98,254,193,242,194,242,195,242,
+ 196,242,197,242,198,242,225,254,226,254,227,254,228,254,229,254,
+ 230,254,0,2,242,0,3,254,0,4,242,0,5,254,0,6,242,0,7,254,10,86,255,
+ 218,255,219,255,220,255,221,255,0,108,255,0,112,255,0,114,255,0,
+ 116,255,27,80,250,85,247,86,250,87,244,88,248,90,246,211,250,212,
+ 250,213,250,214,250,215,250,217,250,218,250,219,250,220,250,221,
+ 250,222,246,0,78,250,0,82,250,0,100,247,0,102,247,0,108,250,0,112,
+ 250,0,114,250,0,116,250,0,122,246,73,45,245,46,242,47,245,59,249,
+ 60,248,66,241,80,254,98,244,102,245,106,251,112,244,115,251,118,
+ 249,120,244,122,244,193,241,194,241,195,241,196,241,197,241,198,
+ 241,211,254,212,254,213,254,214,254,215,254,217,254,225,250,226,
+ 244,227,244,228,250,229,250,230,244,233,245,234,245,235,245,236,
+ 252,238,251,243,244,244,244,245,244,246,244,247,244,249,244,250,
+ 249,251,249,252,249,253,249,254,244,0,1,244,0,2,241,0,3,250,0,4,
+ 241,0,5,244,0,6,241,0,7,244,0,21,252,0,25,245,0,27,245,0,29,245,
+ 0,49,251,0,78,254,0,79,244,0,82,254,0,83,244,0,87,251,0,89,251,0,
+ 91,251,0,109,249,0,113,249,0,115,249,0,117,249,11,66,250,193,250,
+ 194,250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,
+ 72,45,235,46,240,47,235,59,245,60,245,66,234,72,254,80,250,98,238,
+ 102,238,106,247,112,235,118,244,193,234,194,234,195,234,196,234,
+ 197,234,198,234,211,250,212,250,213,250,214,250,215,250,217,250,
+ 225,245,226,238,227,245,228,245,229,245,230,238,233,245,234,238,
+ 235,245,236,245,237,254,238,247,239,254,240,254,243,242,244,235,
+ 245,235,246,242,247,242,249,235,250,244,251,244,252,244,253,244,
+ 0,2,234,0,3,245,0,4,234,0,5,238,0,6,234,0,7,238,0,21,245,0,25,238,
+ 0,27,238,0,29,245,0,32,254,0,36,254,0,45,254,0,49,247,0,78,250,0,
+ 79,242,0,82,250,0,83,235,0,109,244,0,113,244,0,115,244,0,117,244,
+ 68,45,242,46,246,47,242,59,251,60,251,66,237,80,255,98,244,102,244,
+ 106,250,112,244,118,249,122,245,193,237,194,237,195,237,196,237,
+ 197,237,198,237,211,255,212,255,213,255,214,255,215,255,217,255,
+ 225,244,226,244,227,244,228,244,229,244,230,244,233,250,234,244,
+ 235,244,236,250,238,250,243,244,244,244,245,244,246,244,247,244,
+ 249,244,250,249,251,249,252,249,253,249,254,245,0,1,245,0,2,237,
+ 0,3,244,0,4,237,0,5,244,0,6,237,0,7,244,0,21,250,0,25,244,0,27,244,
+ 0,29,244,0,49,250,0,78,255,0,79,244,0,82,255,0,83,244,0,109,249,
+ 0,113,249,0,115,249,0,117,249,65,45,235,46,238,47,235,59,242,60,
+ 242,66,237,80,252,98,240,102,240,106,248,112,239,118,238,193,237,
+ 194,237,195,237,196,237,197,237,198,237,211,252,212,252,213,252,
+ 214,252,215,252,217,252,225,247,226,240,227,240,228,247,229,247,
+ 230,240,233,247,234,240,235,240,236,247,238,248,243,245,244,239,
+ 245,239,246,245,247,245,249,239,250,245,251,238,252,238,253,245,
+ 0,2,237,0,3,247,0,4,237,0,5,240,0,6,237,0,7,240,0,21,247,0,25,240,
+ 0,27,240,0,29,240,0,49,248,0,78,252,0,79,245,0,82,252,0,83,239,0,
+ 109,245,0,113,238,0,115,238,0,117,238,3,119,254,120,254,12,47,250,
+ 118,254,119,254,250,254,251,254,252,254,253,254,0,109,254,0,113,
+ 254,0,115,254,0,117,254,4,122,254,254,254,0,1,254,10,104,254,119,
+ 253,120,253,121,254,122,254,254,254,0,1,254,0,33,254,0,37,254,16,
+ 98,255,103,253,106,254,225,255,226,255,227,255,228,255,229,255,230,
+ 255,238,254,0,3,255,0,5,255,0,7,255,0,51,249,1,32,25,9,2,119,253,
+ 22,102,255,112,255,122,254,233,255,234,255,235,255,236,255,243,255,
+ 244,255,245,255,246,255,247,255,249,255,254,254,0,1,254,0,21,255,
+ 0,25,255,0,27,255,0,29,255,0,79,255,0,83,255,2,120,255,5,119,250,
+ 122,254,254,254,0,1,254,6,119,254,120,253,122,255,254,255,0,1,255,
+ 4,122,255,254,255,0,1,255,7,45,250,46,254,47,248,104,254,0,33,254,
+ 0,37,254,31,45,246,47,246,98,253,102,254,112,254,225,253,226,253,
+ 227,253,228,253,229,253,230,253,233,254,234,254,235,254,236,254,
+ 243,254,244,254,245,254,246,254,247,254,249,254,0,3,253,0,5,253,
+ 0,7,253,0,21,254,0,25,254,0,27,254,0,29,254,0,79,254,0,83,254,22,
+ 45,246,47,246,98,255,112,255,225,255,226,255,227,255,228,255,229,
+ 255,230,255,243,255,244,255,245,255,246,255,247,255,249,255,0,3,
+ 255,0,5,255,0,7,255,0,79,255,0,83,255,10,102,254,233,254,234,254,
+ 235,254,236,254,0,21,254,0,25,254,0,27,254,0,29,254,3,45,246,47,
+ 246,41,68,250,72,250,80,248,82,248,85,238,86,248,87,234,88,242,90,
+ 239,119,245,120,242,122,242,200,250,211,248,212,248,213,248,214,
+ 248,215,248,217,248,218,248,219,248,220,248,221,248,222,239,254,
+ 242,0,1,242,0,8,250,0,14,250,0,32,250,0,36,250,0,78,248,0,82,248,
+ 0,100,238,0,102,238,0,108,248,0,112,248,0,114,248,0,116,248,0,122,
+ 239,1,32,25,238,41,68,250,72,250,80,248,82,248,85,238,86,248,87,
+ 234,88,242,90,239,119,245,120,242,122,242,200,250,211,248,212,248,
+ 213,248,214,248,215,248,217,248,218,248,219,248,220,248,221,248,
+ 222,239,254,242,0,1,242,0,8,250,0,14,250,0,32,250,0,36,250,0,78,
+ 248,0,82,248,0,100,238,0,102,238,0,108,248,0,112,248,0,114,248,0,
+ 116,248,0,122,239,1,32,25,238,41,68,250,72,250,80,248,82,248,85,
+ 238,86,248,87,234,88,242,90,239,119,245,120,242,122,242,200,250,
+ 211,248,212,248,213,248,214,248,215,248,217,248,218,248,219,248,
+ 220,248,221,248,222,239,254,242,0,1,242,0,8,250,0,14,250,0,32,250,
+ 0,36,250,0,78,248,0,82,248,0,100,238,0,102,238,0,108,248,0,112,248,
+ 0,114,248,0,116,248,0,122,239,1,32,25,238,41,68,250,72,250,80,248,
+ 82,248,85,238,86,248,87,234,88,242,90,239,119,245,120,242,122,242,
+ 200,250,211,248,212,248,213,248,214,248,215,248,217,248,218,248,
+ 219,248,220,248,221,248,222,239,254,242,0,1,242,0,8,250,0,14,250,
+ 0,32,250,0,36,250,0,78,248,0,82,248,0,100,238,0,102,238,0,108,248,
+ 0,112,248,0,114,248,0,116,248,0,122,239,1,32,25,238,41,68,250,72,
+ 250,80,248,82,248,85,238,86,248,87,234,88,242,90,239,119,245,120,
+ 242,122,242,200,250,211,248,212,248,213,248,214,248,215,248,217,
+ 248,218,248,219,248,220,248,221,248,222,239,254,242,0,1,242,0,8,
+ 250,0,14,250,0,32,250,0,36,250,0,78,248,0,82,248,0,100,238,0,102,
+ 238,0,108,248,0,112,248,0,114,248,0,116,248,0,122,239,1,32,25,238,
+ 41,68,250,72,250,80,248,82,248,85,238,86,248,87,234,88,242,90,239,
+ 119,245,120,242,122,242,200,250,211,248,212,248,213,248,214,248,
+ 215,248,217,248,218,248,219,248,220,248,221,248,222,239,254,242,
+ 0,1,242,0,8,250,0,14,250,0,32,250,0,36,250,0,78,248,0,82,248,0,100,
+ 238,0,102,238,0,108,248,0,112,248,0,114,248,0,116,248,0,122,239,
+ 1,32,25,238,11,66,251,193,251,194,251,195,251,196,251,197,251,198,
+ 251,0,2,251,0,4,251,0,6,251,20,66,251,85,250,87,249,88,251,89,250,
+ 90,249,193,251,194,251,195,251,196,251,197,251,198,251,222,249,0,
+ 2,251,0,4,251,0,6,251,0,100,250,0,102,250,0,122,249,20,66,251,85,
+ 250,87,249,88,251,89,250,90,249,193,251,194,251,195,251,196,251,
+ 197,251,198,251,222,249,0,2,251,0,4,251,0,6,251,0,100,250,0,102,
+ 250,0,122,249,20,66,251,85,250,87,249,88,251,89,250,90,249,193,251,
+ 194,251,195,251,196,251,197,251,198,251,222,249,0,2,251,0,4,251,
+ 0,6,251,0,100,250,0,102,250,0,122,249,20,66,251,85,250,87,249,88,
+ 251,89,250,90,249,193,251,194,251,195,251,196,251,197,251,198,251,
+ 222,249,0,2,251,0,4,251,0,6,251,0,100,250,0,102,250,0,122,249,20,
+ 66,251,85,250,87,249,88,251,89,250,90,249,193,251,194,251,195,251,
+ 196,251,197,251,198,251,222,249,0,2,251,0,4,251,0,6,251,0,100,250,
+ 0,102,250,0,122,249,20,66,251,85,250,87,249,88,251,89,250,90,249,
+ 193,251,194,251,195,251,196,251,197,251,198,251,222,249,0,2,251,
+ 0,4,251,0,6,251,0,100,250,0,102,250,0,122,249,11,66,250,193,250,
+ 194,250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,
+ 11,66,250,193,250,194,250,195,250,196,250,197,250,198,250,0,2,250,
+ 0,4,250,0,6,250,11,66,250,193,250,194,250,195,250,196,250,197,250,
+ 198,250,0,2,250,0,4,250,0,6,250,11,66,250,193,250,194,250,195,250,
+ 196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,65,45,235,46,238,
+ 47,235,59,242,60,242,66,237,80,252,98,240,102,240,106,248,112,239,
+ 118,238,193,237,194,237,195,237,196,237,197,237,198,237,211,252,
+ 212,252,213,252,214,252,215,252,217,252,225,247,226,240,227,240,
+ 228,247,229,247,230,240,233,247,234,240,235,240,236,247,238,248,
+ 243,245,244,239,245,239,246,245,247,245,249,239,250,245,251,238,
+ 252,238,253,245,0,2,237,0,3,247,0,4,237,0,5,240,0,6,237,0,7,240,
+ 0,21,247,0,25,240,0,27,240,0,29,240,0,49,248,0,78,252,0,79,245,0,
+ 82,252,0,83,239,0,109,245,0,113,238,0,115,238,0,117,238,3,119,254,
+ 120,254,3,119,254,120,254,3,119,254,120,254,3,119,254,120,254,3,
+ 119,254,120,254,3,119,254,120,254,4,122,254,254,254,0,1,254,10,104,
+ 254,119,253,120,253,121,254,122,254,254,254,0,1,254,0,33,254,0,37,
+ 254,10,104,254,119,253,120,253,121,254,122,254,254,254,0,1,254,0,
+ 33,254,0,37,254,10,104,254,119,253,120,253,121,254,122,254,254,254,
+ 0,1,254,0,33,254,0,37,254,10,104,254,119,253,120,253,121,254,122,
+ 254,254,254,0,1,254,0,33,254,0,37,254,2,119,253,2,119,253,2,119,
+ 253,2,119,253,5,119,250,122,254,254,254,0,1,254,6,119,254,120,253,
+ 122,255,254,255,0,1,255,6,119,254,120,253,122,255,254,255,0,1,255,
+ 6,119,254,120,253,122,255,254,255,0,1,255,6,119,254,120,253,122,
+ 255,254,255,0,1,255,6,119,254,120,253,122,255,254,255,0,1,255,6,
+ 119,254,120,253,122,255,254,255,0,1,255,3,45,246,47,246,3,45,246,
+ 47,246,41,68,250,72,250,80,248,82,248,85,238,86,248,87,234,88,242,
+ 90,239,119,245,120,242,122,242,200,250,211,248,212,248,213,248,214,
+ 248,215,248,217,248,218,248,219,248,220,248,221,248,222,239,254,
+ 242,0,1,242,0,8,250,0,14,250,0,32,250,0,36,250,0,78,248,0,82,248,
+ 0,100,238,0,102,238,0,108,248,0,112,248,0,114,248,0,116,248,0,122,
+ 239,1,32,25,238,3,119,254,120,254,41,68,250,72,250,80,248,82,248,
+ 85,238,86,248,87,234,88,242,90,239,119,245,120,242,122,242,200,250,
+ 211,248,212,248,213,248,214,248,215,248,217,248,218,248,219,248,
+ 220,248,221,248,222,239,254,242,0,1,242,0,8,250,0,14,250,0,32,250,
+ 0,36,250,0,78,248,0,82,248,0,100,238,0,102,238,0,108,248,0,112,248,
+ 0,114,248,0,116,248,0,122,239,1,32,25,238,3,119,254,120,254,41,68,
+ 250,72,250,80,248,82,248,85,238,86,248,87,234,88,242,90,239,119,
+ 245,120,248,122,248,200,250,211,248,212,248,213,248,214,248,215,
+ 248,217,248,218,248,219,248,220,248,221,248,222,239,254,248,0,1,
+ 248,0,8,250,0,14,250,0,32,250,0,36,250,0,78,248,0,82,248,0,100,238,
+ 0,102,238,0,108,248,0,112,248,0,114,248,0,116,248,0,122,239,1,32,
+ 25,238,3,119,254,120,254,4,122,254,254,254,0,1,254,4,122,254,254,
+ 254,0,1,254,16,66,250,87,250,88,252,90,248,193,250,194,250,195,250,
+ 196,250,197,250,198,250,222,248,0,2,250,0,4,250,0,6,250,0,122,248,
+ 16,66,250,87,250,88,252,90,248,193,250,194,250,195,250,196,250,197,
+ 250,198,250,222,248,0,2,250,0,4,250,0,6,250,0,122,248,10,104,254,
+ 119,253,120,253,121,254,122,254,254,254,0,1,254,0,33,254,0,37,254,
+ 10,104,254,119,253,120,253,121,254,122,254,254,254,0,1,254,0,33,
+ 254,0,37,254,10,104,254,119,253,120,253,121,254,122,254,254,254,
+ 0,1,254,0,33,254,0,37,254,10,104,254,119,253,120,253,121,254,122,
+ 254,254,254,0,1,254,0,33,254,0,37,254,2,119,253,2,119,253,40,80,
+ 252,102,253,112,251,118,254,122,253,211,252,212,252,213,252,214,
+ 252,215,252,217,252,233,253,234,253,235,253,236,253,243,251,244,
+ 251,245,251,246,251,247,251,249,251,250,254,251,254,252,254,253,
+ 254,254,253,0,1,253,0,21,253,0,25,253,0,27,253,0,29,253,0,78,252,
+ 0,79,251,0,82,252,0,83,251,0,109,254,0,113,254,0,115,254,0,117,254,
+ 22,102,255,112,255,122,254,233,255,234,255,235,255,236,255,243,255,
+ 244,255,245,255,246,255,247,255,249,255,254,254,0,1,254,0,21,255,
+ 0,25,255,0,27,255,0,29,255,0,79,255,0,83,255,13,85,242,87,240,88,
+ 245,90,240,122,248,222,240,254,248,0,1,248,0,100,242,0,102,242,0,
+ 122,240,1,32,25,242,2,120,255,13,85,242,87,240,88,245,90,240,122,
+ 248,222,240,254,248,0,1,248,0,100,242,0,102,242,0,122,240,1,32,25,
+ 242,2,120,255,5,122,248,254,248,0,1,248,1,32,25,242,13,85,242,87,
+ 240,88,245,90,240,122,248,222,240,254,248,0,1,248,0,100,242,0,102,
+ 242,0,122,240,1,32,25,242,2,120,255,11,66,251,193,251,194,251,195,
+ 251,196,251,197,251,198,251,0,2,251,0,4,251,0,6,251,5,119,250,122,
+ 254,254,254,0,1,254,11,66,251,193,251,194,251,195,251,196,251,197,
+ 251,198,251,0,2,251,0,4,251,0,6,251,5,119,250,122,254,254,254,0,
+ 1,254,11,66,251,193,251,194,251,195,251,196,251,197,251,198,251,
+ 0,2,251,0,4,251,0,6,251,5,119,250,122,254,254,254,0,1,254,20,66,
+ 251,85,250,87,249,88,251,89,250,90,249,193,251,194,251,195,251,196,
+ 251,197,251,198,251,222,249,0,2,251,0,4,251,0,6,251,0,100,250,0,
+ 102,250,0,122,249,6,119,254,120,253,122,255,254,255,0,1,255,20,66,
+ 251,85,250,87,249,88,251,89,250,90,249,193,251,194,251,195,251,196,
+ 251,197,251,198,251,222,249,0,2,251,0,4,251,0,6,251,0,100,250,0,
+ 102,250,0,122,249,6,119,254,120,253,122,255,254,255,0,1,255,27,80,
+ 250,85,247,86,250,87,244,88,248,90,246,211,250,212,250,213,250,214,
+ 250,215,250,217,250,218,250,219,250,220,250,221,250,222,246,0,78,
+ 250,0,82,250,0,100,247,0,102,247,0,108,250,0,112,250,0,114,250,0,
+ 116,250,0,122,246,7,45,250,46,254,47,248,104,254,0,33,254,0,37,254,
+ 27,80,250,85,247,86,250,87,244,88,248,90,246,211,250,212,250,213,
+ 250,214,250,215,250,217,250,218,250,219,250,220,250,221,250,222,
+ 246,0,78,250,0,82,250,0,100,247,0,102,247,0,108,250,0,112,250,0,
+ 114,250,0,116,250,0,122,246,7,45,250,46,254,47,248,104,254,0,33,
+ 254,0,37,254,27,80,250,85,247,86,250,87,244,88,248,90,246,211,250,
+ 212,250,213,250,214,250,215,250,217,250,218,250,219,250,220,250,
+ 221,250,222,246,0,78,250,0,82,250,0,100,247,0,102,247,0,108,250,
+ 0,112,250,0,114,250,0,116,250,0,122,246,7,45,250,46,254,47,248,104,
+ 254,0,33,254,0,37,254,73,45,245,46,242,47,245,59,249,60,248,66,241,
+ 80,254,98,244,102,245,106,251,112,244,115,251,118,249,120,244,122,
+ 244,193,241,194,241,195,241,196,241,197,241,198,241,211,254,212,
+ 254,213,254,214,254,215,254,217,254,225,250,226,244,227,244,228,
+ 250,229,250,230,244,233,252,234,245,235,252,236,252,238,251,243,
+ 244,244,244,245,244,246,244,247,244,249,244,250,249,251,249,252,
+ 249,253,249,254,244,0,1,244,0,2,241,0,3,250,0,4,241,0,5,244,0,6,
+ 241,0,7,244,0,21,245,0,25,245,0,27,245,0,29,245,0,49,251,0,78,254,
+ 0,79,244,0,82,254,0,83,244,0,87,251,0,89,251,0,91,251,0,109,249,
+ 0,113,249,0,115,249,0,117,249,73,45,245,46,242,47,245,59,249,60,
+ 248,66,241,80,254,98,244,102,245,106,251,112,244,115,251,118,249,
+ 120,244,122,244,193,241,194,241,195,241,196,241,197,241,198,241,
+ 211,254,212,254,213,254,214,254,215,254,217,254,225,250,226,244,
+ 227,244,228,250,229,250,230,244,233,245,234,245,235,252,236,252,
+ 238,251,243,244,244,244,245,244,246,244,247,244,249,244,250,249,
+ 251,249,252,249,253,249,254,244,0,1,244,0,2,241,0,3,250,0,4,241,
+ 0,5,244,0,6,241,0,7,244,0,21,252,0,25,245,0,27,245,0,29,245,0,49,
+ 251,0,78,254,0,79,244,0,82,254,0,83,244,0,87,251,0,89,251,0,91,251,
+ 0,109,249,0,113,249,0,115,249,0,117,249,11,66,250,193,250,194,250,
+ 195,250,196,250,197,250,198,250,0,2,250,0,4,250,0,6,250,11,66,250,
+ 193,250,194,250,195,250,196,250,197,250,198,250,0,2,250,0,4,250,
+ 0,6,250,11,66,250,193,250,194,250,195,250,196,250,197,250,198,250,
+ 0,2,250,0,4,250,0,6,250,11,66,250,193,250,194,250,195,250,196,250,
+ 197,250,198,250,0,2,250,0,4,250,0,6,250,65,45,235,46,238,47,235,
+ 59,242,60,242,66,237,80,252,98,240,102,240,106,248,112,239,118,238,
+ 193,237,194,237,195,237,196,237,197,237,198,237,211,252,212,252,
+ 213,252,214,252,215,252,217,252,225,247,226,240,227,240,228,240,
+ 229,247,230,240,233,247,234,240,235,240,236,247,238,248,243,245,
+ 244,239,245,239,246,245,247,245,249,239,250,245,251,238,252,238,
+ 253,245,0,2,237,0,3,247,0,4,237,0,5,240,0,6,237,0,7,240,0,21,247,
+ 0,25,240,0,27,240,0,29,240,0,49,248,0,78,252,0,79,245,0,82,252,0,
+ 83,239,0,109,245,0,113,238,0,115,238,0,117,238,12,66,244,193,244,
+ 194,244,195,244,196,244,197,244,198,244,0,2,244,0,4,244,0,6,244,
+ 1,32,24,245,21,33,245,101,249,109,255,115,249,116,248,117,254,119,
+ 249,0,19,249,0,60,255,0,62,255,0,68,255,0,87,249,0,89,249,0,91,249,
+ 0,93,248,0,97,248,0,99,248,0,101,254,1,2,25,248,1,32,25,245,11,66,
+ 244,193,244,194,244,195,244,196,244,197,244,198,244,0,2,244,0,4,
+ 244,0,6,244
+};
+static afm_cuint16 afm_Times_Roman_highchars_index[] = { /* 220 */
+ 161,162,163,164,165,166,167,168,169,170,171,172,174,175,176,177,
+ 178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,
+ 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,
+ 210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,
+ 226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,
+ 242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,257,
+ 258,259,260,261,262,263,268,269,270,271,272,273,274,275,278,279,
+ 280,281,282,283,286,287,290,291,298,299,302,303,304,305,310,311,
+ 313,314,315,316,317,318,321,322,323,324,325,326,327,328,332,333,
+ 336,337,338,339,340,341,342,343,344,345,346,347,350,351,352,353,
+ 354,355,356,357,362,363,366,367,368,369,370,371,376,377,378,379,
+ 380,381,382,402,536,537,710,711,728,729,730,731,732,733,8211,8212,
+ 8216,8217,8218,8220,8221,8222,8224,8225,8226,8230,8240,8249,8250,
+ 8260,8364,8482,8706,8710,8721,8722,8730,8800,8804,8805,9674,63171,
+ 64257,64258
+};
+static afm_cunicode afm_Times_Roman_ligatures[] = { /* 3 */
+ 102,105,64257
+};
+const afm_fontinfo afm_fontinfolist[] = {
+ { /* Courier.afm 761 bytes */
+ "Courier", "Courier",
+ 629, -157,
+ afm_Courier_widths,
+ NULL,
+ NULL,
+ afm_Courier_highchars_index, 220,
+ afm_Courier_ligatures, 1},
+ { /* Courier-Bold.afm 761 bytes */
+ "Courier-Bold", "Courier Bold",
+ 629, -157,
+ afm_Courier_Bold_widths,
+ NULL,
+ NULL,
+ afm_Courier_Bold_highchars_index, 220,
+ afm_Courier_Bold_ligatures, 1},
+ { /* Courier-BoldOblique.afm 761 bytes */
+ "Courier-BoldOblique", "Courier Bold Oblique",
+ 629, -157,
+ afm_Courier_BoldOblique_widths,
+ NULL,
+ NULL,
+ afm_Courier_BoldOblique_highchars_index, 220,
+ afm_Courier_BoldOblique_ligatures, 1},
+ { /* Courier-Oblique.afm 761 bytes */
+ "Courier-Oblique", "Courier Oblique",
+ 629, -157,
+ afm_Courier_Oblique_widths,
+ NULL,
+ NULL,
+ afm_Courier_Oblique_highchars_index, 220,
+ afm_Courier_Oblique_ligatures, 1},
+ { /* Helvetica.afm 7841 bytes */
+ "Helvetica", "Helvetica",
+ 718, -207,
+ afm_Helvetica_widths,
+ afm_Helvetica_kerning_index,
+ afm_Helvetica_kerning_data,
+ afm_Helvetica_highchars_index, 220,
+ afm_Helvetica_ligatures, 1},
+ { /* Helvetica-Bold.afm 7336 bytes */
+ "Helvetica-Bold", "Helvetica Bold",
+ 718, -207,
+ afm_Helvetica_Bold_widths,
+ afm_Helvetica_Bold_kerning_index,
+ afm_Helvetica_Bold_kerning_data,
+ afm_Helvetica_Bold_highchars_index, 220,
+ afm_Helvetica_Bold_ligatures, 1},
+ { /* Helvetica-BoldOblique.afm 7336 bytes */
+ "Helvetica-BoldOblique", "Helvetica Bold Oblique",
+ 718, -207,
+ afm_Helvetica_BoldOblique_widths,
+ afm_Helvetica_BoldOblique_kerning_index,
+ afm_Helvetica_BoldOblique_kerning_data,
+ afm_Helvetica_BoldOblique_highchars_index, 220,
+ afm_Helvetica_BoldOblique_ligatures, 1},
+ { /* Helvetica-Oblique.afm 7841 bytes */
+ "Helvetica-Oblique", "Helvetica Oblique",
+ 718, -207,
+ afm_Helvetica_Oblique_widths,
+ afm_Helvetica_Oblique_kerning_index,
+ afm_Helvetica_Oblique_kerning_data,
+ afm_Helvetica_Oblique_highchars_index, 220,
+ afm_Helvetica_Oblique_ligatures, 1},
+ { /* ZapfDingbats.afm 416 bytes */
+ "ZapfDingbats", "ITC Zapf Dingbats",
+ 0, 0,
+ afm_ZapfDingbats_widths,
+ NULL,
+ NULL,
+ afm_ZapfDingbats_highchars_index, 107,
+ NULL, 0},
+ { /* Symbol.afm 563 bytes */
+ "Symbol", "Symbol",
+ 0, 0,
+ afm_Symbol_widths,
+ NULL,
+ NULL,
+ afm_Symbol_highchars_index, 156,
+ NULL, 0},
+ { /* Times-Bold.afm 6761 bytes */
+ "Times-Bold", "Times Bold",
+ 683, -217,
+ afm_Times_Bold_widths,
+ afm_Times_Bold_kerning_index,
+ afm_Times_Bold_kerning_data,
+ afm_Times_Bold_highchars_index, 220,
+ afm_Times_Bold_ligatures, 1},
+ { /* Times-BoldItalic.afm 6270 bytes */
+ "Times-BoldItalic", "Times Bold Italic",
+ 683, -217,
+ afm_Times_BoldItalic_widths,
+ afm_Times_BoldItalic_kerning_index,
+ afm_Times_BoldItalic_kerning_data,
+ afm_Times_BoldItalic_highchars_index, 220,
+ afm_Times_BoldItalic_ligatures, 1},
+ { /* Times-Italic.afm 6975 bytes */
+ "Times-Italic", "Times Italic",
+ 683, -217,
+ afm_Times_Italic_widths,
+ afm_Times_Italic_kerning_index,
+ afm_Times_Italic_kerning_data,
+ afm_Times_Italic_highchars_index, 220,
+ afm_Times_Italic_ligatures, 1},
+ { /* Times-Roman.afm 6287 bytes */
+ "Times-Roman", "Times Roman",
+ 683, -217,
+ afm_Times_Roman_widths,
+ afm_Times_Roman_kerning_index,
+ afm_Times_Roman_kerning_data,
+ afm_Times_Roman_highchars_index, 220,
+ afm_Times_Roman_ligatures, 1},
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+const int afm_fontinfo_count = 14;
diff --git a/program/src/rrd_afm_data.h b/program/src/rrd_afm_data.h
--- /dev/null
@@ -0,0 +1,191 @@
+/****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd_afm_data.h Encoded afm (Adobe Font Metrics) for selected fonts.
+ ****************************************************************************/
+
+#ifndef RRD_AFM_DATA_H
+#define RRD_AFM_DATA_H
+
+/*
+Description of data structures:
+
+ Ideally, the struct should be a list of fonts, and each font
+ is a list of character-info.
+ Each character has a structure:
+ struct charinfo {
+ char16 thechar;
+ int width;
+ struct {
+ char16 nextchar;
+ int deltawidth;
+ } kernings[];
+ struct {
+ char16 nextchar;
+ char16 resultingchar;
+ } ligatures[];
+ }
+
+ The data for typical fonts makes this a very sparse data structure.
+ For most fonts, only the letter "f" has ligatures.
+ All fonts have all (or almost all) of the characters 32-126,
+ most fonts have all 161-255,
+ and all fonts have very few 256-65535.
+ Most kerning pairs have both chars 32-126.
+
+ The most basic design decisionÊis to have all this data as
+ const C globals all set up by array/struct initialisers
+ so runtime setup overhead is minimal.
+ The complete other possibility would be to parse and load
+ this info at runtime, but for rrdtool I have preferred
+ speed for flexibility as the same few fonts will be used
+ zillions of times.
+
+ So the idea is to rewrite the above structure into
+ something which:
+ 1) uses/wastes minimal memory
+ 2) is fast for most characters
+ 3) supports at least Iso-Latin-1, prefer full unicode.
+ 4) doesn't need full precision in char width
+ (we can afford to loose 0.2% as rrdtool only needs to calculate
+ overall layout of elements, not positioning individual
+ characters)
+ 5) can be written as constant initialisers to C structs/arrays
+ so we don't have runtime overhead starting rrdtool.
+ 6) can be easily generated by some script so it is easy
+ to select a set of fonts and have the C data updated.
+ So adding/removing fonts is a matter of a recompile.
+
+Implementation design:
+ All character structs are sorted by unicode value. Info for
+ characters below 32 is discarded and the chars are treated
+ as a space. Missing characters in the 32-126 range are
+ substituted with default values so we can use direct array
+ access for those. For characters above 126, binary search
+ is used (not yet, liniar now but uses good guess for most latin 1
+ characters).
+
+ Ligature handling can be discarded as ligatures have very small
+ effects on string width. The width of the "fi" ligature
+ is the same (or very close) to the width of "f" plus the width
+ of "i".
+ If implemented, it can be a simple list (global for the font,
+ not for each character) because all fonts I've seen that have
+ ligatures has max 3 pairs: "fi", "fl", "ffl" and no other.
+
+ Most characters has less than 10 kern pairs, few 10-20, and
+ extremly few 20-30. This is implemented as a simple
+ linear search with characters 256-65536 encoding using a prefix
+ so most kern pairs only take 2 bytes:
+ unsigned 8 bit char value and signed 8 bit kern width.
+ Using a non-packed format would enable binary search, but
+ would use almost twice as much memory for a yet unknown
+ gain in speed.
+
+ Character widths are stored as unsigned bytes. Width of
+ one character is font-size * bytevalue * (1000 / 6)
+ AFM specifies widths as integers with 1000 representing 1 * font-size.
+ Kerning delta widths has same scaling factor, but the value
+ is a signed byte as many kerning widths are negative and smaller
+ than avarage character width.
+
+ Kerning info is stored in a shared packed int8 array
+ to reduce the number of structs and memory usage.
+ This sets the maximum number of kerning pairs to
+ approx 15000.
+ The font I have seen with most kern pairs is
+ "Bodoni Old Face BE Bold Italic Oldstyle Figures"
+ which has 1718 pairs for 62 chars.
+ Typical fonts have 100-150 pairs.
+ For each character needs then only a 16 bit index
+ into this shared table.
+ The format of the sub-arrays are:
+ count ( unicode deltawidth )
+ with the (...) repeated count times.
+ The count and the unicode is packed because a lot
+ entries is less than 256, and most below 400.
+ Therefore an escape sequence is used.
+ If the value is >= 510
+ 1, high-8bits, low-8bits
+ else if the value is >= 254
+ 0, value minus 254
+ else
+ value plus 1
+ An index of zero is treated as a NULL pointer,
+ and the first byte in a shared array is
+ therefore not used (and filled with a dummy value).
+ The array is only created if non-empty.
+ No entries can be zero (they are redundant),
+ and no subarray can be empty (as the index pointer
+ then is 0 meaning no sub array).
+ The deltawidth is stored as a non-escaped signed byte.
+
+ So for each character needed info is:
+ width: unsigned 8 bit int.
+ kerning-subarray-index: unsigned 16 bit int.
+
+ The first 126-32+1 entries are for the characters
+ 32-126. If any is missing, a dummy entry is created.
+ For characters 126-65535 a font-global
+ array of struct {unicode, char-index} is
+ used for binary search (not yet, liniar now).
+
+ Ligatures can be implemented as a font-global
+ array of struct {
+ unicode char1, char2, resultingchar;
+ }
+
+ Font-global info is stored in a struct afm_fontinfo (see below).
+
+ The highchars_index and ligatures structures are flattened
+ to a simple array to avoid accidental padding between
+ structs if the structsize is problematic for some platforms.
+
+ All fonts are stored in an array of this struct,
+ sorted by fullname for binary search (not yet sorted).
+
+ The .afm files are compiled by a perl script which creates
+ rrd_afm_data.c
+ The only thing rrd_afm_data.c contains is this compiled data.
+
+ Compiled on Mac OS X the size of rrd_afm_data.o
+ is 67 Kb for the standard 14 postscript fonts,
+ and 490 Kb for a set of 276 Adobe fonts.
+*/
+
+typedef unsigned char afm_uint8;
+typedef signed char afm_sint8;
+typedef unsigned short afm_uint16;
+typedef signed short afm_sint16;
+typedef unsigned short afm_unicode;
+
+typedef const afm_uint8 afm_cuint8;
+typedef const afm_sint8 afm_csint8;
+typedef const afm_uint16 afm_cuint16;
+typedef const afm_sint16 afm_csint16;
+typedef const afm_unicode afm_cunicode;
+
+typedef struct afm_fontinfo {
+ const char *fullname; /* e.g. "Futura Bold Oblique" */
+ const char *postscript_name; /* e.g. "Futura-BoldOblique" */
+ afm_cuint16 ascender, descender;
+ afm_cuint8 *widths;
+ afm_csint16 *kerning_index;
+ afm_cuint8 *kerning_data;
+ afm_cuint16 *highchars_index;
+ afm_cuint16 highchars_count;
+ afm_cunicode *ligatures;
+ afm_cuint16 ligatures_count;
+} afm_fontinfo;
+
+typedef struct old_afm_fontinfo {
+ const char *fontname, *fullname;
+ const unsigned short *charinfo, *intarray;
+ const unsigned short charinfocount;
+ const unsigned short fixedpitch;
+} old_afm_fontinfo;
+
+extern const afm_fontinfo afm_fontinfolist[];
+extern const int afm_fontinfo_count;
+
+#endif
diff --git a/program/src/rrd_cgi.c b/program/src/rrd_cgi.c
--- /dev/null
+++ b/program/src/rrd_cgi.c
@@ -0,0 +1,1412 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_cgi.c RRD Web Page Generator
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+
+
+#define MEMBLK 1024
+/*#define DEBUG_PARSER
+#define DEBUG_VARS*/
+
+typedef struct var_s {
+ char *name, *value;
+} s_var;
+
+typedef struct cgi_s {
+ s_var **vars;
+} s_cgi;
+
+/* in arg[0] find tags beginning with arg[1] call arg[2] on them
+ and replace by result of arg[2] call */
+int parse(char **, long, char *, char *(*)(long , const char **));
+
+/**************************************************/
+/* tag replacers ... they are called from parse */
+/* through function pointers */
+/**************************************************/
+
+/* return cgi var named arg[0] */
+char* cgiget(long , const char **);
+
+/* return a quoted cgi var named arg[0] */
+char* cgigetq(long , const char **);
+
+/* return a quoted and sanitized cgi variable */
+char* cgigetqp(long , const char **);
+
+/* call rrd_graph and insert appropriate image tag */
+char* drawgraph(long, const char **);
+
+/* return PRINT functions from last rrd_graph call */
+char* drawprint(long, const char **);
+
+/* pretty-print the <last></last> value for some.rrd via strftime() */
+char* printtimelast(long, const char **);
+
+/* pretty-print current time */
+char* printtimenow(long, const char **);
+
+/* set an environment variable */
+char* rrdsetenv(long, const char **);
+
+/* get an environment variable */
+char* rrdgetenv(long, const char **);
+
+/* include the named file at this point */
+char* includefile(long, const char **);
+
+/* for how long is the output of the cgi valid ? */
+char* rrdgoodfor(long, const char **);
+
+/* return rrdcgi version string */
+char* rrdgetinternal(long, const char **);
+
+char* rrdstrip(char *buf);
+char* scanargs(char *line, int *argc, char ***args);
+
+/* format at-time specified times using strftime */
+char* printstrftime(long, const char**);
+
+/** HTTP protocol needs special format, and GMT time **/
+char *http_time(time_t *);
+
+/* return a pointer to newly allocated copy of this string */
+char *stralloc(const char *);
+
+/* global variable for rrdcgi */
+s_cgi *rrdcgiArg;
+
+/* rrdcgiHeader
+ *
+ * Prints a valid CGI Header (Content-type...) etc.
+ */
+void rrdcgiHeader(void);
+
+/* rrdcgiDecodeString
+ * decode html escapes
+ */
+
+char *rrdcgiDecodeString(char *text);
+
+/* rrdcgiDebug
+ *
+ * Set/unsets debugging
+ */
+void rrdcgiDebug(int level, int where);
+
+/* rrdcgiInit
+ *
+ * Reads in variables set via POST or stdin.
+ */
+s_cgi *rrdcgiInit (void);
+
+/* rrdcgiGetValue
+ *
+ * Returns the value of the specified variable or NULL if it's empty
+ * or doesn't exist.
+ */
+char *rrdcgiGetValue (s_cgi *parms, const char *name);
+
+/* rrdcgiFreeList
+ *
+ * Frees a list as returned by rrdcgiGetVariables()
+ */
+void rrdcgiFreeList (char **list);
+
+/* rrdcgiFree
+ *
+ * Frees the internal data structures
+ */
+void rrdcgiFree (s_cgi *parms);
+
+/* rrdcgiReadVariables()
+ *
+ * Read from stdin if no string is provided via CGI. Variables that
+ * doesn't have a value associated with it doesn't get stored.
+ */
+s_var **rrdcgiReadVariables(void);
+
+
+int rrdcgiDebugLevel = 0;
+int rrdcgiDebugStderr = 1;
+char *rrdcgiHeaderString = NULL;
+char *rrdcgiType = NULL;
+
+/* rrd interface to the variable functions {put,get}var() */
+char* rrdgetvar(long argc, const char **args);
+char* rrdsetvar(long argc, const char **args);
+char* rrdsetvarconst(long argc, const char **args);
+
+
+/* variable store: put/get key-value pairs */
+static int initvar();
+static void donevar();
+static const char* getvar(const char* varname);
+static const char* putvar(const char* name, const char* value, int is_const);
+
+/* key value pair that makes up an entry in the variable store */
+typedef struct
+{
+ int is_const; /* const variable or not */
+ const char* name; /* variable name */
+ const char* value; /* variable value */
+} vardata;
+
+/* the variable heap:
+ start with a heapsize of 10 variables */
+#define INIT_VARSTORE_SIZE 10
+static vardata* varheap = NULL;
+static size_t varheap_size = 0;
+
+/* allocate and initialize variable heap */
+static int
+initvar()
+{
+ varheap = (vardata*)malloc(sizeof(vardata) * INIT_VARSTORE_SIZE);
+ if (varheap == NULL) {
+ fprintf(stderr, "ERROR: unable to initialize variable store\n");
+ return -1;
+ }
+ memset(varheap, 0, sizeof(vardata) * INIT_VARSTORE_SIZE);
+ varheap_size = INIT_VARSTORE_SIZE;
+ return 0;
+}
+
+/* cleanup: free allocated memory */
+static void
+donevar()
+{
+ int i;
+ if (varheap) {
+ for (i=0; i<(int)varheap_size; i++) {
+ if (varheap[i].name) {
+ free((char*)varheap[i].name);
+ }
+ if (varheap[i].value) {
+ free((char*)varheap[i].value);
+ }
+ }
+ free(varheap);
+ }
+}
+
+/* Get a variable from the variable store.
+ Return NULL in case the requested variable was not found. */
+static const char*
+getvar(const char* name)
+{
+ int i;
+ for (i=0; i<(int)varheap_size && varheap[i].name; i++) {
+ if (0 == strcmp(name, varheap[i].name)) {
+#ifdef DEBUG_VARS
+ printf("<!-- getvar(%s) -> %s -->\n", name, varheap[i].value);
+#endif
+ return varheap[i].value;
+ }
+ }
+#ifdef DEBUG_VARS
+ printf("<!-- getvar(%s) -> Not found-->\n", name);
+#endif
+ return NULL;
+}
+
+/* Put a variable into the variable store. If a variable by that
+ name exists, it's value is overwritten with the new value unless it was
+ marked as 'const' (initialized by RRD::SETCONSTVAR).
+ Returns a copy the newly allocated value on success, NULL on error. */
+static const char*
+putvar(const char* name, const char* value, int is_const)
+{
+ int i;
+ for (i=0; i < (int)varheap_size && varheap[i].name; i++) {
+ if (0 == strcmp(name, varheap[i].name)) {
+ /* overwrite existing entry */
+ if (varheap[i].is_const) {
+#ifdef DEBUG_VARS
+ printf("<!-- setver(%s, %s): not assigning: "
+ "const variable -->\n", name, value);
+# endif
+ return varheap[i].value;
+ }
+#ifdef DEBUG_VARS
+ printf("<!-- setvar(%s, %s): overwriting old value (%s) -->\n",
+ name, value, varheap[i].value);
+#endif
+ /* make it possible to promote a variable to readonly */
+ varheap[i].is_const = is_const;
+ free((char*)varheap[i].value);
+ varheap[i].value = stralloc(value);
+ return varheap[i].value;
+ }
+ }
+
+ /* no existing variable found by that name, add it */
+ if (i == (int)varheap_size) {
+ /* ran out of heap: resize heap to double size */
+ size_t new_size = varheap_size * 2;
+ varheap = (vardata*)(realloc(varheap, sizeof(vardata) * new_size));
+ if (!varheap) {
+ fprintf(stderr, "ERROR: Unable to realloc variable heap\n");
+ return NULL;
+ }
+ /* initialize newly allocated memory */;
+ memset(&varheap[varheap_size], 0, sizeof(vardata) * varheap_size);
+ varheap_size = new_size;
+ }
+ varheap[i].is_const = is_const;
+ varheap[i].name = stralloc(name);
+ varheap[i].value = stralloc(value);
+
+#ifdef DEBUG_VARS
+ printf("<!-- setvar(%s, %s): adding new variable -->\n", name, value);
+#endif
+ return varheap[i].value;
+}
+
+/* expand those RRD:* directives that can be used recursivly */
+static char*
+rrd_expand_vars(char* buffer)
+{
+ int i;
+
+#ifdef DEBUG_PARSER
+ printf("expanding variables in '%s'\n", buffer);
+#endif
+
+ for (i=0; buffer[i]; i++) {
+ if (buffer[i] != '<')
+ continue;
+ parse(&buffer, i, "<RRD::CV", cgiget);
+ parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
+ parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
+ parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
+ parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
+ parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
+ parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
+ parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
+ parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
+ }
+ return buffer;
+}
+
+static long goodfor=0;
+static char **calcpr=NULL;
+static void calfree (void){
+ if (calcpr) {
+ long i;
+ for(i=0;calcpr[i];i++){
+ if (calcpr[i]){
+ free(calcpr[i]);
+ }
+ }
+ if (calcpr) {
+ free(calcpr);
+ }
+ }
+}
+
+/* create freeable version of the string */
+char * stralloc(const char *str){
+ char* nstr;
+ if (!str) {
+ return NULL;
+ }
+ nstr = malloc((strlen(str)+1));
+ strcpy(nstr,str);
+ return(nstr);
+}
+
+int main(int argc, char *argv[]) {
+ long length;
+ char *buffer;
+ char *server_url = NULL;
+ long i;
+ long filter=0;
+#ifdef MUST_DISABLE_SIGFPE
+ signal(SIGFPE,SIG_IGN);
+#endif
+#ifdef MUST_DISABLE_FPMASK
+ fpsetmask(0);
+#endif
+ optind = 0; opterr = 0; /* initialize getopt */
+
+ /* what do we get for cmdline arguments?
+ for (i=0;i<argc;i++)
+ printf("%d-'%s'\n",i,argv[i]); */
+ while (1) {
+ static struct option long_options[] = {
+ { "filter", no_argument, 0, 'f' },
+ { 0, 0, 0, 0}
+ };
+ int option_index = 0;
+ int opt;
+ opt = getopt_long(argc, argv, "f", long_options, &option_index);
+ if (opt == EOF) {
+ break;
+ }
+
+ switch(opt) {
+ case 'f':
+ filter=1;
+ break;
+ case '?':
+ printf("unknown commandline option '%s'\n",argv[optind-1]);
+ return -1;
+ }
+ }
+
+ if (!filter) {
+ rrdcgiDebug(0,0);
+ rrdcgiArg = rrdcgiInit();
+ server_url = getenv("SERVER_URL");
+ }
+
+ /* make sure we have one extra argument,
+ if there are others, we do not care Apache gives several */
+
+ /* if ( (optind != argc-2
+ && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
+ && optind != argc-1) { */
+
+ if ( optind >= argc ) {
+ fprintf(stderr, "ERROR: expected a filename\n");
+ exit(1);
+ } else {
+ length = readfile(argv[optind], &buffer, 1);
+ }
+
+ if(rrd_test_error()) {
+ fprintf(stderr, "ERROR: %s\n",rrd_get_error());
+ exit(1);
+ }
+
+ /* initialize variable heap */
+ initvar();
+
+#ifdef DEBUG_PARSER
+ /* some fake header for testing */
+ printf ("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
+#endif
+
+
+ /* expand rrd directives in buffer recursivly */
+ for (i=0; buffer[i]; i++) {
+ if (buffer[i] != '<')
+ continue;
+ if (!filter) {
+ parse(&buffer, i, "<RRD::CV", cgiget);
+ parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
+ parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
+ parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
+ }
+ parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
+ parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
+ parse(&buffer, i, "<RRD::GRAPH", drawgraph);
+ parse(&buffer, i, "<RRD::INCLUDE", includefile);
+ parse(&buffer, i, "<RRD::PRINT", drawprint);
+ parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
+ parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
+ parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
+ parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
+ parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
+ parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
+ parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
+ }
+
+ if (!filter) {
+ printf ("Content-Type: text/html\n"
+ "Content-Length: %d\n",
+ strlen(buffer));
+
+ if (labs(goodfor) > 0) {
+ time_t now;
+ now = time(NULL);
+ printf("Last-Modified: %s\n", http_time(&now));
+ now += labs(goodfor);
+ printf("Expires: %s\n", http_time(&now));
+ if (goodfor < 0) {
+ printf("Refresh: %ld\n", labs(goodfor));
+ }
+ }
+ printf("\n");
+ }
+
+ /* output result */
+ printf("%s", buffer);
+
+ /* cleanup */
+ calfree();
+ if (buffer){
+ free(buffer);
+ }
+ donevar();
+ exit(0);
+}
+
+/* remove occurrences of .. this is a general measure to make
+ paths which came in via cgi do not go UP ... */
+
+char* rrdsetenv(long argc, const char **args) {
+ if (argc >= 2) {
+ char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
+ if (xyz == NULL) {
+ return stralloc("[ERROR: allocating setenv buffer]");
+ };
+ sprintf(xyz, "%s=%s", args[0], args[1]);
+ if(putenv(xyz) == -1) {
+ free(xyz);
+ return stralloc("[ERROR: failed to do putenv]");
+ };
+ return stralloc("");
+ }
+ return stralloc("[ERROR: setenv failed because not enough "
+ "arguments were defined]");
+}
+
+/* rrd interface to the variable function putvar() */
+char*
+rrdsetvar(long argc, const char **args)
+{
+ if (argc >= 2)
+ {
+ const char* result = putvar(args[0], args[1], 0 /* not const */);
+ if (result) {
+ /* setvar does not return the value set */
+ return stralloc("");
+ }
+ return stralloc("[ERROR: putvar failed]");
+ }
+ return stralloc("[ERROR: putvar failed because not enough arguments "
+ "were defined]");
+}
+
+/* rrd interface to the variable function putvar() */
+char*
+rrdsetvarconst(long argc, const char **args)
+{
+ if (argc >= 2)
+ {
+ const char* result = putvar(args[0], args[1], 1 /* const */);
+ if (result) {
+ /* setvar does not return the value set */
+ return stralloc("");
+ }
+ return stralloc("[ERROR: putvar failed]");
+ }
+ return stralloc("[ERROR: putvar failed because not enough arguments "
+ "were defined]");
+}
+
+char* rrdgetenv(long argc, const char **args) {
+ char buf[128];
+ const char* envvar;
+ if (argc != 1) {
+ return stralloc("[ERROR: getenv failed because it did not "
+ "get 1 argument only]");
+ };
+ envvar = getenv(args[0]);
+ if (envvar) {
+ return stralloc(envvar);
+ } else {
+ snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
+ return stralloc(buf);
+ }
+}
+
+char* rrdgetvar(long argc, const char **args) {
+ char buf[128];
+ const char* value;
+ if (argc != 1) {
+ return stralloc("[ERROR: getvar failed because it did not "
+ "get 1 argument only]");
+ };
+ value = getvar(args[0]);
+ if (value) {
+ return stralloc(value);
+ } else {
+ snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
+ return stralloc(buf);
+ }
+}
+
+char* rrdgoodfor(long argc, const char **args){
+ if (argc == 1) {
+ goodfor = atol(args[0]);
+ } else {
+ return stralloc("[ERROR: goodfor expected 1 argument]");
+ }
+
+ if (goodfor == 0){
+ return stralloc("[ERROR: goodfor value must not be 0]");
+ }
+
+ return stralloc("");
+}
+
+char* rrdgetinternal(long argc, const char **args){
+ if (argc == 1) {
+ if( strcasecmp( args[0], "VERSION") == 0) {
+ return stralloc(PACKAGE_VERSION);
+ } else if( strcasecmp( args[0], "COMPILETIME") == 0) {
+ return stralloc(__DATE__ " " __TIME__);
+ } else {
+ return stralloc("[ERROR: internal unknown argument]");
+ }
+ } else {
+ return stralloc("[ERROR: internal expected 1 argument]");
+ }
+}
+
+/* Format start or end times using strftime. We always need both the
+ * start and end times, because, either might be relative to the other.
+ * */
+#define MAX_STRFTIME_SIZE 256
+char* printstrftime(long argc, const char **args){
+ struct rrd_time_value start_tv, end_tv;
+ char *parsetime_error = NULL;
+ char formatted[MAX_STRFTIME_SIZE];
+ struct tm *the_tm;
+ time_t start_tmp, end_tmp;
+
+ /* Make sure that we were given the right number of args */
+ if( argc != 4) {
+ rrd_set_error( "wrong number of args %d", argc);
+ return stralloc("");
+ }
+
+ /* Init start and end time */
+ parsetime("end-24h", &start_tv);
+ parsetime("now", &end_tv);
+
+ /* Parse the start and end times we were given */
+ if( (parsetime_error = parsetime( args[1], &start_tv))) {
+ rrd_set_error( "start time: %s", parsetime_error);
+ return stralloc("");
+ }
+ if( (parsetime_error = parsetime( args[2], &end_tv))) {
+ rrd_set_error( "end time: %s", parsetime_error);
+ return stralloc("");
+ }
+ if( proc_start_end( &start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
+ return stralloc("");
+ }
+
+ /* Do we do the start or end */
+ if( strcasecmp( args[0], "START") == 0) {
+ the_tm = localtime( &start_tmp);
+ }
+ else if( strcasecmp( args[0], "END") == 0) {
+ the_tm = localtime( &end_tmp);
+ }
+ else {
+ rrd_set_error( "start/end not found in '%s'", args[0]);
+ return stralloc("");
+ }
+
+ /* now format it */
+ if( strftime( formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
+ return( stralloc( formatted));
+ }
+ else {
+ rrd_set_error( "strftime failed");
+ return stralloc("");
+ }
+}
+
+char* includefile(long argc, const char **args){
+ char *buffer;
+ if (argc >= 1) {
+ const char* filename = args[0];
+ readfile(filename, &buffer, 0);
+ if (rrd_test_error()) {
+ char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE));
+ sprintf(err, "[ERROR: %s]",rrd_get_error());
+ rrd_clear_error();
+ return err;
+ } else {
+ return buffer;
+ }
+ }
+ else
+ {
+ return stralloc("[ERROR: No Inclue file defined]");
+ }
+}
+
+/* make a copy of buf and replace open/close brackets with '_' */
+char* rrdstrip(char *buf) {
+ char* p;
+ if (buf == NULL) {
+ return NULL;
+ }
+ /* make a copy of the buffer */
+ buf = stralloc(buf);
+ if (buf == NULL) {
+ return NULL;
+ }
+
+ p = buf;
+ while (*p) {
+ if (*p == '<' || *p == '>') {
+ *p = '_';
+ }
+ p++;
+ }
+ return buf;
+}
+
+char* cgigetq(long argc, const char **args){
+ if (argc>= 1){
+ char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg,args[0]));
+ char *buf2;
+ char *c,*d;
+ int qc=0;
+ if (buf==NULL) return NULL;
+
+ for(c=buf;*c != '\0';c++)
+ if (*c == '"') qc++;
+ if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
+ perror("Malloc Buffer");
+ exit(1);
+ };
+ c=buf;
+ d=buf2;
+ *(d++) = '"';
+ while(*c != '\0'){
+ if (*c == '"') {
+ *(d++) = '"';
+ *(d++) = '\'';
+ *(d++) = '"';
+ *(d++) = '\'';
+ }
+ *(d++) = *(c++);
+ }
+ *(d++) = '"';
+ *(d) = '\0';
+ free(buf);
+ return buf2;
+ }
+
+ return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
+}
+
+/* remove occurrences of .. this is a general measure to make
+ paths which came in via cgi do not go UP ... */
+
+char* cgigetqp(long argc, const char **args){
+ char* buf;
+ char* buf2;
+ char* p;
+ char* d;
+
+ if (argc < 1)
+ {
+ return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
+ }
+
+ buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
+ if (!buf)
+ {
+ return NULL;
+ }
+
+ buf2 = malloc(strlen(buf)+1);
+ if (!buf2)
+ {
+ perror("cgigetqp(): Malloc Path Buffer");
+ exit(1);
+ };
+
+ p = buf;
+ d = buf2;
+
+ while (*p)
+ {
+ /* prevent mallicious paths from entering the system */
+ if (p[0] == '.' && p[1] == '.')
+ {
+ p += 2;
+ *d++ = '_';
+ *d++ = '_';
+ }
+ else
+ {
+ *d++ = *p++;
+ }
+ }
+
+ *d = 0;
+ free(buf);
+
+ /* Make sure the path is relative, e.g. does not start with '/' */
+ p = buf2;
+ while ('/' == *p)
+ {
+ *p++ = '_';
+ }
+
+ return buf2;
+}
+
+
+char* cgiget(long argc, const char **args){
+ if (argc>= 1)
+ return rrdstrip(rrdcgiGetValue(rrdcgiArg,args[0]));
+ else
+ return stralloc("[ERROR: not enough arguments for RRD::CV]");
+}
+
+
+
+char* drawgraph(long argc, const char **args){
+ int i,xsize, ysize;
+ double ymin,ymax;
+ for(i=0;i<argc;i++)
+ if(strcmp(args[i],"--imginfo")==0 || strcmp(args[i],"-g")==0) break;
+ if(i==argc) {
+ args[argc++] = "--imginfo";
+ args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
+ }
+ calfree();
+ if( rrd_graph(argc+1, (char **) args-1, &calcpr, &xsize, &ysize,NULL,&ymin,&ymax) != -1 ) {
+ return stralloc(calcpr[0]);
+ } else {
+ if (rrd_test_error()) {
+ char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char));
+ sprintf(err, "[ERROR: %s]",rrd_get_error());
+ rrd_clear_error();
+ calfree();
+ return err;
+ }
+ }
+ return NULL;
+}
+
+char* drawprint(long argc, const char **args){
+ if (argc==1 && calcpr){
+ long i=0;
+ while (calcpr[i] != NULL) i++; /*determine number lines in calcpr*/
+ if (atol(args[0])<i-1)
+ return stralloc(calcpr[atol(args[0])+1]);
+ }
+ return stralloc("[ERROR: RRD::PRINT argument error]");
+}
+
+char* printtimelast(long argc, const char **args) {
+ time_t last;
+ struct tm tm_last;
+ char *buf;
+ if ( argc == 2 ) {
+ buf = malloc(255);
+ if (buf == NULL){
+ return stralloc("[ERROR: allocating strftime buffer]");
+ };
+ last = rrd_last(argc+1, (char **) args-1);
+ if (rrd_test_error()) {
+ char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char));
+ sprintf(err, "[ERROR: %s]",rrd_get_error());
+ rrd_clear_error();
+ return err;
+ }
+ tm_last = *localtime(&last);
+ strftime(buf,254,args[1],&tm_last);
+ return buf;
+ }
+ if ( argc < 2 ) {
+ return stralloc("[ERROR: too few arguments for RRD::TIME::LAST]");
+ }
+ return stralloc("[ERROR: not enough arguments for RRD::TIME::LAST]");
+}
+
+char* printtimenow(long argc, const char **args) {
+ time_t now = time(NULL);
+ struct tm tm_now;
+ char *buf;
+ if ( argc == 1 ) {
+ buf = malloc(255);
+ if (buf == NULL){
+ return stralloc("[ERROR: allocating strftime buffer]");
+ };
+ tm_now = *localtime(&now);
+ strftime(buf,254,args[0],&tm_now);
+ return buf;
+ }
+ if ( argc < 1 ) {
+ return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
+ }
+ return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
+}
+
+/* Scan buffer until an unescaped '>' arives.
+ * Update argument array with arguments found.
+ * Return end cursor where parsing stopped, or NULL in case of failure.
+ *
+ * FIXME:
+ * To allow nested constructs, we call rrd_expand_vars() for arguments
+ * that contain RRD::x directives. These introduce a small memory leak
+ * since we have to stralloc the arguments the way parse() works.
+ */
+char*
+scanargs(char *line, int *argument_count, char ***arguments)
+{
+ char *getP; /* read cursor */
+ char *putP; /* write cursor */
+ char Quote; /* type of quote if in quoted string, 0 otherwise */
+ int tagcount; /* open tag count */
+ int in_arg; /* if we currently are parsing an argument or not */
+ int argsz; /* argument array size */
+ int curarg_contains_rrd_directives;
+
+ /* local array of arguments while parsing */
+ int argc = 0;
+ char** argv;
+
+#ifdef DEBUG_PARSER
+ printf("<-- scanargs(%s) -->\n", line);
+#endif
+
+ *arguments = NULL;
+ *argument_count = 0;
+
+ /* create initial argument array of char pointers */
+ argsz = 32;
+ argv = (char **)malloc(argsz * sizeof(char *));
+ if (!argv) {
+ return NULL;
+ }
+
+ /* skip leading blanks */
+ while (isspace((int)*line)) {
+ line++;
+ }
+
+ getP = line;
+ putP = line;
+
+ Quote = 0;
+ in_arg = 0;
+ tagcount = 0;
+
+ curarg_contains_rrd_directives = 0;
+
+ /* start parsing 'line' for arguments */
+ while (*getP)
+ {
+ unsigned char c = *getP++;
+
+ if (c == '>' && !Quote && !tagcount) {
+ /* this is our closing tag, quit scanning */
+ break;
+ }
+
+ /* remove all special chars */
+ if (c < ' ') {
+ c = ' ';
+ }
+
+ switch (c)
+ {
+ case ' ':
+ if (Quote || tagcount) {
+ /* copy quoted/tagged (=RRD expanded) string */
+ *putP++ = c;
+ }
+ else if (in_arg)
+ {
+ /* end argument string */
+ *putP++ = 0;
+ in_arg = 0;
+ if (curarg_contains_rrd_directives) {
+ argv[argc-1] = rrd_expand_vars(stralloc(argv[argc-1]));
+ curarg_contains_rrd_directives = 0;
+ }
+ }
+ break;
+
+ case '"': /* Fall through */
+ case '\'':
+ if (Quote != 0) {
+ if (Quote == c) {
+ Quote = 0;
+ } else {
+ /* copy quoted string */
+ *putP++ = c;
+ }
+ } else {
+ if (!in_arg) {
+ /* reference start of argument string in argument array */
+ argv[argc++] = putP;
+ in_arg=1;
+ }
+ Quote = c;
+ }
+ break;
+
+ default:
+ if (!in_arg) {
+ /* start new argument */
+ argv[argc++] = putP;
+ in_arg = 1;
+ }
+ if (c == '>') {
+ if (tagcount) {
+ tagcount--;
+ }
+ }
+ if (c == '<') {
+ tagcount++;
+ if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
+ curarg_contains_rrd_directives = 1;
+ }
+ }
+ *putP++ = c;
+ break;
+ }
+
+ /* check if our argument array is still large enough */
+ if (argc == argsz) {
+ /* resize argument array */
+ argsz *= 2;
+ argv = rrd_realloc(argv, argsz * sizeof(char *));
+ if (*argv == NULL) {
+ return NULL;
+ }
+ }
+ }
+
+ /* terminate last argument found */
+ *putP = '\0';
+ if (curarg_contains_rrd_directives) {
+ argv[argc-1] = rrd_expand_vars(stralloc(argv[argc-1]));
+ }
+
+#ifdef DEBUG_PARSER
+ if (argc > 0) {
+ int n;
+ printf("<-- arguments found [%d]\n", argc);
+ for (n=0; n<argc; n++) {
+ printf("arg %02d: '%s'\n", n, argv[n]);
+ }
+ printf("-->\n");
+ } else {
+ printf("<!-- No arguments found -->\n");
+ }
+#endif
+
+ /* update caller's notion of the argument array and it's size */
+ *arguments = argv;
+ *argument_count = argc;
+
+ if (Quote) {
+ return NULL;
+ }
+
+ /* Return new scanning cursor:
+ pointer to char after closing bracket */
+ return getP;
+}
+
+
+/*
+ * Parse(): scan current portion of buffer for given tag.
+ * If found, parse tag arguments and call 'func' for it.
+ * The result of func is inserted at the current position
+ * in the buffer.
+ */
+int
+parse(
+ char **buf, /* buffer */
+ long i, /* offset in buffer */
+ char *tag, /* tag to handle */
+ char *(*func)(long , const char **) /* function to call for 'tag' */
+ )
+{
+ /* the name of the vairable ... */
+ char *val;
+ long valln;
+ char **args;
+ char *end;
+ long end_offset;
+ int argc;
+ size_t taglen = strlen(tag);
+
+ /* Current position in buffer should start with 'tag' */
+ if (strncmp((*buf)+i, tag, taglen) != 0) {
+ return 0;
+ }
+ /* .. and match exactly (a whitespace following 'tag') */
+ if (! isspace(*((*buf) + i + taglen)) ) {
+ return 0;
+ }
+
+#ifdef DEBUG_PARSER
+ printf("parse(): handling tag '%s'\n", tag);
+#endif
+
+ /* Scan for arguments following the tag;
+ scanargs() puts \0 into *buf ... so after scanargs it is probably
+ not a good time to use strlen on buf */
+ end = scanargs((*buf) + i + taglen, &argc, &args);
+ if (end)
+ {
+ /* got arguments, call function for 'tag' with arguments */
+ val = func(argc, (const char **) args);
+ free(args);
+ }
+ else
+ {
+ /* unable to parse arguments, undo 0-termination by scanargs */
+ for (; argc > 0; argc--) {
+ *((args[argc-1])-1) = ' ';
+ }
+
+ /* next call, try parsing at current offset +1 */
+ end = (*buf) + i + 1;
+
+ val = stralloc("[ERROR: Parsing Problem with the following text\n"
+ " Check original file. This may have been altered "
+ "by parsing.]\n\n");
+ }
+
+ /* remember offset where we have to continue parsing */
+ end_offset = end - (*buf);
+
+ valln = 0;
+ if (val) {
+ valln = strlen(val);
+ }
+
+ /* Optionally resize buffer to hold the replacement value:
+ Calculating the new length of the buffer is simple. add current
+ buffer pos (i) to length of string after replaced tag to length
+ of replacement string and add 1 for the final zero ... */
+ if (end - (*buf) < (i + valln)) {
+ /* make sure we do not shrink the mallocd block */
+ size_t newbufsize = i + strlen(end) + valln + 1;
+ *buf = rrd_realloc(*buf, newbufsize);
+
+ if (*buf == NULL) {
+ perror("Realoc buf:");
+ exit(1);
+ };
+ }
+
+ /* Update new end pointer:
+ make sure the 'end' pointer gets moved along with the
+ buf pointer when realloc moves memory ... */
+ end = (*buf) + end_offset;
+
+ /* splice the variable:
+ step 1. Shift pending data to make room for 'val' */
+ memmove((*buf) + i + valln, end, strlen(end) + 1);
+
+ /* step 2. Insert val */
+ if (val) {
+ memmove((*buf)+i, val, valln);
+ free(val);
+ }
+ return (valln > 0 ? valln-1: valln);
+}
+
+char *
+http_time(time_t *now) {
+ struct tm *tmptime;
+ static char buf[60];
+
+ tmptime=gmtime(now);
+ strftime(buf,sizeof(buf),"%a, %d %b %Y %H:%M:%S GMT",tmptime);
+ return(buf);
+}
+
+void rrdcgiHeader(void)
+{
+ if (rrdcgiType)
+ printf ("Content-type: %s\n", rrdcgiType);
+ else
+ printf ("Content-type: text/html\n");
+ if (rrdcgiHeaderString)
+ printf ("%s", rrdcgiHeaderString);
+ printf ("\n");
+}
+
+void rrdcgiDebug(int level, int where)
+{
+ if (level > 0)
+ rrdcgiDebugLevel = level;
+ else
+ rrdcgiDebugLevel = 0;
+ if (where)
+ rrdcgiDebugStderr = 0;
+ else
+ rrdcgiDebugStderr = 1;
+}
+
+char *rrdcgiDecodeString(char *text)
+{
+ char *cp, *xp;
+
+ for (cp=text,xp=text; *cp; cp++) {
+ if (*cp == '%') {
+ if (strchr("0123456789ABCDEFabcdef", *(cp+1))
+ && strchr("0123456789ABCDEFabcdef", *(cp+2))) {
+ if (islower(*(cp+1)))
+ *(cp+1) = toupper(*(cp+1));
+ if (islower(*(cp+2)))
+ *(cp+2) = toupper(*(cp+2));
+ *(xp) = (*(cp+1) >= 'A' ? *(cp+1) - 'A' + 10 : *(cp+1) - '0' ) * 16
+ + (*(cp+2) >= 'A' ? *(cp+2) - 'A' + 10 : *(cp+2) - '0');
+ xp++;cp+=2;
+ }
+ } else {
+ *(xp++) = *cp;
+ }
+ }
+ memset(xp, 0, cp-xp);
+ return text;
+}
+
+/* rrdcgiReadVariables()
+ *
+ * Read from stdin if no string is provided via CGI. Variables that
+ * doesn't have a value associated with it doesn't get stored.
+ */
+s_var **rrdcgiReadVariables(void)
+{
+ int length;
+ char *line = NULL;
+ int numargs;
+ char *cp, *ip, *esp, *sptr;
+ s_var **result;
+ int i, k, len;
+ char tmp[101];
+
+ cp = getenv("REQUEST_METHOD");
+ ip = getenv("CONTENT_LENGTH");
+
+ if (cp && !strcmp(cp, "POST")) {
+ if (ip) {
+ length = atoi(ip);
+ if ((line = (char *)malloc (length+2)) == NULL)
+ return NULL;
+ fgets(line, length+1, stdin);
+ } else
+ return NULL;
+ } else if (cp && !strcmp(cp, "GET")) {
+ esp = getenv("QUERY_STRING");
+ if (esp && strlen(esp)) {
+ if ((line = (char *)malloc (strlen(esp)+2)) == NULL)
+ return NULL;
+ sprintf (line, "%s", esp);
+ } else
+ return NULL;
+ } else {
+ length = 0;
+ printf ("(offline mode: enter name=value pairs on standard input)\n");
+ memset (tmp, 0, sizeof(tmp));
+ while((cp = fgets (tmp, 100, stdin)) != NULL) {
+ if (strlen(tmp)) {
+ if (tmp[strlen(tmp)-1] == '\n')
+ tmp[strlen(tmp)-1] = '&';
+ if (length) {
+ length += strlen(tmp);
+ len = (length+1) * sizeof(char);
+ if ((line = (char *)realloc (line, len)) == NULL)
+ return NULL;
+ strcat (line, tmp);
+ } else {
+ length = strlen(tmp);
+ len = (length+1) * sizeof(char);
+ if ((line = (char *)malloc (len)) == NULL)
+ return NULL;
+ memset (line, 0, len);
+ strcpy (line, tmp);
+ }
+ }
+ memset (tmp, 0, sizeof(tmp));
+ }
+ if (!line)
+ return NULL;
+ if (line[strlen(line)-1] == '&')
+ line[strlen(line)-1] = '\0';
+ }
+
+ /*
+ * From now on all cgi variables are stored in the variable line
+ * and look like foo=bar&foobar=barfoo&foofoo=
+ */
+
+ if (rrdcgiDebugLevel > 0) {
+ if (rrdcgiDebugStderr)
+ fprintf (stderr, "Received cgi input: %s\n", line);
+ else
+ printf ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n", line);
+ }
+
+ for (cp=line; *cp; cp++)
+ if (*cp == '+')
+ *cp = ' ';
+
+ if (strlen(line)) {
+ for (numargs=1,cp=line; *cp; cp++)
+ if (*cp == '&') numargs++;
+ } else
+ numargs = 0;
+ if (rrdcgiDebugLevel > 0) {
+ if (rrdcgiDebugStderr)
+ fprintf (stderr, "%d cgi variables found.\n", numargs);
+ else
+ printf ("%d cgi variables found.<br>\n", numargs);
+ }
+
+ len = (numargs+1) * sizeof(s_var *);
+ if ((result = (s_var **)malloc (len)) == NULL)
+ return NULL;
+ memset (result, 0, len);
+
+ cp = line;
+ i=0;
+ while (*cp) {
+ if ((ip = (char *)strchr(cp, '&')) != NULL) {
+ *ip = '\0';
+ }else
+ ip = cp + strlen(cp);
+
+ if ((esp=(char *)strchr(cp, '=')) == NULL) {
+ cp = ++ip;
+ continue;
+ }
+
+ if (!strlen(esp)) {
+ cp = ++ip;
+ continue;
+ }
+
+ if (i<numargs) {
+
+ /* try to find out if there's already such a variable */
+ for (k=0; k<i && (strncmp (result[k]->name,cp, esp-cp) || !(strlen (result[k]->name) == esp-cp)); k++);
+
+ if (k == i) { /* No such variable yet */
+ if ((result[i] = (s_var *)malloc(sizeof(s_var))) == NULL)
+ return NULL;
+ if ((result[i]->name = (char *)malloc((esp-cp+1) * sizeof(char))) == NULL)
+ return NULL;
+ memset (result[i]->name, 0, esp-cp+1);
+ strncpy(result[i]->name, cp, esp-cp);
+ cp = ++esp;
+ if ((result[i]->value = (char *)malloc((ip-esp+1) * sizeof(char))) == NULL)
+ return NULL;
+ memset (result[i]->value, 0, ip-esp+1);
+ strncpy(result[i]->value, cp, ip-esp);
+ result[i]->value = rrdcgiDecodeString(result[i]->value);
+ if (rrdcgiDebugLevel) {
+ if (rrdcgiDebugStderr)
+ fprintf (stderr, "%s: %s\n", result[i]->name, result[i]->value);
+ else
+ printf ("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n", result[i]->name, result[i]->value);
+ }
+ i++;
+ } else { /* There is already such a name, suppose a mutiple field */
+ cp = ++esp;
+ len = (strlen(result[k]->value)+(ip-esp)+2) * sizeof (char);
+ if ((sptr = (char *)malloc(len)) == NULL)
+ return NULL;
+ memset (sptr, 0, len);
+ sprintf (sptr, "%s\n", result[k]->value);
+ strncat(sptr, cp, ip-esp);
+ free(result[k]->value);
+ result[k]->value = rrdcgiDecodeString (sptr);
+ }
+ }
+ cp = ++ip;
+ }
+ return result;
+}
+
+/* rrdcgiInit()
+ *
+ * Read from stdin if no string is provided via CGI. Variables that
+ * doesn't have a value associated with it doesn't get stored.
+ */
+s_cgi *rrdcgiInit(void)
+{
+ s_cgi *res;
+ s_var **vars;
+
+ vars = rrdcgiReadVariables();
+
+ if (!vars)
+ return NULL;
+
+ if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
+ return NULL;
+ res->vars = vars;
+
+ return res;
+}
+
+char *rrdcgiGetValue(s_cgi *parms, const char *name)
+{
+ int i;
+
+ if (!parms || !parms->vars)
+ return NULL;
+ for (i=0;parms->vars[i]; i++)
+ if (!strcmp(name,parms->vars[i]->name)) {
+ if (rrdcgiDebugLevel > 0) {
+ if (rrdcgiDebugStderr)
+ fprintf (stderr, "%s found as %s\n", name, parms->vars[i]->value);
+ else
+ printf ("%s found as %s<br>\n", name, parms->vars[i]->value);
+ }
+ return parms->vars[i]->value;
+ }
+ if (rrdcgiDebugLevel) {
+ if (rrdcgiDebugStderr)
+ fprintf (stderr, "%s not found\n", name);
+ else
+ printf ("%s not found<br>\n", name);
+ }
+ return NULL;
+}
+
+void rrdcgiFreeList (char **list)
+{
+ int i;
+
+ for (i=0; list[i] != NULL; i++)
+ free (list[i]);
+ free (list);
+}
+
+void rrdcgiFree (s_cgi *parms)
+{
+ int i;
+
+ if (!parms)
+ return;
+ if (parms->vars) {
+ for (i=0;parms->vars[i]; i++) {
+ if (parms->vars[i]->name)
+ free (parms->vars[i]->name);
+ if (parms->vars[i]->value)
+ free (parms->vars[i]->value);
+ free (parms->vars[i]);
+ }
+ free (parms->vars);
+ }
+ free (parms);
+
+ if (rrdcgiHeaderString) {
+ free (rrdcgiHeaderString);
+ rrdcgiHeaderString = NULL;
+ }
+ if (rrdcgiType) {
+ free (rrdcgiType);
+ rrdcgiType = NULL;
+ }
+}
+
diff --git a/program/src/rrd_create.c b/program/src/rrd_create.c
--- /dev/null
+++ b/program/src/rrd_create.c
@@ -0,0 +1,714 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_create.c creates new rrds
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include "rrd_hw.h"
+
+#include "rrd_is_thread_safe.h"
+
+unsigned long FnvHash(const char *str);
+int create_hw_contingent_rras(rrd_t *rrd, unsigned short period, unsigned long hashed_name);
+void parseGENERIC_DS(const char *def,rrd_t *rrd, int ds_idx);
+
+int
+rrd_create(int argc, char **argv)
+{
+ time_t last_up = time(NULL)-10;
+ unsigned long pdp_step = 300;
+ struct rrd_time_value last_up_tv;
+ char *parsetime_error = NULL;
+ long long_tmp;
+ int rc;
+ optind = 0; opterr = 0; /* initialize getopt */
+
+ while (1){
+ static struct option long_options[] =
+ {
+ {"start", required_argument, 0, 'b'},
+ {"step", required_argument,0,'s'},
+ {0,0,0,0}
+ };
+ int option_index = 0;
+ int opt;
+ opt = getopt_long(argc, argv, "b:s:",
+ long_options, &option_index);
+
+ if (opt == EOF)
+ break;
+
+ switch(opt) {
+ case 'b':
+ if ((parsetime_error = parsetime(optarg, &last_up_tv))) {
+ rrd_set_error("start time: %s", parsetime_error );
+ return(-1);
+ }
+ if (last_up_tv.type == RELATIVE_TO_END_TIME ||
+ last_up_tv.type == RELATIVE_TO_START_TIME) {
+ rrd_set_error("specifying time relative to the 'start' "
+ "or 'end' makes no sense here");
+ return(-1);
+ }
+
+ last_up = mktime(&last_up_tv.tm) + last_up_tv.offset;
+
+ if (last_up < 3600*24*365*10){
+ rrd_set_error("the first entry to the RRD should be after 1980");
+ return(-1);
+ }
+ break;
+
+ case 's':
+ long_tmp = atol(optarg);
+ if (long_tmp < 1){
+ rrd_set_error("step size should be no less than one second");
+ return(-1);
+ }
+ pdp_step = long_tmp;
+ break;
+
+ case '?':
+ if (optopt != 0)
+ rrd_set_error("unknown option '%c'", optopt);
+ else
+ rrd_set_error("unknown option '%s'",argv[optind-1]);
+ return(-1);
+ }
+ }
+ if (optind == argc) {
+ rrd_set_error("what is the name of the rrd file you want to create?");
+ return -1;
+ }
+ rc = rrd_create_r(argv[optind],
+ pdp_step, last_up,
+ argc - optind - 1, (const char **)(argv + optind + 1));
+
+ return rc;
+}
+
+/* #define DEBUG */
+int
+rrd_create_r(const char *filename,
+ unsigned long pdp_step, time_t last_up,
+ int argc, const char **argv)
+{
+ rrd_t rrd;
+ long i;
+ int offset;
+ char *token;
+ char dummychar1[2], dummychar2[2];
+ unsigned short token_idx, error_flag, period=0;
+ unsigned long hashed_name;
+
+ /* init rrd clean */
+ rrd_init(&rrd);
+ /* static header */
+ if((rrd.stat_head = calloc(1,sizeof(stat_head_t)))==NULL){
+ rrd_set_error("allocating rrd.stat_head");
+ rrd_free(&rrd);
+ return(-1);
+ }
+
+ /* live header */
+ if((rrd.live_head = calloc(1,sizeof(live_head_t)))==NULL){
+ rrd_set_error("allocating rrd.live_head");
+ rrd_free(&rrd);
+ return(-1);
+ }
+
+ /* set some defaults */
+ strcpy(rrd.stat_head->cookie,RRD_COOKIE);
+ strcpy(rrd.stat_head->version,RRD_VERSION);
+ rrd.stat_head->float_cookie = FLOAT_COOKIE;
+ rrd.stat_head->ds_cnt = 0; /* this will be adjusted later */
+ rrd.stat_head->rra_cnt = 0; /* ditto */
+ rrd.stat_head->pdp_step = pdp_step; /* 5 minute default */
+
+ /* a default value */
+ rrd.ds_def = NULL;
+ rrd.rra_def = NULL;
+
+ rrd.live_head->last_up = last_up;
+
+ /* optind points to the first non-option command line arg,
+ * in this case, the file name. */
+ /* Compute the FNV hash value (used by SEASONAL and DEVSEASONAL
+ * arrays. */
+ hashed_name = FnvHash(filename);
+ for(i=0;i<argc;i++){
+ unsigned int ii;
+ if (strncmp(argv[i],"DS:",3)==0){
+ size_t old_size = sizeof(ds_def_t)*(rrd.stat_head->ds_cnt);
+ if((rrd.ds_def = rrd_realloc(rrd.ds_def,
+ old_size+sizeof(ds_def_t)))==NULL){
+ rrd_set_error("allocating rrd.ds_def");
+ rrd_free(&rrd);
+ return(-1);
+ }
+ memset(&rrd.ds_def[rrd.stat_head->ds_cnt], 0, sizeof(ds_def_t));
+ /* extract the name and type */
+ switch (sscanf(&argv[i][3],
+ DS_NAM_FMT "%1[:]" DST_FMT "%1[:]%n",
+ rrd.ds_def[rrd.stat_head->ds_cnt].ds_nam,
+ dummychar1,
+ rrd.ds_def[rrd.stat_head->ds_cnt].dst,
+ dummychar2,
+ &offset)) {
+ case 0:
+ case 1: rrd_set_error("Invalid DS name"); break;
+ case 2:
+ case 3: rrd_set_error("Invalid DS type"); break;
+ case 4: /* (%n may or may not be counted) */
+ case 5: /* check for duplicate datasource names */
+ for (ii=0;ii<rrd.stat_head->ds_cnt;ii++)
+ if(strcmp(rrd.ds_def[rrd.stat_head->ds_cnt].ds_nam,
+ rrd.ds_def[ii].ds_nam) == 0)
+ rrd_set_error("Duplicate DS name: %s",
+ rrd.ds_def[ii].ds_nam);
+ /* DS_type may be valid or not. Checked later */
+ break;
+ default: rrd_set_error("invalid DS format");
+ }
+ if (rrd_test_error()) {
+ rrd_free(&rrd);
+ return -1;
+ }
+
+ /* parse the remainder of the arguments */
+ switch(dst_conv(rrd.ds_def[rrd.stat_head->ds_cnt].dst))
+ {
+ case DST_COUNTER:
+ case DST_ABSOLUTE:
+ case DST_GAUGE:
+ case DST_DERIVE:
+ parseGENERIC_DS(&argv[i][offset+3],&rrd, rrd.stat_head->ds_cnt);
+ break;
+ case DST_CDEF:
+ parseCDEF_DS(&argv[i][offset+3],&rrd, rrd.stat_head->ds_cnt);
+ break;
+ default:
+ rrd_set_error("invalid DS type specified");
+ break;
+ }
+
+ if (rrd_test_error()) {
+ rrd_free(&rrd);
+ return -1;
+ }
+ rrd.stat_head -> ds_cnt++;
+ } else if (strncmp(argv[i],"RRA:",4)==0){
+ char *argvcopy;
+ char *tokptr;
+ size_t old_size = sizeof(rra_def_t)*(rrd.stat_head->rra_cnt);
+ if((rrd.rra_def = rrd_realloc(rrd.rra_def,
+ old_size+sizeof(rra_def_t)))==NULL)
+ {
+ rrd_set_error("allocating rrd.rra_def");
+ rrd_free(&rrd);
+ return(-1);
+ }
+ memset(&rrd.rra_def[rrd.stat_head->rra_cnt], 0, sizeof(rra_def_t));
+
+ argvcopy = strdup(argv[i]);
+ token = strtok_r(&argvcopy[4],":", &tokptr);
+ token_idx = error_flag = 0;
+ while (token != NULL)
+ {
+ switch(token_idx)
+ {
+ case 0:
+ if (sscanf(token,CF_NAM_FMT,
+ rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam) != 1)
+ rrd_set_error("Failed to parse CF name");
+ switch(cf_conv(rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam))
+ {
+ case CF_HWPREDICT:
+ /* initialize some parameters */
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_hw_alpha].u_val = 0.1;
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_hw_beta].u_val = 1.0/288;
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_dependent_rra_idx].u_cnt =
+ rrd.stat_head -> rra_cnt;
+ break;
+ case CF_DEVSEASONAL:
+ case CF_SEASONAL:
+ /* initialize some parameters */
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_seasonal_gamma].u_val = 0.1;
+ /* fall through */
+ case CF_DEVPREDICT:
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_dependent_rra_idx].u_cnt = -1;
+ break;
+ case CF_FAILURES:
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_delta_pos].u_val = 2.0;
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_delta_neg].u_val = 2.0;
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_window_len].u_cnt = 3;
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_failure_threshold].u_cnt = 2;
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_dependent_rra_idx].u_cnt = -1;
+ break;
+ /* invalid consolidation function */
+ case -1:
+ rrd_set_error("Unrecognized consolidation function %s",
+ rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam);
+ default:
+ break;
+ }
+ /* default: 1 pdp per cdp */
+ rrd.rra_def[rrd.stat_head->rra_cnt].pdp_cnt = 1;
+ break;
+ case 1:
+ switch(cf_conv(rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam))
+ {
+ case CF_HWPREDICT:
+ case CF_DEVSEASONAL:
+ case CF_SEASONAL:
+ case CF_DEVPREDICT:
+ case CF_FAILURES:
+ rrd.rra_def[rrd.stat_head->rra_cnt].row_cnt = atoi(token);
+ break;
+ default:
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_cdp_xff_val].u_val = atof(token);
+ if (rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_cdp_xff_val].u_val<0.0 ||
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_cdp_xff_val].u_val>=1.0)
+ rrd_set_error("Invalid xff: must be between 0 and 1");
+ break;
+ }
+ break;
+ case 2:
+ switch(cf_conv(rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam))
+ {
+ case CF_HWPREDICT:
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_hw_alpha].u_val = atof(token);
+ if (atof(token) <= 0.0 || atof(token) >= 1.0)
+ rrd_set_error("Invalid alpha: must be between 0 and 1");
+ break;
+ case CF_DEVSEASONAL:
+ case CF_SEASONAL:
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_seasonal_gamma].u_val =
+ atof(token);
+ if (atof(token) <= 0.0 || atof(token) >= 1.0)
+ rrd_set_error("Invalid gamma: must be between 0 and 1");
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_seasonal_smooth_idx].u_cnt
+ = hashed_name % rrd.rra_def[rrd.stat_head->rra_cnt].row_cnt;
+ break;
+ case CF_FAILURES:
+ /* specifies the # of violations that constitutes the failure threshold */
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_failure_threshold].u_cnt =
+ atoi(token);
+ if (atoi(token) < 1 || atoi(token) > MAX_FAILURES_WINDOW_LEN)
+ rrd_set_error("Failure threshold is out of range %d, %d",1,
+ MAX_FAILURES_WINDOW_LEN);
+ break;
+ case CF_DEVPREDICT:
+ /* specifies the index (1-based) of CF_DEVSEASONAL array
+ * associated with this CF_DEVPREDICT array. */
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_dependent_rra_idx].u_cnt =
+ atoi(token) - 1;
+ break;
+ default:
+ rrd.rra_def[rrd.stat_head->rra_cnt].pdp_cnt = atoi(token);
+ break;
+ }
+ break;
+ case 3:
+ switch(cf_conv(rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam))
+ {
+ case CF_HWPREDICT:
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_hw_beta].u_val = atof(token);
+ if (atof(token) < 0.0 || atof(token) > 1.0)
+ rrd_set_error("Invalid beta: must be between 0 and 1");
+ break;
+ case CF_DEVSEASONAL:
+ case CF_SEASONAL:
+ /* specifies the index (1-based) of CF_HWPREDICT array
+ * associated with this CF_DEVSEASONAL or CF_SEASONAL array.
+ * */
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_dependent_rra_idx].u_cnt =
+ atoi(token) - 1;
+ break;
+ case CF_FAILURES:
+ /* specifies the window length */
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_window_len].u_cnt =
+ atoi(token);
+ if (atoi(token) < 1 || atoi(token) > MAX_FAILURES_WINDOW_LEN)
+ rrd_set_error("Window length is out of range %d, %d",1,
+ MAX_FAILURES_WINDOW_LEN);
+ /* verify that window length exceeds the failure threshold */
+ if (rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_window_len].u_cnt <
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_failure_threshold].u_cnt)
+ rrd_set_error("Window length is shorter than the failure threshold");
+ break;
+ case CF_DEVPREDICT:
+ /* shouldn't be any more arguments */
+ rrd_set_error("Unexpected extra argument for consolidation function DEVPREDICT");
+ break;
+ default:
+ rrd.rra_def[rrd.stat_head->rra_cnt].row_cnt = atoi(token);
+ break;
+ }
+ break;
+ case 4:
+ switch(cf_conv(rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam))
+ {
+ case CF_FAILURES:
+ /* specifies the index (1-based) of CF_DEVSEASONAL array
+ * associated with this CF_DEVFAILURES array. */
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_dependent_rra_idx].u_cnt =
+ atoi(token) - 1;
+ break;
+ case CF_HWPREDICT:
+ /* length of the associated CF_SEASONAL and CF_DEVSEASONAL arrays. */
+ period = atoi(token);
+ if (period > rrd.rra_def[rrd.stat_head->rra_cnt].row_cnt)
+ rrd_set_error("Length of seasonal cycle exceeds length of HW prediction array");
+ break;
+ default:
+ /* shouldn't be any more arguments */
+ rrd_set_error("Unexpected extra argument for consolidation function %s",
+ rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam);
+ break;
+ }
+ break;
+ case 5:
+ /* If we are here, this must be a CF_HWPREDICT RRA.
+ * Specifies the index (1-based) of CF_SEASONAL array
+ * associated with this CF_HWPREDICT array. If this argument
+ * is missing, then the CF_SEASONAL, CF_DEVSEASONAL, CF_DEVPREDICT,
+ * CF_FAILURES.
+ * arrays are created automatically. */
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_dependent_rra_idx].u_cnt =
+ atoi(token) - 1;
+ break;
+ default:
+ /* should never get here */
+ rrd_set_error("Unknown error");
+ break;
+ } /* end switch */
+ if (rrd_test_error())
+ {
+ /* all errors are unrecoverable */
+ free(argvcopy);
+ rrd_free(&rrd);
+ return (-1);
+ }
+ token = strtok_r(NULL,":", &tokptr);
+ token_idx++;
+ } /* end while */
+ free(argvcopy);
+#ifdef DEBUG
+ fprintf(stderr,"Creating RRA CF: %s, dep idx %lu, current idx %lu\n",
+ rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam,
+ rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_dependent_rra_idx].u_cnt,
+ rrd.stat_head -> rra_cnt);
+#endif
+ /* should we create CF_SEASONAL, CF_DEVSEASONAL, and CF_DEVPREDICT? */
+ if (cf_conv(rrd.rra_def[rrd.stat_head->rra_cnt].cf_nam) == CF_HWPREDICT
+ && rrd.rra_def[rrd.stat_head->rra_cnt].par[RRA_dependent_rra_idx].u_cnt
+ == rrd.stat_head -> rra_cnt)
+ {
+#ifdef DEBUG
+ fprintf(stderr,"Creating HW contingent RRAs\n");
+#endif
+ if (create_hw_contingent_rras(&rrd,period,hashed_name) == -1) {
+ rrd_set_error("creating contingent RRA");
+ rrd_free(&rrd);
+ return -1;
+ }
+ }
+ rrd.stat_head->rra_cnt++;
+ } else {
+ rrd_set_error("can't parse argument '%s'",argv[i]);
+ rrd_free(&rrd);
+ return -1;
+ }
+ }
+
+
+ if (rrd.stat_head->rra_cnt < 1){
+ rrd_set_error("you must define at least one Round Robin Archive");
+ rrd_free(&rrd);
+ return(-1);
+ }
+
+ if (rrd.stat_head->ds_cnt < 1){
+ rrd_set_error("you must define at least one Data Source");
+ rrd_free(&rrd);
+ return(-1);
+ }
+ return rrd_create_fn(filename, &rrd);
+}
+
+void parseGENERIC_DS(const char *def,rrd_t *rrd, int ds_idx)
+{
+ char minstr[DS_NAM_SIZE], maxstr[DS_NAM_SIZE];
+ /*
+ int temp;
+
+ temp = sscanf(def,"%lu:%18[^:]:%18[^:]",
+ &(rrd -> ds_def[ds_idx].par[DS_mrhb_cnt].u_cnt),
+ minstr,maxstr);
+ */
+ if (sscanf(def,"%lu:%18[^:]:%18[^:]",
+ &(rrd -> ds_def[ds_idx].par[DS_mrhb_cnt].u_cnt),
+ minstr,maxstr) == 3)
+ {
+ if (minstr[0] == 'U' && minstr[1] == 0)
+ rrd -> ds_def[ds_idx].par[DS_min_val].u_val = DNAN;
+ else
+ rrd -> ds_def[ds_idx].par[DS_min_val].u_val = atof(minstr);
+
+ if (maxstr[0] == 'U' && maxstr[1] == 0)
+ rrd -> ds_def[ds_idx].par[DS_max_val].u_val = DNAN;
+ else
+ rrd -> ds_def[ds_idx].par[DS_max_val].u_val = atof(maxstr);
+
+ if (! isnan(rrd -> ds_def[ds_idx].par[DS_min_val].u_val) &&
+ ! isnan(rrd -> ds_def[ds_idx].par[DS_max_val].u_val) &&
+ rrd -> ds_def[ds_idx].par[DS_min_val].u_val
+ >= rrd -> ds_def[ds_idx].par[DS_max_val].u_val ) {
+ rrd_set_error("min must be less than max in DS definition");
+ return;
+ }
+ } else {
+ rrd_set_error("failed to parse data source %s", def);
+ }
+}
+
+/* Create the CF_DEVPREDICT, CF_DEVSEASONAL, CF_SEASONAL, and CF_FAILURES RRAs
+ * associated with a CF_HWPREDICT RRA. */
+int
+create_hw_contingent_rras(rrd_t *rrd, unsigned short period, unsigned long hashed_name)
+{
+ size_t old_size;
+ rra_def_t* current_rra;
+
+ /* save index to CF_HWPREDICT */
+ unsigned long hw_index = rrd -> stat_head -> rra_cnt;
+ /* advance the pointer */
+ (rrd -> stat_head -> rra_cnt)++;
+ /* allocate the memory for the 4 contingent RRAs */
+ old_size = sizeof(rra_def_t)*(rrd -> stat_head->rra_cnt);
+ if ((rrd -> rra_def = rrd_realloc(rrd -> rra_def,
+ old_size+4*sizeof(rra_def_t)))==NULL)
+ {
+ rrd_set_error("allocating rrd.rra_def");
+ return(-1);
+ }
+ /* clear memory */
+ memset(&(rrd -> rra_def[rrd -> stat_head->rra_cnt]), 0, 4*sizeof(rra_def_t));
+
+ /* create the CF_SEASONAL RRA */
+ current_rra = &(rrd -> rra_def[rrd -> stat_head -> rra_cnt]);
+ strcpy(current_rra -> cf_nam,"SEASONAL");
+ current_rra -> row_cnt = period;
+ current_rra -> par[RRA_seasonal_smooth_idx].u_cnt = hashed_name % period;
+ current_rra -> pdp_cnt = 1;
+ current_rra -> par[RRA_seasonal_gamma].u_val =
+ rrd -> rra_def[hw_index].par[RRA_hw_alpha].u_val;
+ current_rra -> par[RRA_dependent_rra_idx].u_cnt = hw_index;
+ rrd -> rra_def[hw_index].par[RRA_dependent_rra_idx].u_cnt = rrd -> stat_head -> rra_cnt;
+
+ /* create the CF_DEVSEASONAL RRA */
+ (rrd -> stat_head -> rra_cnt)++;
+ current_rra = &(rrd -> rra_def[rrd -> stat_head -> rra_cnt]);
+ strcpy(current_rra -> cf_nam,"DEVSEASONAL");
+ current_rra -> row_cnt = period;
+ current_rra -> par[RRA_seasonal_smooth_idx].u_cnt = hashed_name % period;
+ current_rra -> pdp_cnt = 1;
+ current_rra -> par[RRA_seasonal_gamma].u_val =
+ rrd -> rra_def[hw_index].par[RRA_hw_alpha].u_val;
+ current_rra -> par[RRA_dependent_rra_idx].u_cnt = hw_index;
+
+ /* create the CF_DEVPREDICT RRA */
+ (rrd -> stat_head -> rra_cnt)++;
+ current_rra = &(rrd -> rra_def[rrd -> stat_head -> rra_cnt]);
+ strcpy(current_rra -> cf_nam,"DEVPREDICT");
+ current_rra -> row_cnt = (rrd -> rra_def[hw_index]).row_cnt;
+ current_rra -> pdp_cnt = 1;
+ current_rra -> par[RRA_dependent_rra_idx].u_cnt
+ = hw_index + 2; /* DEVSEASONAL */
+
+ /* create the CF_FAILURES RRA */
+ (rrd -> stat_head -> rra_cnt)++;
+ current_rra = &(rrd -> rra_def[rrd -> stat_head -> rra_cnt]);
+ strcpy(current_rra -> cf_nam,"FAILURES");
+ current_rra -> row_cnt = period;
+ current_rra -> pdp_cnt = 1;
+ current_rra -> par[RRA_delta_pos].u_val = 2.0;
+ current_rra -> par[RRA_delta_neg].u_val = 2.0;
+ current_rra -> par[RRA_failure_threshold].u_cnt = 7;
+ current_rra -> par[RRA_window_len].u_cnt = 9;
+ current_rra -> par[RRA_dependent_rra_idx].u_cnt =
+ hw_index + 2; /* DEVSEASONAL */
+ return 0;
+}
+
+/* create and empty rrd file according to the specs given */
+
+int
+rrd_create_fn(const char *file_name, rrd_t *rrd)
+{
+ unsigned long i,ii;
+ FILE *rrd_file;
+ rrd_value_t *unknown;
+ int unkn_cnt;
+
+ long rrd_head_size;
+
+ if ((rrd_file = fopen(file_name,"wb")) == NULL ) {
+ rrd_set_error("creating '%s': %s",file_name, rrd_strerror(errno));
+ free(rrd->stat_head);
+ rrd->stat_head = NULL;
+ free(rrd->ds_def);
+ rrd->ds_def = NULL;
+ free(rrd->rra_def);
+ rrd->rra_def = NULL;
+ return(-1);
+ }
+
+ fwrite(rrd->stat_head,
+ sizeof(stat_head_t), 1, rrd_file);
+
+ fwrite(rrd->ds_def,
+ sizeof(ds_def_t), rrd->stat_head->ds_cnt, rrd_file);
+
+ fwrite(rrd->rra_def,
+ sizeof(rra_def_t), rrd->stat_head->rra_cnt, rrd_file);
+
+ fwrite(rrd->live_head,
+ sizeof(live_head_t),1, rrd_file);
+
+ if((rrd->pdp_prep = calloc(1,sizeof(pdp_prep_t))) == NULL){
+ rrd_set_error("allocating pdp_prep");
+ rrd_free(rrd);
+ fclose(rrd_file);
+ return(-1);
+ }
+
+ strcpy(rrd->pdp_prep->last_ds,"UNKN");
+
+ rrd->pdp_prep->scratch[PDP_val].u_val = 0.0;
+ rrd->pdp_prep->scratch[PDP_unkn_sec_cnt].u_cnt =
+ rrd->live_head->last_up % rrd->stat_head->pdp_step;
+
+ for(i=0; i < rrd->stat_head->ds_cnt; i++)
+ fwrite( rrd->pdp_prep,sizeof(pdp_prep_t),1,rrd_file);
+
+ if((rrd->cdp_prep = calloc(1,sizeof(cdp_prep_t))) == NULL){
+ rrd_set_error("allocating cdp_prep");
+ rrd_free(rrd);
+ fclose(rrd_file);
+ return(-1);
+ }
+
+
+ for(i=0; i < rrd->stat_head->rra_cnt; i++) {
+ switch (cf_conv(rrd->rra_def[i].cf_nam))
+ {
+ case CF_HWPREDICT:
+ init_hwpredict_cdp(rrd->cdp_prep);
+ break;
+ case CF_SEASONAL:
+ case CF_DEVSEASONAL:
+ init_seasonal_cdp(rrd->cdp_prep);
+ break;
+ case CF_FAILURES:
+ /* initialize violation history to 0 */
+ for (ii = 0; ii < MAX_CDP_PAR_EN; ii++)
+ {
+ /* We can zero everything out, by setting u_val to the
+ * NULL address. Each array entry in scratch is 8 bytes
+ * (a double), but u_cnt only accessed 4 bytes (long) */
+ rrd->cdp_prep->scratch[ii].u_val = 0.0;
+ }
+ break;
+ default:
+ /* can not be zero because we don't know anything ... */
+ rrd->cdp_prep->scratch[CDP_val].u_val = DNAN;
+ /* startup missing pdp count */
+ rrd->cdp_prep->scratch[CDP_unkn_pdp_cnt].u_cnt =
+ ((rrd->live_head->last_up -
+ rrd->pdp_prep->scratch[PDP_unkn_sec_cnt].u_cnt)
+ % (rrd->stat_head->pdp_step
+ * rrd->rra_def[i].pdp_cnt)) / rrd->stat_head->pdp_step;
+ break;
+ }
+
+ for(ii=0; ii < rrd->stat_head->ds_cnt; ii++)
+ {
+ fwrite( rrd->cdp_prep,sizeof(cdp_prep_t),1,rrd_file);
+ }
+ }
+
+ /* now, we must make sure that the rest of the rrd
+ struct is properly initialized */
+
+ if((rrd->rra_ptr = calloc(1,sizeof(rra_ptr_t))) == NULL) {
+ rrd_set_error("allocating rra_ptr");
+ rrd_free(rrd);
+ fclose(rrd_file);
+ return(-1);
+ }
+
+ /* changed this initialization to be consistent with
+ * rrd_restore. With the old value (0), the first update
+ * would occur for cur_row = 1 because rrd_update increments
+ * the pointer a priori. */
+ for (i=0; i < rrd->stat_head->rra_cnt; i++)
+ {
+ rrd->rra_ptr->cur_row = rrd->rra_def[i].row_cnt - 1;
+ fwrite( rrd->rra_ptr, sizeof(rra_ptr_t),1,rrd_file);
+ }
+ rrd_head_size = ftell(rrd_file);
+
+ /* write the empty data area */
+ if ((unknown = (rrd_value_t *)malloc(512 * sizeof(rrd_value_t))) == NULL) {
+ rrd_set_error("allocating unknown");
+ rrd_free(rrd);
+ fclose(rrd_file);
+ return(-1);
+ }
+ for (i = 0; i < 512; ++i)
+ unknown[i] = DNAN;
+
+ unkn_cnt = 0;
+ for (i = 0; i < rrd->stat_head->rra_cnt; i++)
+ unkn_cnt += rrd->stat_head->ds_cnt * rrd->rra_def[i].row_cnt;
+
+ while (unkn_cnt > 0) {
+ fwrite(unknown, sizeof(rrd_value_t), min(unkn_cnt, 512), rrd_file);
+ unkn_cnt -= 512;
+ }
+ free(unknown);
+
+ /* lets see if we had an error */
+ if(ferror(rrd_file)){
+ rrd_set_error("a file error occurred while creating '%s'",file_name);
+ fclose(rrd_file);
+ rrd_free(rrd);
+ return(-1);
+ }
+
+#ifdef HAVE_POSIX_FADVISE
+ /* this file is not going to be read again any time
+ soon, so we drop everything except the header portion from
+ the buffer cache. for this to work, we have to fdsync the file
+ first though. This will not be all that fast, but 'good' data
+ like other rrdfiles headers will stay in cache. Now this only works if creating
+ a single rrd file is not too large, but I assume this should not be the case
+ in general. Otherwhise we would have to sync and release while writing all
+ the unknown data. */
+ fflush(rrd_file);
+ fdatasync(fileno(rrd_file));
+ if (0 != posix_fadvise(fileno(rrd_file), rrd_head_size, 0, POSIX_FADV_DONTNEED)) {
+ rrd_set_error("setting POSIX_FADV_DONTNEED on '%s': %s",file_name, rrd_strerror(errno));
+ fclose(rrd_file);
+ return(-1);
+ }
+#endif
+
+ fclose(rrd_file);
+ rrd_free(rrd);
+ return (0);
+}
diff --git a/program/src/rrd_datalang.c b/program/src/rrd_datalang.c
--- /dev/null
@@ -0,0 +1,34 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_datalang A system for passing named and typed parameters between
+ * the different parts of rrdtool
+ *
+ * In rrdtool thre are a number of places where large and complex
+ * data structures have to be passed from one function to another
+ * eg when rrd_info returns its findings, but also when a function like
+ * rrd_graph get called.
+ *
+ * At the moment function calling is done with an argc/argv type interface
+ * it's special property is that all data is passed as strings, which can lead
+ * to unnecessary conversions being performed when rrdtool functions are called
+ * from a typed language
+ *
+ * Data returns from functions is not standardized at all, which is
+ * efficient in the sense that the data return interface can be tailord to
+ * the specific needs of the function at hand, but it also leads to
+ * increassed probability for implementation errors as things have to be
+ * reinvented for each function. Also adding new functions into all the
+ * language bindings is quite cumbersom.
+ *
+ * Therefore I want to develop a standardized interface for passing named
+ * and typed data into functions and for returning data from functions to
+ * their callers. I am thinking about working of the code in rrd_info.c ...
+ *
+ * Does anyone have experiance in this field or any pointers to read up on
+ * related work ? Or maybe even an existing library for this ?
+ *
+ * Cheers
+ * tobi / 2001-03-10
+ *
+ *****************************************************************************/
diff --git a/program/src/rrd_diff.c b/program/src/rrd_diff.c
--- /dev/null
+++ b/program/src/rrd_diff.c
@@ -0,0 +1,118 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ * This code is stolen from rateup (mrtg-2.x) by Dave Rand
+ *****************************************************************************
+ * diff calculate the difference between two very long integers available as
+ * strings
+ *****************************************************************************
+ * $Id$
+ * $Log$
+ * Revision 1.4 2003/03/10 00:30:34 oetiker
+ * handle cases with two negative numbers
+ * -- Sasha Mikheev <sasha@avalon-net.co.il>
+ *
+ * Revision 1.3 2002/04/01 18:31:22 oetiker
+ * "!" takes a higher preference than "||" this means rrd_update N:: would
+ * segfault -- Oliver Cook <ollie@uk.clara.net>
+ *
+ * Revision 1.2 2002/02/01 20:34:49 oetiker
+ * fixed version number and date/time
+ *
+ * Revision 1.1.1.1 2001/02/25 22:25:05 oetiker
+ * checkin
+ *
+ * Revision 1.1 1998/10/08 18:21:45 oetiker
+ * Initial revision
+ *
+ * Revision 1.3 1998/02/06 21:10:52 oetiker
+ * removed max define .. it is now in rrd_tool.h
+ *
+ * Revision 1.2 1997/12/07 20:38:03 oetiker
+ * ansified
+ *
+ * Revision 1.1 1997/11/28 23:31:59 oetiker
+ * Initial revision
+ *
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+
+double
+rrd_diff(char *a, char *b)
+{
+ char res[LAST_DS_LEN+1], *a1, *b1, *r1, *fix;
+ int c,x,m;
+ char a_neg=0, b_neg=0;
+ double result;
+
+ while (!(isdigit((int)*a) || *a==0)) {
+ if(*a=='-')
+ a_neg = 1;
+ a++;
+ }
+ fix=a;
+ while (isdigit((int)*fix))
+ fix++;
+ *fix = 0; /* maybe there is some non digit data in the string */
+ while (!(isdigit((int)*b) || *b==0)) {
+ if(*b=='-')
+ b_neg = 1;
+ b++;
+ }
+ fix=b;
+ while (isdigit((int)*fix))
+ fix++;
+ *fix = 0; /* maybe there is some non digit data in the string */
+ if(!isdigit((int)*a) || !isdigit((int)*b))
+ return DNAN;
+ if(a_neg+b_neg == 1) /* can not handle numbers with different signs yet */
+ return DNAN;
+ a1 = &a[strlen(a)-1];
+ m = max(strlen(a),strlen(b));
+ if (m > LAST_DS_LEN) return DNAN; /* result string too short */
+
+ r1 = &res[m+1];
+ for (b1 = res;b1 <= r1; b1++) *b1 = ' ';
+ b1 = &b[strlen(b)-1];
+ r1[1] = 0; /* Null terminate result */
+ c = 0;
+ for (x=0; x<m; x++) {
+ if (a1 >= a && b1 >= b) {
+ *r1 = ((*a1 - c) - *b1) + '0';
+ } else if (a1 >= a) {
+ *r1 = (*a1 - c);
+ } else {
+ *r1 = ('0' - *b1 - c) + '0';
+ }
+ if (*r1 < '0') {
+ *r1 += 10;
+ c=1;
+ } else
+ if (*r1 > '9') { /* 0 - 10 */
+ *r1 -= 10;
+ c=1;
+ } else {
+ c=0;
+ }
+ a1--;b1--;r1--;
+ }
+ if (c) {
+ r1 = &res[m+1];
+ for (x=0; isdigit((int)*r1) && x<m; x++,r1--) {
+ *r1 = ('9' - *r1 + c) + '0';
+ if (*r1 > '9') {
+ *r1 -= 10;
+ c=1;
+ } else {
+ c=0;
+ }
+ }
+ result = -atof(res);
+ } else
+ result = atof(res);
+
+ if(a_neg+b_neg==2) /* both are negatives, reverse sign */
+ result = -result;
+
+ return result;
+}
diff --git a/program/src/rrd_dump.c b/program/src/rrd_dump.c
--- /dev/null
+++ b/program/src/rrd_dump.c
@@ -0,0 +1,367 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_dump Display a RRD
+ *****************************************************************************
+ * $Id$
+ * $Log$
+ * Revision 1.7 2004/05/25 20:53:21 oetiker
+ * prevent small leak when resources are exhausted -- Mike Slifcak
+ *
+ * Revision 1.6 2004/05/25 20:51:49 oetiker
+ * Update displayed copyright messages to be consistent. -- Mike Slifcak
+ *
+ * Revision 1.5 2003/02/13 07:05:27 oetiker
+ * Find attached the patch I promised to send to you. Please note that there
+ * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c
+ * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This
+ * library is identical to librrd, but it contains support code for per-thread
+ * global variables currently used for error information only. This is similar
+ * to how errno per-thread variables are implemented. librrd_th must be linked
+ * alongside of libpthred
+ *
+ * There is also a new file "THREADS", holding some documentation.
+ *
+ * -- Peter Stamfest <peter@stamfest.at>
+ *
+ * Revision 1.4 2002/02/01 20:34:49 oetiker
+ * fixed version number and date/time
+ *
+ * Revision 1.3 2001/03/10 23:54:39 oetiker
+ * Support for COMPUTE data sources (CDEF data sources). Removes the RPN
+ * parser and calculator from rrd_graph and puts then in a new file,
+ * rrd_rpncalc.c. Changes to core files rrd_create and rrd_update. Some
+ * clean-up of aberrant behavior stuff, including a bug fix.
+ * Documentation update (rrdcreate.pod, rrdupdate.pod). Change xml format.
+ * -- Jake Brutlag <jakeb@corp.webtv.net>
+ *
+ * Revision 1.2 2001/03/04 13:01:55 oetiker
+ *
+ * Revision 1.1.1.1 2001/02/25 22:25:05 oetiker
+ * checkin
+ *
+ *****************************************************************************/
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+
+#if !(defined(NETWARE) || defined(WIN32))
+extern char *tzname[2];
+#endif
+
+int
+rrd_dump(int argc, char **argv)
+{
+ int rc;
+
+ if (argc < 2) {
+ rrd_set_error("Not enough arguments");
+ return -1;
+ }
+
+ if (argc == 3)
+ {
+ rc = rrd_dump_r(argv[1], argv[2]);
+ }
+ else
+ {
+ rc = rrd_dump_r(argv[1], NULL);
+ }
+
+ return rc;
+}
+
+int
+rrd_dump_r(const char *filename, char *outname)
+{
+ unsigned int i,ii,ix,iii=0;
+ time_t now;
+ char somestring[255];
+ rrd_value_t my_cdp;
+ long rra_base, rra_start, rra_next;
+ FILE *in_file;
+ FILE *out_file;
+ rrd_t rrd;
+ rrd_value_t value;
+ struct tm tm;
+ if(rrd_open(filename, &in_file,&rrd, RRD_READONLY)==-1){
+ rrd_free(&rrd);
+ return(-1);
+ }
+
+ out_file = NULL;
+ if (outname)
+ {
+ if (!(out_file = fopen(outname, "w")))
+ {
+ return (-1);
+ }
+ }
+ else
+ {
+ out_file = stdout;
+ }
+
+ fputs("<!-- Round Robin Database Dump -->", out_file);
+ fputs("<rrd>", out_file);
+ fprintf(out_file, "\t<version> %s </version>\n",RRD_VERSION);
+ fprintf(out_file, "\t<step> %lu </step> <!-- Seconds -->\n",rrd.stat_head->pdp_step);
+#if HAVE_STRFTIME
+ localtime_r(&rrd.live_head->last_up, &tm);
+ strftime(somestring,200,"%Y-%m-%d %H:%M:%S %Z",
+ &tm);
+#else
+# error "Need strftime"
+#endif
+ fprintf(out_file, "\t<lastupdate> %ld </lastupdate> <!-- %s -->\n\n",
+ rrd.live_head->last_up,somestring);
+ for(i=0;i<rrd.stat_head->ds_cnt;i++){
+ fprintf(out_file, "\t<ds>\n");
+ fprintf(out_file, "\t\t<name> %s </name>\n",rrd.ds_def[i].ds_nam);
+ fprintf(out_file, "\t\t<type> %s </type>\n",rrd.ds_def[i].dst);
+ if (dst_conv(rrd.ds_def[i].dst) != DST_CDEF) {
+ fprintf(out_file, "\t\t<minimal_heartbeat> %lu </minimal_heartbeat>\n",rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt);
+ if (isnan(rrd.ds_def[i].par[DS_min_val].u_val)){
+ fprintf(out_file, "\t\t<min> NaN </min>\n");
+ } else {
+ fprintf(out_file, "\t\t<min> %0.10e </min>\n",rrd.ds_def[i].par[DS_min_val].u_val);
+ }
+ if (isnan(rrd.ds_def[i].par[DS_max_val].u_val)){
+ fprintf(out_file, "\t\t<max> NaN </max>\n");
+ } else {
+ fprintf(out_file, "\t\t<max> %0.10e </max>\n",rrd.ds_def[i].par[DS_max_val].u_val);
+ }
+ } else { /* DST_CDEF */
+ char *str=NULL;
+ rpn_compact2str((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef]),rrd.ds_def,&str);
+ fprintf(out_file, "\t\t<cdef> %s </cdef>\n", str);
+ free(str);
+ }
+ fprintf(out_file, "\n\t\t<!-- PDP Status -->\n");
+ fprintf(out_file, "\t\t<last_ds> %s </last_ds>\n",rrd.pdp_prep[i].last_ds);
+ if (isnan(rrd.pdp_prep[i].scratch[PDP_val].u_val)){
+ fprintf(out_file, "\t\t<value> NaN </value>\n");
+ } else {
+ fprintf(out_file, "\t\t<value> %0.10e </value>\n",rrd.pdp_prep[i].scratch[PDP_val].u_val);
+ }
+ fprintf(out_file, "\t\t<unknown_sec> %lu </unknown_sec>\n",
+ rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
+
+ fprintf(out_file, "\t</ds>\n\n");
+ }
+
+ fputs("<!-- Round Robin Archives -->", out_file);
+
+ rra_base=ftell(in_file);
+ rra_next = rra_base;
+
+ for(i=0;i<rrd.stat_head->rra_cnt;i++){
+
+ long timer=0;
+ rra_start= rra_next;
+ rra_next += ( rrd.stat_head->ds_cnt
+ * rrd.rra_def[i].row_cnt
+ * sizeof(rrd_value_t));
+ fprintf(out_file, "\t<rra>\n");
+ fprintf(out_file, "\t\t<cf> %s </cf>\n",rrd.rra_def[i].cf_nam);
+ fprintf(out_file, "\t\t<pdp_per_row> %lu </pdp_per_row> <!-- %lu seconds -->\n\n",
+ rrd.rra_def[i].pdp_cnt, rrd.rra_def[i].pdp_cnt
+ *rrd.stat_head->pdp_step);
+ /* support for RRA parameters */
+ fprintf(out_file, "\t\t<params>\n");
+ switch(cf_conv(rrd.rra_def[i].cf_nam)) {
+ case CF_HWPREDICT:
+ fprintf(out_file, "\t\t<hw_alpha> %0.10e </hw_alpha>\n",
+ rrd.rra_def[i].par[RRA_hw_alpha].u_val);
+ fprintf(out_file, "\t\t<hw_beta> %0.10e </hw_beta>\n",
+ rrd.rra_def[i].par[RRA_hw_beta].u_val);
+ fprintf(out_file, "\t\t<dependent_rra_idx> %lu </dependent_rra_idx>\n",
+ rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt);
+ break;
+ case CF_SEASONAL:
+ case CF_DEVSEASONAL:
+ fprintf(out_file, "\t\t<seasonal_gamma> %0.10e </seasonal_gamma>\n",
+ rrd.rra_def[i].par[RRA_seasonal_gamma].u_val);
+ fprintf(out_file, "\t\t<seasonal_smooth_idx> %lu </seasonal_smooth_idx>\n",
+ rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt);
+ fprintf(out_file, "\t\t<dependent_rra_idx> %lu </dependent_rra_idx>\n",
+ rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt);
+ break;
+ case CF_FAILURES:
+ fprintf(out_file, "\t\t<delta_pos> %0.10e </delta_pos>\n",
+ rrd.rra_def[i].par[RRA_delta_pos].u_val);
+ fprintf(out_file, "\t\t<delta_neg> %0.10e </delta_neg>\n",
+ rrd.rra_def[i].par[RRA_delta_neg].u_val);
+ fprintf(out_file, "\t\t<window_len> %lu </window_len>\n",
+ rrd.rra_def[i].par[RRA_window_len].u_cnt);
+ fprintf(out_file, "\t\t<failure_threshold> %lu </failure_threshold>\n",
+ rrd.rra_def[i].par[RRA_failure_threshold].u_cnt);
+ /* fall thru */
+ case CF_DEVPREDICT:
+ fprintf(out_file, "\t\t<dependent_rra_idx> %lu </dependent_rra_idx>\n",
+ rrd.rra_def[i].par[RRA_dependent_rra_idx].u_cnt);
+ break;
+ case CF_AVERAGE:
+ case CF_MAXIMUM:
+ case CF_MINIMUM:
+ case CF_LAST:
+ default:
+ fprintf(out_file, "\t\t<xff> %0.10e </xff>\n", rrd.rra_def[i].par[RRA_cdp_xff_val].u_val);
+ break;
+ }
+ fprintf(out_file, "\t\t</params>\n");
+ fprintf(out_file, "\t\t<cdp_prep>\n");
+ for(ii=0;ii<rrd.stat_head->ds_cnt;ii++){
+ unsigned long ivalue;
+ fprintf(out_file, "\t\t\t<ds>\n");
+ /* support for exporting all CDP parameters */
+ /* parameters common to all CFs */
+ /* primary_val and secondary_val do not need to be saved between updates
+ * so strictly speaking they could be omitted.
+ * However, they can be useful for diagnostic purposes, so are included here. */
+ value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt
+ +ii].scratch[CDP_primary_val].u_val;
+ if (isnan(value)) {
+ fprintf(out_file, "\t\t\t<primary_value> NaN </primary_value>\n");
+ } else {
+ fprintf(out_file, "\t\t\t<primary_value> %0.10e </primary_value>\n", value);
+ }
+ value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_secondary_val].u_val;
+ if (isnan(value)) {
+ fprintf(out_file, "\t\t\t<secondary_value> NaN </secondary_value>\n");
+ } else {
+ fprintf(out_file, "\t\t\t<secondary_value> %0.10e </secondary_value>\n", value);
+ }
+ switch(cf_conv(rrd.rra_def[i].cf_nam)) {
+ case CF_HWPREDICT:
+ value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_intercept].u_val;
+ if (isnan(value)) {
+ fprintf(out_file, "\t\t\t<intercept> NaN </intercept>\n");
+ } else {
+ fprintf(out_file, "\t\t\t<intercept> %0.10e </intercept>\n", value);
+ }
+ value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_last_intercept].u_val;
+ if (isnan(value)) {
+ fprintf(out_file, "\t\t\t<last_intercept> NaN </last_intercept>\n");
+ } else {
+ fprintf(out_file, "\t\t\t<last_intercept> %0.10e </last_intercept>\n", value);
+ }
+ value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_slope].u_val;
+ if (isnan(value)) {
+ fprintf(out_file, "\t\t\t<slope> NaN </slope>\n");
+ } else {
+ fprintf(out_file, "\t\t\t<slope> %0.10e </slope>\n", value);
+ }
+ value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_last_slope].u_val;
+ if (isnan(value)) {
+ fprintf(out_file, "\t\t\t<last_slope> NaN </last_slope>\n");
+ } else {
+ fprintf(out_file, "\t\t\t<last_slope> %0.10e </last_slope>\n", value);
+ }
+ ivalue = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_null_count].u_cnt;
+ fprintf(out_file, "\t\t\t<nan_count> %lu </nan_count>\n", ivalue);
+ ivalue = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_last_null_count].u_cnt;
+ fprintf(out_file, "\t\t\t<last_nan_count> %lu </last_nan_count>\n", ivalue);
+ break;
+ case CF_SEASONAL:
+ case CF_DEVSEASONAL:
+ value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_seasonal].u_val;
+ if (isnan(value)) {
+ fprintf(out_file, "\t\t\t<seasonal> NaN </seasonal>\n");
+ } else {
+ fprintf(out_file, "\t\t\t<seasonal> %0.10e </seasonal>\n", value);
+ }
+ value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_last_seasonal].u_val;
+ if (isnan(value)) {
+ fprintf(out_file, "\t\t\t<last_seasonal> NaN </last_seasonal>\n");
+ } else {
+ fprintf(out_file, "\t\t\t<last_seasonal> %0.10e </last_seasonal>\n", value);
+ }
+ ivalue = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_init_seasonal].u_cnt;
+ fprintf(out_file, "\t\t\t<init_flag> %lu </init_flag>\n", ivalue);
+ break;
+ case CF_DEVPREDICT:
+ break;
+ case CF_FAILURES:
+ {
+ unsigned short vidx;
+ char *violations_array = (char *) ((void*)
+ rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch);
+ fprintf(out_file, "\t\t\t<history> ");
+ for (vidx = 0; vidx < rrd.rra_def[i].par[RRA_window_len].u_cnt; ++vidx)
+ {
+ fprintf(out_file, "%d",violations_array[vidx]);
+ }
+ fprintf(out_file, " </history>\n");
+ }
+ break;
+ case CF_AVERAGE:
+ case CF_MAXIMUM:
+ case CF_MINIMUM:
+ case CF_LAST:
+ default:
+ value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_val].u_val;
+ if (isnan(value)) {
+ fprintf(out_file, "\t\t\t<value> NaN </value>\n");
+ } else {
+ fprintf(out_file, "\t\t\t<value> %0.10e </value>\n", value);
+ }
+ fprintf(out_file, "\t\t\t<unknown_datapoints> %lu </unknown_datapoints>\n",
+ rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_unkn_pdp_cnt].u_cnt);
+ break;
+ }
+ fprintf(out_file, "\t\t\t</ds>\n");
+ }
+ fprintf(out_file, "\t\t</cdp_prep>\n");
+
+ fprintf(out_file, "\t\t<database>\n");
+ fseek(in_file,(rra_start
+ +(rrd.rra_ptr[i].cur_row+1)
+ * rrd.stat_head->ds_cnt
+ * sizeof(rrd_value_t)),SEEK_SET);
+ timer = - (rrd.rra_def[i].row_cnt-1);
+ ii=rrd.rra_ptr[i].cur_row;
+ for(ix=0;ix<rrd.rra_def[i].row_cnt;ix++){
+ ii++;
+ if (ii>=rrd.rra_def[i].row_cnt) {
+ fseek(in_file,rra_start,SEEK_SET);
+ ii=0; /* wrap if max row cnt is reached */
+ }
+ now = (rrd.live_head->last_up
+ - rrd.live_head->last_up
+ % (rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step))
+ + (timer*rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step);
+
+ timer++;
+#if HAVE_STRFTIME
+ localtime_r(&now, &tm);
+ strftime(somestring,200,"%Y-%m-%d %H:%M:%S %Z", &tm);
+#else
+# error "Need strftime"
+#endif
+ fprintf(out_file, "\t\t\t<!-- %s / %d --> <row>",somestring,(int)now);
+ for(iii=0;iii<rrd.stat_head->ds_cnt;iii++){
+ fread(&my_cdp,sizeof(rrd_value_t),1,in_file);
+ if (isnan(my_cdp)){
+ fprintf(out_file, "<v> NaN </v>");
+ } else {
+ fprintf(out_file, "<v> %0.10e </v>",my_cdp);
+ };
+ }
+ fprintf(out_file, "</row>\n");
+ }
+ fprintf(out_file, "\t\t</database>\n\t</rra>\n");
+
+ }
+ fprintf(out_file, "</rrd>\n");
+ rrd_free(&rrd);
+ fclose(in_file);
+ if (out_file != stdout)
+ {
+ fclose(out_file);
+ }
+ return(0);
+}
+
+
+
+
diff --git a/program/src/rrd_error.c b/program/src/rrd_error.c
--- /dev/null
+++ b/program/src/rrd_error.c
@@ -0,0 +1,145 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_error.c Common Header File
+ *****************************************************************************
+ * $Id$
+ * $Log$
+ * Revision 1.4 2003/02/22 21:57:03 oetiker
+ * a patch to avoid a memory leak and a Makefile.am patch to
+ * distribute all required source files -- Peter Stamfest <peter@stamfest.at>
+ *
+ * Revision 1.3 2003/02/13 07:05:27 oetiker
+ * Find attached the patch I promised to send to you. Please note that there
+ * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c
+ * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This
+ * library is identical to librrd, but it contains support code for per-thread
+ * global variables currently used for error information only. This is similar
+ * to how errno per-thread variables are implemented. librrd_th must be linked
+ * alongside of libpthred
+ *
+ * There is also a new file "THREADS", holding some documentation.
+ *
+ * -- Peter Stamfest <peter@stamfest.at>
+ *
+ * Revision 1.2 2002/02/01 20:34:49 oetiker
+ * fixed version number and date/time
+ *
+ * Revision 1.1.1.1 2001/02/25 22:25:05 oetiker
+ * checkin
+ *
+ *************************************************************************** */
+
+#include "rrd_tool.h"
+#include <stdarg.h>
+
+#define MAXLEN 4096
+#define ERRBUFLEN 256
+#define CTX (rrd_get_context())
+
+void
+rrd_set_error(char *fmt, ...)
+{
+ va_list argp;
+ rrd_clear_error();
+ va_start(argp, fmt);
+#ifdef HAVE_VSNPRINTF
+ vsnprintf(CTX->rrd_error, CTX->len, fmt, argp);
+#else
+ vsprintf(CTX->rrd_error, fmt, argp);
+#endif
+ va_end(argp);
+}
+
+int
+rrd_test_error(void) {
+ return CTX->rrd_error[0] != '\0';
+}
+
+void
+rrd_clear_error(void){
+ CTX->rrd_error[0] = '\0';
+}
+
+char *
+rrd_get_error(void){
+ return CTX->rrd_error;
+}
+
+#if 0
+/* PS: Keep this stuff around, maybe we want it again if we use
+ rrd_contexts to really associate them with single RRD files and
+ operations on them... Then a single thread may use more than one
+ context. Using these functions would require to change each and
+ every function containing any of the non _r versions... */
+void
+rrd_set_error_r(struct rrd_context *rrd_ctx, char *fmt, ...)
+{
+ va_list argp;
+ rrd_clear_error_r(rrd_ctx);
+ va_start(argp, fmt);
+#ifdef HAVE_VSNPRINTF
+ vsnprintf((char *)rrd_ctx->rrd_error, rrd_ctx->len, fmt, argp);
+ rrd_ctx->rrd_error[rrd_ctx->len]='\0';
+#else
+ vsprintf((char *)rrd_ctx->rrd_error, fmt, argp);
+#endif
+ va_end(argp);
+}
+
+int
+rrd_test_error_r(struct rrd_context *rrd_ctx) {
+ return rrd_ctx->rrd_error[0] != '\0';
+}
+
+void
+rrd_clear_error_r(struct rrd_context *rrd_ctx) {
+ rrd_ctx->rrd_error[0] = '\0';
+}
+
+char *
+rrd_get_error_r(struct rrd_context *rrd_ctx) {
+ return (char *)rrd_ctx->rrd_error;
+}
+#endif
+
+/* PS: Should we move this to some other file? It is not really error
+ related. */
+struct rrd_context *
+rrd_new_context(void) {
+ struct rrd_context *rrd_ctx =
+ (struct rrd_context *) malloc(sizeof(struct rrd_context));
+
+ if (rrd_ctx) {
+ rrd_ctx->rrd_error = malloc(MAXLEN+10);
+ rrd_ctx->lib_errstr = malloc(ERRBUFLEN+10);
+ if (rrd_ctx->rrd_error && rrd_ctx->lib_errstr) {
+ *rrd_ctx->rrd_error = 0;
+ *rrd_ctx->lib_errstr = 0;
+ rrd_ctx->len = MAXLEN;
+ rrd_ctx->errlen = ERRBUFLEN;
+ return rrd_ctx;
+ }
+ if (rrd_ctx->rrd_error) free(rrd_ctx->rrd_error);
+ if (rrd_ctx->lib_errstr) free(rrd_ctx->lib_errstr);
+ free(rrd_ctx);
+ }
+ return NULL;
+}
+
+void
+rrd_free_context(struct rrd_context *rrd_ctx) {
+ if (rrd_ctx) {
+ if (rrd_ctx->rrd_error) free(rrd_ctx->rrd_error);
+ if (rrd_ctx->lib_errstr) free(rrd_ctx->lib_errstr);
+ free(rrd_ctx);
+ }
+}
+
+#if 0
+void rrd_globalize_error(struct rrd_context *rrd_ctx) {
+ if (rrd_ctx) {
+ rrd_set_error(rrd_ctx->rrd_error);
+ }
+}
+#endif
diff --git a/program/src/rrd_fetch.c b/program/src/rrd_fetch.c
--- /dev/null
+++ b/program/src/rrd_fetch.c
@@ -0,0 +1,493 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_fetch.c read date from an rrd to use for further processing
+ *****************************************************************************
+ * $Id$
+ * $Log$
+ * Revision 1.8 2004/05/18 18:53:03 oetiker
+ * big spell checking patch -- slif@bellsouth.net
+ *
+ * Revision 1.7 2003/11/11 19:46:21 oetiker
+ * replaced time_value with rrd_time_value as MacOS X introduced a struct of that name in their standard headers
+ *
+ * Revision 1.6 2003/01/16 23:27:54 oetiker
+ * fix border condition in rra selection of rrd_fetch
+ * -- Stanislav Sinyagin <ssinyagin@yahoo.com>
+ *
+ * Revision 1.5 2002/06/23 22:29:40 alex
+ * Added "step=1800" and such to "DEF"
+ * Cleaned some of the signed vs. unsigned problems
+ *
+ * Revision 1.4 2002/02/01 20:34:49 oetiker
+ * fixed version number and date/time
+ *
+ * Revision 1.3 2001/12/24 06:51:49 alex
+ * A patch of size 44Kbytes... in short:
+ *
+ * Found and repaired the off-by-one error in rrd_fetch_fn().
+ * As a result I had to remove the hacks in rrd_fetch_fn(),
+ * rrd_tool.c, vdef_calc(), data_calc(), data_proc() and
+ * reduce_data(). There may be other places which I didn't
+ * find so be careful.
+ *
+ * Enhanced debugging in rrd_fetch_fn(), it shows the RRA selection
+ * process.
+ *
+ * Added the ability to print VDEF timestamps. At the moment it
+ * is a hack, I needed it now to fix the off-by-one error.
+ * If the format string is "%c" (and nothing else!), the time
+ * will be printed by both ctime() and as a long int.
+ *
+ * Moved some code around (slightly altering it) from rrd_graph()
+ * initializing now in rrd_graph_init()
+ * options parsing now in rrd_graph_options()
+ * script parsing now in rrd_graph_script()
+ *
+ * Revision 1.2 2001/12/17 12:48:43 oetiker
+ * fix overflow error ...
+ *
+ * Revision 1.1.1.1 2001/02/25 22:25:05 oetiker
+ * checkin
+ *
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+
+#include "rrd_is_thread_safe.h"
+/*#define DEBUG*/
+
+int
+rrd_fetch(int argc,
+ char **argv,
+ time_t *start,
+ time_t *end, /* which time frame do you want ?
+ * will be changed to represent reality */
+ unsigned long *step, /* which stepsize do you want?
+ * will be changed to represent reality */
+ unsigned long *ds_cnt, /* number of data sources in file */
+ char ***ds_namv, /* names of data sources */
+ rrd_value_t **data) /* two dimensional array containing the data */
+{
+
+
+ long step_tmp =1;
+ time_t start_tmp=0, end_tmp=0;
+ const char *cf;
+
+ struct rrd_time_value start_tv, end_tv;
+ char *parsetime_error = NULL;
+ optind = 0; opterr = 0; /* initialize getopt */
+
+ /* init start and end time */
+ parsetime("end-24h", &start_tv);
+ parsetime("now", &end_tv);
+
+ while (1){
+ static struct option long_options[] =
+ {
+ {"resolution", required_argument, 0, 'r'},
+ {"start", required_argument, 0, 's'},
+ {"end", required_argument, 0, 'e'},
+ {0,0,0,0}
+ };
+ int option_index = 0;
+ int opt;
+ opt = getopt_long(argc, argv, "r:s:e:",
+ long_options, &option_index);
+
+ if (opt == EOF)
+ break;
+
+ switch(opt) {
+ case 's':
+ if ((parsetime_error = parsetime(optarg, &start_tv))) {
+ rrd_set_error( "start time: %s", parsetime_error );
+ return -1;
+ }
+ break;
+ case 'e':
+ if ((parsetime_error = parsetime(optarg, &end_tv))) {
+ rrd_set_error( "end time: %s", parsetime_error );
+ return -1;
+ }
+ break;
+ case 'r':
+ step_tmp = atol(optarg);
+ break;
+ case '?':
+ rrd_set_error("unknown option '-%c'",optopt);
+ return(-1);
+ }
+ }
+
+
+ if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
+ return -1;
+ }
+
+
+ if (start_tmp < 3600*24*365*10){
+ rrd_set_error("the first entry to fetch should be after 1980");
+ return(-1);
+ }
+
+ if (end_tmp < start_tmp) {
+ rrd_set_error("start (%ld) should be less than end (%ld)", start_tmp, end_tmp);
+ return(-1);
+ }
+
+ *start = start_tmp;
+ *end = end_tmp;
+
+ if (step_tmp < 1) {
+ rrd_set_error("step must be >= 1 second");
+ return -1;
+ }
+ *step = step_tmp;
+
+ if (optind + 1 >= argc){
+ rrd_set_error("not enough arguments");
+ return -1;
+ }
+
+ cf = argv[optind+1];
+
+ if (rrd_fetch_r(argv[optind],cf,start,end,step,ds_cnt,ds_namv,data) == -1)
+ return(-1);
+ return (0);
+}
+
+int
+rrd_fetch_r(
+ const char *filename, /* name of the rrd */
+ const char *cf, /* which consolidation function ?*/
+ time_t *start,
+ time_t *end, /* which time frame do you want ?
+ * will be changed to represent reality */
+ unsigned long *step, /* which stepsize do you want?
+ * will be changed to represent reality */
+ unsigned long *ds_cnt, /* number of data sources in file */
+ char ***ds_namv, /* names of data_sources */
+ rrd_value_t **data) /* two dimensional array containing the data */
+{
+ enum cf_en cf_idx;
+
+ if ((int)(cf_idx=cf_conv(cf)) == -1 ){
+ return -1;
+ }
+
+ return (rrd_fetch_fn(filename,cf_idx,start,end,step,ds_cnt,ds_namv,data));
+}
+
+int
+rrd_fetch_fn(
+ const char *filename, /* name of the rrd */
+ enum cf_en cf_idx, /* which consolidation function ?*/
+ time_t *start,
+ time_t *end, /* which time frame do you want ?
+ * will be changed to represent reality */
+ unsigned long *step, /* which stepsize do you want?
+ * will be changed to represent reality */
+ unsigned long *ds_cnt, /* number of data sources in file */
+ char ***ds_namv, /* names of data_sources */
+ rrd_value_t **data) /* two dimensional array containing the data */
+{
+ long i,ii;
+ FILE *in_file;
+ time_t cal_start,cal_end, rra_start_time,rra_end_time;
+ long best_full_rra=0, best_part_rra=0, chosen_rra=0, rra_pointer=0;
+ long best_full_step_diff=0, best_part_step_diff=0, tmp_step_diff=0, tmp_match=0, best_match=0;
+ long full_match, rra_base;
+ long start_offset, end_offset;
+ int first_full = 1;
+ int first_part = 1;
+ rrd_t rrd;
+ rrd_value_t *data_ptr;
+ unsigned long rows;
+ long rrd_head_size;
+
+#ifdef DEBUG
+fprintf(stderr,"Entered rrd_fetch_fn() searching for the best match\n");
+fprintf(stderr,"Looking for: start %10lu end %10lu step %5lu\n",
+ *start,*end,*step);
+#endif
+
+ if(rrd_open(filename,&in_file,&rrd, RRD_READONLY)==-1)
+ return(-1);
+
+ rrd_head_size = ftell(in_file);
+
+ /* when was the really last update of this file ? */
+
+ if (((*ds_namv) = (char **) malloc(rrd.stat_head->ds_cnt * sizeof(char*)))==NULL){
+ rrd_set_error("malloc fetch ds_namv array");
+ rrd_free(&rrd);
+ fclose(in_file);
+ return(-1);
+ }
+
+ for(i=0;(unsigned long)i<rrd.stat_head->ds_cnt;i++){
+ if ((((*ds_namv)[i]) = malloc(sizeof(char) * DS_NAM_SIZE))==NULL){
+ rrd_set_error("malloc fetch ds_namv entry");
+ rrd_free(&rrd);
+ free(*ds_namv);
+ fclose(in_file);
+ return(-1);
+ }
+ strncpy((*ds_namv)[i],rrd.ds_def[i].ds_nam,DS_NAM_SIZE-1);
+ (*ds_namv)[i][DS_NAM_SIZE-1]='\0';
+
+ }
+
+ /* find the rra which best matches the requirements */
+ for(i=0;(unsigned)i<rrd.stat_head->rra_cnt;i++){
+ if(cf_conv(rrd.rra_def[i].cf_nam) == cf_idx){
+
+ cal_end = (rrd.live_head->last_up - (rrd.live_head->last_up
+ % (rrd.rra_def[i].pdp_cnt
+ * rrd.stat_head->pdp_step)));
+ cal_start = (cal_end
+ - (rrd.rra_def[i].pdp_cnt
+ * rrd.rra_def[i].row_cnt
+ * rrd.stat_head->pdp_step));
+
+ full_match = *end -*start;
+#ifdef DEBUG
+fprintf(stderr,"Considering: start %10lu end %10lu step %5lu ",
+ cal_start,cal_end,
+ rrd.stat_head->pdp_step * rrd.rra_def[i].pdp_cnt);
+#endif
+ /* we need step difference in either full or partial case */
+ tmp_step_diff = labs(*step - (rrd.stat_head->pdp_step
+ * rrd.rra_def[i].pdp_cnt));
+ /* best full match */
+ if(cal_end >= *end
+ && cal_start <= *start){
+ if (first_full || (tmp_step_diff < best_full_step_diff)){
+ first_full=0;
+ best_full_step_diff = tmp_step_diff;
+ best_full_rra=i;
+#ifdef DEBUG
+fprintf(stderr,"best full match so far\n");
+#endif
+ } else {
+#ifdef DEBUG
+fprintf(stderr,"full match, not best\n");
+#endif
+ }
+
+ } else {
+ /* best partial match */
+ tmp_match = full_match;
+ if (cal_start>*start)
+ tmp_match -= (cal_start-*start);
+ if (cal_end<*end)
+ tmp_match -= (*end-cal_end);
+ if (first_part ||
+ (best_match < tmp_match) ||
+ (best_match == tmp_match &&
+ tmp_step_diff < best_part_step_diff)){
+#ifdef DEBUG
+fprintf(stderr,"best partial so far\n");
+#endif
+ first_part=0;
+ best_match = tmp_match;
+ best_part_step_diff = tmp_step_diff;
+ best_part_rra =i;
+ } else {
+#ifdef DEBUG
+fprintf(stderr,"partial match, not best\n");
+#endif
+ }
+ }
+ }
+ }
+
+ /* lets see how the matching went. */
+ if (first_full==0)
+ chosen_rra = best_full_rra;
+ else if (first_part==0)
+ chosen_rra = best_part_rra;
+ else {
+ rrd_set_error("the RRD does not contain an RRA matching the chosen CF");
+ rrd_free(&rrd);
+ fclose(in_file);
+ return(-1);
+ }
+
+ /* set the wish parameters to their real values */
+ *step = rrd.stat_head->pdp_step * rrd.rra_def[chosen_rra].pdp_cnt;
+ *start -= (*start % *step);
+ *end += (*step - *end % *step);
+ rows = (*end - *start) / *step + 1;
+
+#ifdef DEBUG
+ fprintf(stderr,"We found: start %10lu end %10lu step %5lu rows %lu\n",
+ *start,*end,*step,rows);
+#endif
+
+/* Start and end are now multiples of the step size. The amount of
+** steps we want is (end-start)/step and *not* an extra one.
+** Reasoning: if step is s and we want to graph from t to t+s,
+** we need exactly ((t+s)-t)/s rows. The row to collect from the
+** database is the one with time stamp (t+s) which means t to t+s.
+*/
+ *ds_cnt = rrd.stat_head->ds_cnt;
+ if (((*data) = malloc(*ds_cnt * rows * sizeof(rrd_value_t)))==NULL){
+ rrd_set_error("malloc fetch data area");
+ for (i=0;(unsigned long)i<*ds_cnt;i++)
+ free((*ds_namv)[i]);
+ free(*ds_namv);
+ rrd_free(&rrd);
+ fclose(in_file);
+ return(-1);
+ }
+
+ data_ptr=(*data);
+
+ /* find base address of rra */
+ rra_base=ftell(in_file);
+ for(i=0;i<chosen_rra;i++)
+ rra_base += ( *ds_cnt
+ * rrd.rra_def[i].row_cnt
+ * sizeof(rrd_value_t));
+
+ /* find start and end offset */
+ rra_end_time = (rrd.live_head->last_up
+ - (rrd.live_head->last_up % *step));
+ rra_start_time = (rra_end_time
+ - ( *step * (rrd.rra_def[chosen_rra].row_cnt-1)));
+ /* here's an error by one if we don't be careful */
+ start_offset =(long)(*start + *step - rra_start_time) / (long)*step;
+ end_offset = (long)(rra_end_time - *end ) / (long)*step;
+#ifdef DEBUG
+ fprintf(stderr,"rra_start %lu, rra_end %lu, start_off %li, end_off %li\n",
+ rra_start_time,rra_end_time,start_offset,end_offset);
+#endif
+
+ /* fill the gap at the start if needs be */
+
+ if (start_offset <= 0)
+ rra_pointer = rrd.rra_ptr[chosen_rra].cur_row+1;
+ else
+ rra_pointer = rrd.rra_ptr[chosen_rra].cur_row+1+start_offset;
+
+ if(fseek(in_file,(rra_base
+ + (rra_pointer
+ * *ds_cnt
+ * sizeof(rrd_value_t))),SEEK_SET) != 0){
+ rrd_set_error("seek error in RRA");
+ for (i=0;(unsigned)i<*ds_cnt;i++)
+ free((*ds_namv)[i]);
+ free(*ds_namv);
+ rrd_free(&rrd);
+ free(*data);
+ *data = NULL;
+ fclose(in_file);
+ return(-1);
+
+ }
+#ifdef DEBUG
+ fprintf(stderr,"First Seek: rra_base %lu rra_pointer %lu\n",
+ rra_base, rra_pointer);
+#endif
+ /* step trough the array */
+
+ for (i=start_offset;
+ i< (signed)rrd.rra_def[chosen_rra].row_cnt - end_offset;
+ i++){
+ /* no valid data yet */
+ if (i<0) {
+#ifdef DEBUG
+ fprintf(stderr,"pre fetch %li -- ",i);
+#endif
+ for(ii=0;(unsigned)ii<*ds_cnt;ii++){
+ *(data_ptr++) = DNAN;
+#ifdef DEBUG
+ fprintf(stderr,"%10.2f ",*(data_ptr-1));
+#endif
+ }
+ }
+ /* past the valid data area */
+ else if (i >= (signed)rrd.rra_def[chosen_rra].row_cnt) {
+#ifdef DEBUG
+ fprintf(stderr,"post fetch %li -- ",i);
+#endif
+ for(ii=0;(unsigned)ii<*ds_cnt;ii++){
+ *(data_ptr++) = DNAN;
+#ifdef DEBUG
+ fprintf(stderr,"%10.2f ",*(data_ptr-1));
+#endif
+ }
+ } else {
+ /* OK we are inside the valid area but the pointer has to
+ * be wrapped*/
+ if (rra_pointer >= (signed)rrd.rra_def[chosen_rra].row_cnt) {
+ rra_pointer -= rrd.rra_def[chosen_rra].row_cnt;
+ if(fseek(in_file,(rra_base+rra_pointer
+ * *ds_cnt
+ * sizeof(rrd_value_t)),SEEK_SET) != 0){
+ rrd_set_error("wrap seek in RRA did fail");
+ for (ii=0;(unsigned)ii<*ds_cnt;ii++)
+ free((*ds_namv)[ii]);
+ free(*ds_namv);
+ rrd_free(&rrd);
+ free(*data);
+ *data = NULL;
+ fclose(in_file);
+ return(-1);
+ }
+#ifdef DEBUG
+ fprintf(stderr,"wrap seek ...\n");
+#endif
+ }
+
+ if(fread(data_ptr,
+ sizeof(rrd_value_t),
+ *ds_cnt,in_file) != rrd.stat_head->ds_cnt){
+ rrd_set_error("fetching cdp from rra");
+ for (ii=0;(unsigned)ii<*ds_cnt;ii++)
+ free((*ds_namv)[ii]);
+ free(*ds_namv);
+ rrd_free(&rrd);
+ free(*data);
+ *data = NULL;
+ fclose(in_file);
+ return(-1);
+ }
+#ifdef HAVE_POSIX_FADVISE
+ /* don't pollute the buffer cache with data read from the file. We do this while reading to
+ keep damage minimal */
+ if (0 != posix_fadvise(fileno(in_file), rrd_head_size, 0, POSIX_FADV_DONTNEED)) {
+ rrd_set_error("setting POSIX_FADV_DONTNEED on '%s': %s",filename, rrd_strerror(errno));
+ fclose(in_file);
+ return(-1);
+ }
+#endif
+
+#ifdef DEBUG
+ fprintf(stderr,"post fetch %li -- ",i);
+ for(ii=0;ii<*ds_cnt;ii++)
+ fprintf(stderr,"%10.2f ",*(data_ptr+ii));
+#endif
+ data_ptr += *ds_cnt;
+ rra_pointer ++;
+ }
+#ifdef DEBUG
+ fprintf(stderr,"\n");
+#endif
+
+ }
+ rrd_free(&rrd);
+#ifdef HAVE_POSIX_FADVISE
+ /* and just to be sure we drop everything except the header at the end */
+ if (0 != posix_fadvise(fileno(in_file), rrd_head_size, 0, POSIX_FADV_DONTNEED)) {
+ rrd_set_error("setting POSIX_FADV_DONTNEED on '%s': %s",filename, rrd_strerror(errno));
+ fclose(in_file);
+ return(-1);
+ }
+#endif
+ fclose(in_file);
+ return(0);
+}
diff --git a/program/src/rrd_first.c b/program/src/rrd_first.c
--- /dev/null
+++ b/program/src/rrd_first.c
@@ -0,0 +1,100 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_first Return
+ *****************************************************************************
+ * Initial version by Burton Strauss, ntopSupport.com - 3/2005
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+
+
+time_t
+rrd_first(int argc, char **argv)
+{
+ int target_rraindex=0;
+ char *endptr;
+ optind = 0; opterr = 0; /* initialize getopt */
+
+ while (1){
+ static struct option long_options[] =
+ {
+ {"rraindex", required_argument, 0, 129},
+ {0,0,0,0}
+ };
+ int option_index = 0;
+ int opt;
+ opt = getopt_long(argc, argv, "", long_options, &option_index);
+
+ if(opt == EOF)
+ break;
+
+ switch(opt) {
+ case 129:
+ target_rraindex=strtol(optarg,&endptr,0);
+ if(target_rraindex < 0) {
+ rrd_set_error("invalid rraindex number");
+ return(-1);
+ }
+ break;
+ default:
+ rrd_set_error("usage rrdtool %s [--rraindex number] file.rrd", argv[0]);
+ return(-1);
+ }
+ }
+
+ if(optind >= argc){
+ rrd_set_error("not enough arguments");
+ return -1;
+ }
+
+ return(rrd_first_r(argv[optind], target_rraindex));
+}
+
+
+time_t
+rrd_first_r(const char *filename, const int rraindex)
+{
+ long rra_start,
+ timer;
+ time_t then;
+ FILE *in_file;
+ rrd_t rrd;
+
+ if(rrd_open(filename,&in_file,&rrd, RRD_READONLY)==-1){
+ rrd_set_error("could not open RRD");
+ return(-1);
+ }
+
+ if((rraindex < 0) || (rraindex >= (int)rrd.stat_head->rra_cnt)) {
+ rrd_set_error("invalid rraindex number");
+ rrd_free(&rrd);
+ fclose(in_file);
+ return(-1);
+ }
+
+ rra_start = ftell(in_file);
+ fseek(in_file,
+ (rra_start +
+ (rrd.rra_ptr[rraindex].cur_row+1) *
+ rrd.stat_head->ds_cnt *
+ sizeof(rrd_value_t)),
+ SEEK_SET);
+ timer = - (rrd.rra_def[rraindex].row_cnt-1);
+ if (rrd.rra_ptr[rraindex].cur_row + 1 > rrd.rra_def[rraindex].row_cnt) {
+ fseek(in_file,rra_start,SEEK_SET);
+ }
+ then = (rrd.live_head->last_up -
+ rrd.live_head->last_up %
+ (rrd.rra_def[rraindex].pdp_cnt*rrd.stat_head->pdp_step)) +
+ (timer *
+ rrd.rra_def[rraindex].pdp_cnt*rrd.stat_head->pdp_step);
+
+ rrd_free(&rrd);
+ fclose(in_file);
+ return(then);
+}
+
+
+
+
diff --git a/program/src/rrd_format.c b/program/src/rrd_format.c
--- /dev/null
+++ b/program/src/rrd_format.c
@@ -0,0 +1,94 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_format.c RRD Database Format helper functions
+ *****************************************************************************
+ * $Id$
+ * $Log$
+ * Revision 1.5 2004/05/18 18:53:03 oetiker
+ * big spell checking patch -- slif@bellsouth.net
+ *
+ * Revision 1.4 2003/02/13 07:05:27 oetiker
+ * Find attached the patch I promised to send to you. Please note that there
+ * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c
+ * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This
+ * library is identical to librrd, but it contains support code for per-thread
+ * global variables currently used for error information only. This is similar
+ * to how errno per-thread variables are implemented. librrd_th must be linked
+ * alongside of libpthred
+ *
+ * There is also a new file "THREADS", holding some documentation.
+ *
+ * -- Peter Stamfest <peter@stamfest.at>
+ *
+ * Revision 1.3 2002/02/01 20:34:49 oetiker
+ * fixed version number and date/time
+ *
+ * Revision 1.2 2001/03/10 23:54:39 oetiker
+ * Support for COMPUTE data sources (CDEF data sources). Removes the RPN
+ * parser and calculator from rrd_graph and puts then in a new file,
+ * rrd_rpncalc.c. Changes to core files rrd_create and rrd_update. Some
+ * clean-up of aberrant behavior stuff, including a bug fix.
+ * Documentation update (rrdcreate.pod, rrdupdate.pod). Change xml format.
+ * -- Jake Brutlag <jakeb@corp.webtv.net>
+ *
+ * Revision 1.1.1.1 2001/02/25 22:25:05 oetiker
+ * checkin
+ *
+ * Revision 1.3 1998/03/08 12:35:11 oetiker
+ * checkpointing things because the current setup seems to work
+ * according to the things said in the manpages
+ *
+ * Revision 1.2 1998/02/26 22:58:22 oetiker
+ * fixed define
+ *
+ * Revision 1.1 1998/02/21 16:14:41 oetiker
+ * Initial revision
+ *
+ *
+ *****************************************************************************/
+#include "rrd_tool.h"
+
+#define converter(VV,VVV) \
+ if (strcmp(#VV, string) == 0) return VVV;
+
+/* conversion functions to allow symbolic entry of enumerations */
+enum dst_en dst_conv(char *string)
+{
+ converter(COUNTER,DST_COUNTER)
+ converter(ABSOLUTE,DST_ABSOLUTE)
+ converter(GAUGE,DST_GAUGE)
+ converter(DERIVE,DST_DERIVE)
+ converter(COMPUTE,DST_CDEF)
+ rrd_set_error("unknown data acquisition function '%s'",string);
+ return(-1);
+}
+
+
+enum cf_en cf_conv(const char *string)
+{
+
+ converter(AVERAGE,CF_AVERAGE)
+ converter(MIN,CF_MINIMUM)
+ converter(MAX,CF_MAXIMUM)
+ converter(LAST,CF_LAST)
+ converter(HWPREDICT,CF_HWPREDICT)
+ converter(DEVPREDICT,CF_DEVPREDICT)
+ converter(SEASONAL,CF_SEASONAL)
+ converter(DEVSEASONAL,CF_DEVSEASONAL)
+ converter(FAILURES,CF_FAILURES)
+ rrd_set_error("unknown consolidation function '%s'",string);
+ return(-1);
+}
+
+#undef converter
+
+long
+ds_match(rrd_t *rrd,char *ds_nam){
+ unsigned long i;
+ for(i=0;i<rrd->stat_head->ds_cnt;i++)
+ if ((strcmp(ds_nam,rrd->ds_def[i].ds_nam))==0)
+ return i;
+ rrd_set_error("unknown data source name '%s'",ds_nam);
+ return -1;
+}
diff --git a/program/src/rrd_format.h b/program/src/rrd_format.h
--- /dev/null
+++ b/program/src/rrd_format.h
@@ -0,0 +1,398 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_format.h RRD Database Format header
+ *****************************************************************************/
+
+#ifndef _RRD_FORMAT_H
+#define _RRD_FORMAT_H
+
+#include "rrd.h"
+
+/*****************************************************************************
+ * put this in your /usr/lib/magic file (/etc/magic on HPUX)
+ *
+ * # rrd database format
+ * 0 string RRD\0 rrd file
+ * >5 string >\0 version '%s'
+ *
+ *****************************************************************************/
+
+#define RRD_COOKIE "RRD"
+/* #define RRD_VERSION "0002" */
+/* changed because microsecond precision requires another field */
+#define RRD_VERSION "0003"
+#define FLOAT_COOKIE 8.642135E130
+
+#include "rrd_nan_inf.h"
+
+typedef union unival {
+ unsigned long u_cnt;
+ rrd_value_t u_val;
+} unival;
+
+
+/****************************************************************************
+ * The RRD Database Structure
+ * ---------------------------
+ *
+ * In oder to properly describe the database structure lets define a few
+ * new words:
+ *
+ * ds - Data Source (ds) providing input to the database. A Data Source (ds)
+ * can be a traffic counter, a temperature, the number of users logged
+ * into a system. The rrd database format can handle the input of
+ * several Data Sources (ds) in a singe database.
+ *
+ * dst - Data Source Type (dst). The Data Source Type (dst) defines the rules
+ * applied to Build Primary Data Points from the input provided by the
+ * data sources (ds).
+ *
+ * pdp - Primary Data Point (pdp). After the database has accepted the
+ * input from the data sources (ds). It starts building Primary
+ * Data Points (pdp) from the data. Primary Data Points (pdp)
+ * are evenly spaced along the time axis (pdp_step). The values
+ * of the Primary Data Points are calculated from the values of
+ * the data source (ds) and the exact time these values were
+ * provided by the data source (ds).
+ *
+ * pdp_st - PDP Start (pdp_st). The moments (pdp_st) in time where
+ * these steps occur are defined by the moments where the
+ * number of seconds since 1970-jan-1 modulo pdp_step equals
+ * zero (pdp_st).
+ *
+ * cf - Consolidation Function (cf). An arbitrary Consolidation Function (cf)
+ * (averaging, min, max) is applied to the primary data points (pdp) to
+ * calculate the consolidated data point.
+ *
+ * cdp - Consolidated Data Point (cdp) is the long term storage format for data
+ * in the rrd database. Consolidated Data Points represent one or
+ * several primary data points collected along the time axis. The
+ * Consolidated Data Points (cdp) are stored in Round Robin Archives
+ * (rra).
+ *
+ * rra - Round Robin Archive (rra). This is the place where the
+ * consolidated data points (cdp) get stored. The data is
+ * organized in rows (row) and columns (col). The Round Robin
+ * Archive got its name from the method data is stored in
+ * there. An RRD database can contain several Round Robin
+ * Archives. Each Round Robin Archive can have a different row
+ * spacing along the time axis (pdp_cnt) and a different
+ * consolidation function (cf) used to build its consolidated
+ * data points (cdp).
+ *
+ * rra_st - RRA Start (rra_st). The moments (rra_st) in time where
+ * Consolidated Data Points (cdp) are added to an rra are
+ * defined by the moments where the number of seconds since
+ * 1970-jan-1 modulo pdp_cnt*pdp_step equals zero (rra_st).
+ *
+ * row - Row (row). A row represent all consolidated data points (cdp)
+ * in a round robin archive who are of the same age.
+ *
+ * col - Column (col). A column (col) represent all consolidated
+ * data points (cdp) in a round robin archive (rra) who
+ * originated from the same data source (ds).
+ *
+ */
+
+/****************************************************************************
+ * POS 1: stat_head_t static header of the database
+ ****************************************************************************/
+
+typedef struct stat_head_t {
+
+ /* Data Base Identification Section ***/
+ char cookie[4]; /* RRD */
+ char version[5]; /* version of the format */
+ double float_cookie; /* is it the correct double
+ * representation ? */
+
+ /* Data Base Structure Definition *****/
+ unsigned long ds_cnt; /* how many different ds provide
+ * input to the rrd */
+ unsigned long rra_cnt; /* how many rras will be maintained
+ * in the rrd */
+ unsigned long pdp_step; /* pdp interval in seconds */
+
+ unival par[10]; /* global parameters ... unused
+ at the moment */
+} stat_head_t;
+
+
+/****************************************************************************
+ * POS 2: ds_def_t (* ds_cnt) Data Source definitions
+ ****************************************************************************/
+
+enum dst_en { DST_COUNTER=0, /* data source types available */
+ DST_ABSOLUTE,
+ DST_GAUGE,
+ DST_DERIVE,
+ DST_CDEF};
+
+enum ds_param_en { DS_mrhb_cnt=0, /* minimum required heartbeat. A
+ * data source must provide input at
+ * least every ds_mrhb seconds,
+ * otherwise it is regarded dead and
+ * will be set to UNKNOWN */
+ DS_min_val, /* the processed input of a ds must */
+ DS_max_val, /* be between max_val and min_val
+ * both can be set to UNKNOWN if you
+ * do not care. Data outside the limits
+ * set to UNKNOWN */
+ DS_cdef = DS_mrhb_cnt}; /* pointer to encoded rpn
+ * expression only applies to DST_CDEF */
+
+/* The magic number here is one less than DS_NAM_SIZE */
+#define DS_NAM_FMT "%19[a-zA-Z0-9_-]"
+#define DS_NAM_SIZE 20
+
+#define DST_FMT "%19[A-Z]"
+#define DST_SIZE 20
+
+typedef struct ds_def_t {
+ char ds_nam[DS_NAM_SIZE]; /* Name of the data source (null terminated)*/
+ char dst[DST_SIZE]; /* Type of data source (null terminated)*/
+ unival par[10]; /* index of this array see ds_param_en */
+} ds_def_t;
+
+/****************************************************************************
+ * POS 3: rra_def_t ( * rra_cnt) one for each store to be maintained
+ ****************************************************************************/
+enum cf_en { CF_AVERAGE=0, /* data consolidation functions */
+ CF_MINIMUM,
+ CF_MAXIMUM,
+ CF_LAST,
+ CF_HWPREDICT,
+ /* An array of predictions using the seasonal
+ * Holt-Winters algorithm. Requires an RRA of type
+ * CF_SEASONAL for this data source. */
+ CF_SEASONAL,
+ /* An array of seasonal effects. Requires an RRA of
+ * type CF_HWPREDICT for this data source. */
+ CF_DEVPREDICT,
+ /* An array of deviation predictions based upon
+ * smoothed seasonal deviations. Requires an RRA of
+ * type CF_DEVSEASONAL for this data source. */
+ CF_DEVSEASONAL,
+ /* An array of smoothed seasonal deviations. Requires
+ * an RRA of type CF_HWPREDICT for this data source.
+ * */
+ CF_FAILURES};
+ /* A binary array of failure indicators: 1 indicates
+ * that the number of violations in the prescribed
+ * window exceeded the prescribed threshold. */
+
+#define MAX_RRA_PAR_EN 10
+enum rra_par_en { RRA_cdp_xff_val=0, /* what part of the consolidated
+ * datapoint must be known, to produce a
+ * valid entry in the rra */
+ RRA_hw_alpha,
+ /* exponential smoothing parameter for the intercept in
+ * the Holt-Winters prediction algorithm. */
+ RRA_hw_beta,
+ /* exponential smoothing parameter for the slope in
+ * the Holt-Winters prediction algorithm. */
+ RRA_dependent_rra_idx,
+ /* For CF_HWPREDICT: index of the RRA with the seasonal
+ * effects of the Holt-Winters algorithm (of type
+ * CF_SEASONAL).
+ * For CF_DEVPREDICT: index of the RRA with the seasonal
+ * deviation predictions (of type CF_DEVSEASONAL).
+ * For CF_SEASONAL: index of the RRA with the Holt-Winters
+ * intercept and slope coefficient (of type CF_HWPREDICT).
+ * For CF_DEVSEASONAL: index of the RRA with the
+ * Holt-Winters prediction (of type CF_HWPREDICT).
+ * For CF_FAILURES: index of the CF_DEVSEASONAL array.
+ * */
+ RRA_seasonal_smooth_idx,
+ /* For CF_SEASONAL and CF_DEVSEASONAL:
+ * an integer between 0 and row_count - 1 which
+ * is index in the seasonal cycle for applying
+ * the period smoother. */
+ RRA_failure_threshold,
+ /* For CF_FAILURES, number of violations within the last
+ * window required to mark a failure. */
+ RRA_seasonal_gamma = RRA_hw_alpha,
+ /* exponential smoothing parameter for seasonal effects.
+ * */
+ RRA_delta_pos = RRA_hw_alpha,
+ RRA_delta_neg = RRA_hw_beta,
+ /* confidence bound scaling parameters for the
+ * the FAILURES RRA. */
+ RRA_window_len = RRA_seasonal_smooth_idx};
+ /* For CF_FAILURES, the length of the window for measuring
+ * failures. */
+
+#define CF_NAM_FMT "%19[A-Z]"
+#define CF_NAM_SIZE 20
+
+typedef struct rra_def_t {
+ char cf_nam[CF_NAM_SIZE];/* consolidation function (null term) */
+ unsigned long row_cnt; /* number of entries in the store */
+ unsigned long pdp_cnt; /* how many primary data points are
+ * required for a consolidated data
+ * point?*/
+ unival par[MAX_RRA_PAR_EN]; /* index see rra_param_en */
+
+} rra_def_t;
+
+
+/****************************************************************************
+ ****************************************************************************
+ ****************************************************************************
+ * LIVE PART OF THE HEADER. THIS WILL BE WRITTEN ON EVERY UPDATE *
+ ****************************************************************************
+ ****************************************************************************
+ ****************************************************************************/
+/****************************************************************************
+ * POS 4: live_head_t
+ ****************************************************************************/
+
+typedef struct live_head_t {
+ time_t last_up; /* when was rrd last updated */
+ long last_up_usec; /* micro seconds part of the
+ update timestamp. Always >= 0 */
+} live_head_t;
+
+
+/****************************************************************************
+ * POS 5: pdp_prep_t (* ds_cnt) here we prepare the pdps
+ ****************************************************************************/
+#define LAST_DS_LEN 30 /* DO NOT CHANGE THIS ... */
+
+enum pdp_par_en { PDP_unkn_sec_cnt=0, /* how many seconds of the current
+ * pdp value is unknown data? */
+
+ PDP_val}; /* current value of the pdp.
+ this depends on dst */
+
+typedef struct pdp_prep_t{
+ char last_ds[LAST_DS_LEN]; /* the last reading from the data
+ * source. this is stored in ASCII
+ * to cater for very large counters
+ * we might encounter in connection
+ * with SNMP. */
+ unival scratch[10]; /* contents according to pdp_par_en */
+} pdp_prep_t;
+
+/* data is passed from pdp to cdp when seconds since epoch modulo pdp_step == 0
+ obviously the updates do not occur at these times only. Especially does the
+ format allow for updates to occur at different times for each data source.
+ The rules which makes this work is as follows:
+
+ * DS updates may only occur at ever increasing points in time
+ * When any DS update arrives after a cdp update time, the *previous*
+ update cycle gets executed. All pdps are transfered to cdps and the
+ cdps feed the rras where necessary. Only then the new DS value
+ is loaded into the PDP. */
+
+
+/****************************************************************************
+ * POS 6: cdp_prep_t (* rra_cnt * ds_cnt ) data prep area for cdp values
+ ****************************************************************************/
+#define MAX_CDP_PAR_EN 10
+#define MAX_CDP_FAILURES_IDX 8
+/* max CDP scratch entries avail to record violations for a FAILURES RRA */
+#define MAX_FAILURES_WINDOW_LEN 28
+enum cdp_par_en { CDP_val=0,
+ /* the base_interval is always an
+ * average */
+ CDP_unkn_pdp_cnt,
+ /* how many unknown pdp were
+ * integrated. This and the cdp_xff
+ * will decide if this is going to
+ * be a UNKNOWN or a valid value */
+ CDP_hw_intercept,
+ /* Current intercept coefficient for the Holt-Winters
+ * prediction algorithm. */
+ CDP_hw_last_intercept,
+ /* Last iteration intercept coefficient for the Holt-Winters
+ * prediction algorihtm. */
+ CDP_hw_slope,
+ /* Current slope coefficient for the Holt-Winters
+ * prediction algorithm. */
+ CDP_hw_last_slope,
+ /* Last iteration slope coeffient. */
+ CDP_null_count,
+ /* Number of sequential Unknown (DNAN) values + 1 preceding
+ * the current prediction.
+ * */
+ CDP_last_null_count,
+ /* Last iteration count of Unknown (DNAN) values. */
+ CDP_primary_val = 8,
+ /* optimization for bulk updates: the value of the first CDP
+ * value to be written in the bulk update. */
+ CDP_secondary_val = 9,
+ /* optimization for bulk updates: the value of subsequent
+ * CDP values to be written in the bulk update. */
+ CDP_hw_seasonal = CDP_hw_intercept,
+ /* Current seasonal coefficient for the Holt-Winters
+ * prediction algorithm. This is stored in CDP prep to avoid
+ * redundant seek operations. */
+ CDP_hw_last_seasonal = CDP_hw_last_intercept,
+ /* Last iteration seasonal coeffient. */
+ CDP_seasonal_deviation = CDP_hw_intercept,
+ CDP_last_seasonal_deviation = CDP_hw_last_intercept,
+ CDP_init_seasonal = CDP_null_count};
+ /* init_seasonal is a flag which when > 0, forces smoothing updates
+ * to occur when rra_ptr.cur_row == 0 */
+
+typedef struct cdp_prep_t{
+ unival scratch[MAX_CDP_PAR_EN];
+ /* contents according to cdp_par_en *
+ * init state should be NAN */
+
+} cdp_prep_t;
+
+/****************************************************************************
+ * POS 7: rra_ptr_t (* rra_cnt) pointers to the current row in each rra
+ ****************************************************************************/
+
+typedef struct rra_ptr_t {
+ unsigned long cur_row; /* current row in the rra*/
+} rra_ptr_t;
+
+
+/****************************************************************************
+ ****************************************************************************
+ * One single struct to hold all the others. For convenience.
+ ****************************************************************************
+ ****************************************************************************/
+typedef struct rrd_t {
+ stat_head_t *stat_head; /* the static header */
+ ds_def_t *ds_def; /* list of data source definitions */
+ rra_def_t *rra_def; /* list of round robin archive def */
+ live_head_t *live_head;
+ pdp_prep_t *pdp_prep; /* pdp data prep area */
+ cdp_prep_t *cdp_prep; /* cdp prep area */
+ rra_ptr_t *rra_ptr; /* list of rra pointers */
+ rrd_value_t *rrd_value; /* list of rrd values */
+} rrd_t;
+
+/****************************************************************************
+ ****************************************************************************
+ * AFTER the header section we have the DATA STORAGE AREA it is made up from
+ * Consolidated Data Points organized in Round Robin Archives.
+ ****************************************************************************
+ ****************************************************************************
+
+ *RRA 0
+ (0,0) .................... ( ds_cnt -1 , 0)
+ .
+ .
+ .
+ (0, row_cnt -1) ... (ds_cnt -1, row_cnt -1)
+
+ *RRA 1
+ *RRA 2
+
+ *RRA rra_cnt -1
+
+ ****************************************************************************/
+
+
+#endif
+
+
+
+
diff --git a/program/src/rrd_getopt.c b/program/src/rrd_getopt.c
--- /dev/null
+++ b/program/src/rrd_getopt.c
@@ -0,0 +1,1002 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97
+ Free Software Foundation, Inc.
+
+ This file is part of the GNU C Library. Its master source is NOT part of
+ the C library, however. The master source lives in /gd/gnu/lib.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+\f
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+ Ditto for AIX 3.2 and <stdlib.h>. */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "../rrd_config.h"
+#endif
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+#include <stdlib.h>
+#include <unistd.h>
+#endif /* GNU C library. */
+
+#ifdef VMS
+#include <unixlib.h>
+#if HAVE_STRING_H - 0
+#include <string.h>
+#endif
+#endif
+
+#if defined (_WIN32) && !defined (__CYGWIN32__)
+/* It's not Unix, really. See? Capital letters. */
+#include <windows.h>
+#define getpid() GetCurrentProcessId()
+#endif
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.
+ When compiling libc, the _ macro is predefined. */
+#ifdef HAVE_LIBINTL_H
+# include <libintl.h>
+# define _(msgid) gettext (msgid)
+#else
+# define _(msgid) (msgid)
+#endif
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "rrd_getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* 1003.2 says this must be 1 before any call. */
+int optind = 1;
+
+/* Formerly, initialization of getopt depended on optind==0, which
+ causes problems with re-calling getopt as programs generally don't
+ know that. */
+
+int __getopt_initialized = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return -1 with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable. */
+static char *posixly_correct;
+\f
+/* we must include string as there are warnings without it ... */
+#include <string.h>
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+#define my_index strchr
+#else
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+char *getenv ();
+
+static char *
+my_index (str, chr)
+ const char *str;
+ int chr;
+{
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *) str;
+ str++;
+ }
+ return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+ If not using GCC, it is ok not to declare it. */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+ That was relevant to code that was here before. */
+#if !defined (__STDC__) || !__STDC__
+/* gcc with -traditional declares the built-in strlen to return int,
+ and has done so at least since version 2.4.5. -- rms. */
+extern int strlen (const char *);
+#endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+\f
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+#ifdef _LIBC
+/* Bash 2.0 gives us an environment variable containing flags
+ indicating ARGV elements that should not be considered arguments. */
+
+static const char *nonoption_flags;
+static int nonoption_flags_len;
+
+static int original_argc;
+static char *const *original_argv;
+
+/* Make sure the environment variable bash 2.0 puts in the environment
+ is valid for the getopt call we must make sure that the ARGV passed
+ to getopt is that one passed to the process. */
+static void store_args (int argc, char *const *argv) __attribute__ ((unused));
+static void
+store_args (int argc, char *const *argv)
+{
+ /* XXX This is no good solution. We should rather copy the args so
+ that we can compare them later. But we must not use malloc(3). */
+ original_argc = argc;
+ original_argv = argv;
+}
+text_set_element (__libc_subinit, store_args);
+#endif
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+#if defined (__STDC__) && __STDC__
+static void exchange (char **);
+#endif
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made. */
+
+#if defined (__STDC__) && __STDC__
+static const char *_getopt_initialize (int, char *const *, const char *);
+#endif
+static const char *
+_getopt_initialize (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ /* Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ first_nonopt = last_nonopt = optind = 1;
+
+ nextchar = NULL;
+
+ posixly_correct = getenv ("POSIXLY_CORRECT");
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (posixly_correct != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+
+#ifdef _LIBC
+ if (posixly_correct == NULL
+ && argc == original_argc && argv == original_argv)
+ {
+ /* Bash 2.0 puts a special variable in the environment for each
+ command it runs, specifying which ARGV elements are the results of
+ file name wildcard expansion and therefore should not be
+ considered as options. */
+ char var[100];
+ sprintf (var, "_%d_GNU_nonoption_argv_flags_", getpid ());
+ nonoption_flags = getenv (var);
+ if (nonoption_flags == NULL)
+ nonoption_flags_len = 0;
+ else
+ nonoption_flags_len = strlen (nonoption_flags);
+ }
+ else
+ nonoption_flags_len = 0;
+#endif
+
+ return optstring;
+}
+\f
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns -1.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ optarg = NULL;
+
+ if (!__getopt_initialized || optind == 0)
+ {
+ optstring = _getopt_initialize (argc, argv, optstring);
+ optind = 1; /* Don't scan ARGV[0], the program name. */
+ __getopt_initialized = 1;
+ }
+
+ /* Test whether ARGV[optind] points to a non-option argument.
+ Either it does not have option syntax, or there is an environment flag
+ from the shell indicating it is not an option. The later information
+ is only used when the used in the GNU libc. */
+#ifdef _LIBC
+#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \
+ || (optind < nonoption_flags_len \
+ && nonoption_flags[optind] == '1'))
+#else
+#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#endif
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ /* Advance to the next ARGV-element. */
+
+ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+ moved back by the user (who may also have changed the arguments). */
+ if (last_nonopt > optind)
+ last_nonopt = optind;
+ if (first_nonopt > optind)
+ first_nonopt = optind;
+
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc && NONOPTION_P)
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* The special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return -1;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if (NONOPTION_P)
+ {
+ if (ordering == REQUIRE_ORDER)
+ return -1;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Skip the initial punctuation. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ /* Decode the current option-ARGV-element. */
+
+ /* Check whether the ARGV-element is a long option.
+
+ If long_only and the ARGV-element has the form "-f", where f is
+ a valid short option, don't consider it an abbreviated form of
+ a long option that starts with f. Otherwise there would be no
+ way to give the -f short option.
+
+ On the other hand, if there's a long option "fubar" and
+ the ARGV-element is "-fu", do consider that an abbreviation of
+ the long option, just like "--fu", and not "-f" with arg "u".
+
+ This distinction seems to be the most useful approach. */
+
+ if (longopts != NULL
+ && (argv[optind][1] == '-'
+ || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = -1;
+ int option_index;
+
+ for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int) (nameend - nextchar)
+ == (unsigned int) strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ optopt = 0;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (opterr) {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ _("%s: option `--%s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ _("%s: option `%c%s' doesn't allow an argument\n"),
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+ nextchar += strlen (nextchar);
+
+ optopt = pfound->val;
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ optopt = pfound->val;
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ optopt = 0;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next short option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+ if (posixly_correct)
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, _("%s: illegal option -- %c\n"),
+ argv[0], c);
+ else
+ fprintf (stderr, _("%s: invalid option -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ return '?';
+ }
+ /* Convenience. Treat POSIX -W foo same as long option --foo */
+ if (temp[0] == 'W' && temp[1] == ';')
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = 0;
+ int option_index;
+
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ return c;
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+
+ /* optarg is now the argument, see if it's in the
+ table of longopts. */
+
+ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int) (nameend - nextchar) == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (opterr)
+ fprintf (stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ nextchar = NULL;
+ return 'W'; /* Let the application handle it. */
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = NULL;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr,
+ _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#endif /* Not ELIDE_CODE. */
+\f
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/program/src/rrd_getopt.h b/program/src/rrd_getopt.h
--- /dev/null
+++ b/program/src/rrd_getopt.h
@@ -0,0 +1,133 @@
+/* Declarations for getopt.
+ Copyright (C) 1989,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+
+ This file is part of the GNU C Library. Its master source is NOT part of
+ the C library, however. The master source lives in /gd/gnu/lib.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+#if defined (__STDC__) && __STDC__
+ const char *name;
+#else
+ char *name;
+#endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+#if defined (__STDC__) && __STDC__
+#ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#else /* not __GNU_LIBRARY__ */
+extern int getopt ();
+#endif /* __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+#else /* not __STDC__ */
+extern int getopt ();
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+#endif /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/program/src/rrd_getopt1.c b/program/src/rrd_getopt1.c
--- /dev/null
@@ -0,0 +1,189 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+ Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+
+ This file is part of the GNU C Library. Its master source is NOT part of
+ the C library, however. The master source lives in /gd/gnu/lib.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+\f
+#ifdef HAVE_CONFIG_H
+#include "../rrd_config.h"
+#endif
+
+#include "rrd_getopt.h"
+
+#if !defined (__STDC__) || !__STDC__
+/* This is a separate conditional since some stdc systems
+ reject `defined (const)'. */
+#ifndef const
+#define const
+#endif
+#endif
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+
+#endif /* Not ELIDE_CODE. */
+\f
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value `%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/program/src/rrd_gfx.c b/program/src/rrd_gfx.c
--- /dev/null
+++ b/program/src/rrd_gfx.c
@@ -0,0 +1,2520 @@
+/****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd_gfx.c graphics wrapper for rrdtool
+ **************************************************************************/
+
+/* #define DEBUG */
+
+/* stupid MSVC doesnt support variadic macros = no debug for now! */
+#ifdef _MSC_VER
+# define RRDPRINTF()
+#else
+# ifdef DEBUG
+# define RRDPRINTF(...) fprintf(stderr, __VA_ARGS__);
+# else
+# define RRDPRINTF(...)
+# endif /* DEBUG */
+#endif /* _MSC_VER */
+#include "rrd_tool.h"
+#include <png.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+
+#include "rrd_gfx.h"
+#include "rrd_afm.h"
+#include "unused.h"
+
+/* lines are better drawn on the pixle than between pixles */
+#define LINEOFFSET 0.5
+
+#define USE_PDF_FAKE_ALPHA 1
+#define USE_EPS_FAKE_ALPHA 1
+
+typedef struct gfx_char_s *gfx_char;
+struct gfx_char_s {
+ FT_UInt index; /* glyph index */
+ FT_Vector pos; /* location from baseline in 26.6 */
+ FT_Glyph image; /* glyph bitmap */
+};
+
+typedef struct gfx_string_s *gfx_string;
+struct gfx_string_s {
+ unsigned int width;
+ unsigned int height;
+ int count; /* number of characters */
+ gfx_char glyphs;
+ size_t num_glyphs;
+ FT_BBox bbox;
+ FT_Matrix transform;
+};
+
+/* compute string bbox */
+static void compute_string_bbox(gfx_string string);
+
+/* create a freetype glyph string */
+gfx_string gfx_string_create ( gfx_canvas_t *canvas, FT_Face face,
+ const char *text, int rotation, double tabwidth, double size);
+
+/* create a freetype glyph string */
+static void gfx_string_destroy ( gfx_string string );
+
+static
+gfx_node_t *gfx_new_node( gfx_canvas_t *canvas,enum gfx_en type){
+ gfx_node_t *node = art_new(gfx_node_t,1);
+ if (node == NULL) return NULL;
+ node->type = type;
+ node->color = 0x0; /* color of element 0xRRGGBBAA alpha 0xff is solid*/
+ node->size =0.0; /* font size, line width */
+ node->path = NULL; /* path */
+ node->points = 0;
+ node->points_max =0;
+ node->closed_path = 0;
+ node->filename = NULL; /* font or image filename */
+ node->text = NULL;
+ node->x = 0.0;
+ node->y = 0.0; /* position */
+ node->angle = 0;
+ node->halign = GFX_H_NULL; /* text alignement */
+ node->valign = GFX_V_NULL; /* text alignement */
+ node->tabwidth = 0.0;
+ node->next = NULL;
+ if (canvas->lastnode != NULL){
+ canvas->lastnode->next = node;
+ }
+ if (canvas->firstnode == NULL){
+ canvas->firstnode = node;
+ }
+ canvas->lastnode = node;
+ return node;
+}
+
+gfx_canvas_t *gfx_new_canvas (void) {
+ gfx_canvas_t *canvas = art_new(gfx_canvas_t,1);
+ canvas->firstnode = NULL;
+ canvas->lastnode = NULL;
+ canvas->imgformat = IF_PNG; /* we default to PNG output */
+ canvas->interlaced = 0;
+ canvas->zoom = 1.0;
+ canvas->font_aa_threshold = -1.0;
+ canvas->aa_type = AA_NORMAL;
+ return canvas;
+}
+
+/* create a new line */
+gfx_node_t *gfx_new_line(gfx_canvas_t *canvas,
+ double X0, double Y0,
+ double X1, double Y1,
+ double width, gfx_color_t color){
+ return gfx_new_dashed_line(canvas, X0, Y0, X1, Y1, width, color, 0, 0);
+}
+
+gfx_node_t *gfx_new_dashed_line(gfx_canvas_t *canvas,
+ double X0, double Y0,
+ double X1, double Y1,
+ double width, gfx_color_t color,
+ double dash_on, double dash_off){
+
+ gfx_node_t *node;
+ ArtVpath *vec;
+ node = gfx_new_node(canvas,GFX_LINE);
+ if (node == NULL) return NULL;
+ vec = art_new(ArtVpath, 3);
+ if (vec == NULL) return NULL;
+ vec[0].code = ART_MOVETO_OPEN; vec[0].x=X0+LINEOFFSET; vec[0].y=Y0+LINEOFFSET;
+ vec[1].code = ART_LINETO; vec[1].x=X1+LINEOFFSET; vec[1].y=Y1+LINEOFFSET;
+ vec[2].code = ART_END; vec[2].x=0;vec[2].y=0;
+
+ node->points = 3;
+ node->points_max = 3;
+ node->color = color;
+ node->size = width;
+ node->dash_on = dash_on;
+ node->dash_off = dash_off;
+ node->path = vec;
+ return node;
+}
+
+/* create a new area */
+gfx_node_t *gfx_new_area (gfx_canvas_t *canvas,
+ double X0, double Y0,
+ double X1, double Y1,
+ double X2, double Y2,
+ gfx_color_t color) {
+
+ gfx_node_t *node;
+ ArtVpath *vec;
+ node = gfx_new_node(canvas,GFX_AREA);
+ if (node == NULL) return NULL;
+ vec = art_new(ArtVpath, 5);
+ if (vec == NULL) return NULL;
+ vec[0].code = ART_MOVETO; vec[0].x=X0; vec[0].y=Y0;
+ vec[1].code = ART_LINETO; vec[1].x=X1; vec[1].y=Y1;
+ vec[2].code = ART_LINETO; vec[2].x=X2; vec[2].y=Y2;
+ vec[3].code = ART_LINETO; vec[3].x=X0; vec[3].y=Y0;
+ vec[4].code = ART_END; vec[4].x=0; vec[4].y=0;
+
+ node->points = 5;
+ node->points_max = 5;
+ node->color = color;
+ node->path = vec;
+
+ return node;
+}
+
+/* add a point to a line or to an area */
+int gfx_add_point (gfx_node_t *node,
+ double x, double y){
+ if (node == NULL) return 1;
+ if (node->type == GFX_AREA) {
+ double X0 = node->path[0].x;
+ double Y0 = node->path[0].y;
+ node->points -= 2;
+ art_vpath_add_point (&(node->path),
+ &(node->points),
+ &(node->points_max),
+ ART_LINETO,
+ x,y);
+ art_vpath_add_point (&(node->path),
+ &(node->points),
+ &(node->points_max),
+ ART_LINETO,
+ X0,Y0);
+ art_vpath_add_point (&(node->path),
+ &(node->points),
+ &(node->points_max),
+ ART_END,
+ 0,0);
+ } else if (node->type == GFX_LINE) {
+ node->points -= 1;
+ art_vpath_add_point (&(node->path),
+ &(node->points),
+ &(node->points_max),
+ ART_LINETO,
+ x+LINEOFFSET,y+LINEOFFSET);
+ art_vpath_add_point (&(node->path),
+ &(node->points),
+ &(node->points_max),
+ ART_END,
+ 0,0);
+
+ } else {
+ /* can only add point to areas and lines */
+ return 1;
+ }
+ return 0;
+}
+
+void gfx_close_path (gfx_node_t *node) {
+ node->closed_path = 1;
+ if (node->path[0].code == ART_MOVETO_OPEN)
+ node->path[0].code = ART_MOVETO;
+}
+
+/* create a text node */
+gfx_node_t *gfx_new_text (gfx_canvas_t *canvas,
+ double x, double y, gfx_color_t color,
+ char* font, double size,
+ double tabwidth, double angle,
+ enum gfx_h_align_en h_align,
+ enum gfx_v_align_en v_align,
+ char* text){
+ gfx_node_t *node = gfx_new_node(canvas,GFX_TEXT);
+
+ node->text = strdup(text);
+ node->size = size;
+ node->filename = strdup(font);
+ node->x = x;
+ node->y = y;
+ node->angle = angle;
+ node->color = color;
+ node->tabwidth = tabwidth;
+ node->halign = h_align;
+ node->valign = v_align;
+#if 0
+ /* debugging: show text anchor
+ green is along x-axis, red is downward y-axis */
+ if (1) {
+ double a = 2 * M_PI * -node->angle / 360.0;
+ double cos_a = cos(a);
+ double sin_a = sin(a);
+ double len = 3;
+ gfx_new_line(canvas,
+ x, y,
+ x + len * cos_a, y - len * sin_a,
+ 0.2, 0x00FF0000);
+ gfx_new_line(canvas,
+ x, y,
+ x + len * sin_a, y + len * cos_a,
+ 0.2, 0xFF000000);
+ }
+#endif
+ return node;
+}
+
+int gfx_render(gfx_canvas_t *canvas,
+ art_u32 width, art_u32 height,
+ gfx_color_t background, FILE *fp){
+ switch (canvas->imgformat) {
+ case IF_PNG:
+ return gfx_render_png (canvas, width, height, background, fp);
+ case IF_SVG:
+ return gfx_render_svg (canvas, width, height, background, fp);
+ case IF_EPS:
+ return gfx_render_eps (canvas, width, height, background, fp);
+ case IF_PDF:
+ return gfx_render_pdf (canvas, width, height, background, fp);
+ default:
+ return -1;
+ }
+}
+
+static void gfx_string_destroy ( gfx_string string ) {
+ unsigned int n;
+ if (string->glyphs) {
+ for (n=0; n<string->num_glyphs; ++n)
+ FT_Done_Glyph (string->glyphs[n].image);
+ free (string->glyphs);
+ }
+ free (string);
+}
+
+
+double gfx_get_text_width ( gfx_canvas_t *canvas,
+ double start, char* font, double size,
+ double tabwidth, char* text, int rotation){
+ switch (canvas->imgformat) {
+ case IF_PNG:
+ return gfx_get_text_width_libart (canvas, start, font, size, tabwidth, text, rotation);
+ case IF_SVG: /* fall through */
+ case IF_EPS:
+ case IF_PDF:
+ return afm_get_text_width(start, font, size, tabwidth, text);
+ default:
+ return size * strlen(text);
+ }
+}
+
+double gfx_get_text_width_libart (
+ gfx_canvas_t *canvas, double UNUSED(start), char* font, double size,
+ double tabwidth, char* text, int rotation ){
+
+ int error;
+ double text_width=0;
+ FT_Face face;
+ FT_Library library=NULL;
+ gfx_string string;
+
+ FT_Init_FreeType( &library );
+ error = FT_New_Face( library, font, 0, &face );
+ if ( error ) {
+ FT_Done_FreeType(library);
+ return -1;
+ }
+ error = FT_Set_Char_Size(face, size*64,size*64, 100,100);
+ if ( error ) {
+ FT_Done_FreeType(library);
+ return -1;
+ }
+ string = gfx_string_create( canvas, face, text, rotation, tabwidth, size );
+ text_width = string->width;
+ gfx_string_destroy(string);
+ FT_Done_FreeType(library);
+ return text_width/64;
+}
+
+static void gfx_libart_close_path(gfx_node_t *node, ArtVpath **vec)
+{
+ /* libart must have end==start for closed paths,
+ even if using ART_MOVETO and not ART_MOVETO_OPEN
+ so add extra point which is the same as the starting point */
+ int points_max = node->points; /* scaled array has exact size */
+ int points = node->points - 1;
+ art_vpath_add_point (vec, &points, &points_max, ART_LINETO,
+ (**vec).x, (**vec).y);
+ art_vpath_add_point (vec, &points, &points_max, ART_END, 0, 0);
+}
+
+
+/* find bbox of a string */
+static void compute_string_bbox(gfx_string string) {
+ unsigned int n;
+ FT_BBox bbox;
+
+ bbox.xMin = bbox.yMin = 32000;
+ bbox.xMax = bbox.yMax = -32000;
+ for ( n = 0; n < string->num_glyphs; n++ ) {
+ FT_BBox glyph_bbox;
+ FT_Glyph_Get_CBox( string->glyphs[n].image, ft_glyph_bbox_gridfit,
+ &glyph_bbox );
+ if (glyph_bbox.xMin < bbox.xMin) {
+ bbox.xMin = glyph_bbox.xMin;
+ }
+ if (glyph_bbox.yMin < bbox.yMin) {
+ bbox.yMin = glyph_bbox.yMin;
+ }
+ if (glyph_bbox.xMax > bbox.xMax) {
+ bbox.xMax = glyph_bbox.xMax;
+ }
+ if (glyph_bbox.yMax > bbox.yMax) {
+ bbox.yMax = glyph_bbox.yMax;
+ }
+ }
+ if ( bbox.xMin > bbox.xMax ) {
+ bbox.xMin = 0;
+ bbox.yMin = 0;
+ bbox.xMax = 0;
+ bbox.yMax = 0;
+ }
+ string->bbox.xMin = bbox.xMin;
+ string->bbox.xMax = bbox.xMax;
+ string->bbox.yMin = bbox.yMin;
+ string->bbox.yMax = bbox.yMax;
+}
+
+/* create a free type glyph string */
+gfx_string gfx_string_create(gfx_canvas_t *canvas, FT_Face face,const char *text,
+ int rotation, double tabwidth, double size )
+{
+
+ FT_GlyphSlot slot = face->glyph; /* a small shortcut */
+ FT_Bool use_kerning;
+ FT_UInt previous;
+ FT_Vector ft_pen;
+
+ gfx_string string = (gfx_string) malloc (sizeof(struct gfx_string_s));
+
+ gfx_char glyph; /* current glyph in table */
+ int n;
+ int error;
+ int gottab = 0;
+
+#ifdef HAVE_MBSTOWCS
+ wchar_t *cstr;
+ size_t clen = strlen(text)+1;
+ cstr = malloc(sizeof(wchar_t) * clen); /* yes we are allocating probably too much here, I know */
+ string->count=mbstowcs(cstr,text,clen);
+ if ( string->count == -1){
+ /* conversion did not work, so lets fall back to just use what we got */
+ string->count=clen-1;
+ for(n=0;text[n] != '\0';n++){
+ cstr[n]=(unsigned char)text[n];
+ }
+ }
+#else
+ char *cstr = strdup(text);
+ string->count = strlen (text);
+#endif
+
+ ft_pen.x = 0; /* start at (0,0) !! */
+ ft_pen.y = 0;
+
+
+ string->width = 0;
+ string->height = 0;
+ string->glyphs = (gfx_char) calloc (string->count,sizeof(struct gfx_char_s));
+ string->num_glyphs = 0;
+ string->transform.xx = (FT_Fixed)( cos(M_PI*(rotation)/180.0)*0x10000);
+ string->transform.xy = (FT_Fixed)(-sin(M_PI*(rotation)/180.0)*0x10000);
+ string->transform.yx = (FT_Fixed)( sin(M_PI*(rotation)/180.0)*0x10000);
+ string->transform.yy = (FT_Fixed)( cos(M_PI*(rotation)/180.0)*0x10000);
+
+ use_kerning = FT_HAS_KERNING(face);
+ previous = 0;
+ glyph = string->glyphs;
+ for (n=0; n<string->count;glyph++,n++) {
+ FT_Vector vec;
+ /* handle the tabs ...
+ have a witespace glyph inserted, but set its width such that the distance
+ of the new right edge is x times tabwidth from 0,0 where x is an integer. */
+ unsigned int letter = cstr[n];
+ letter = afm_fix_osx_charset(letter); /* unsafe macro */
+
+ gottab = 0;
+ if (letter == '\\' && n+1 < string->count && cstr[n+1] == 't'){
+ /* we have a tab here so skip the backslash and
+ set t to ' ' so that we get a white space */
+ gottab = 1;
+ n++;
+ letter = ' ';
+ }
+ if (letter == '\t'){
+ letter = ' ';
+ gottab = 1 ;
+ }
+ /* initialize each struct gfx_char_s */
+ glyph->index = 0;
+ glyph->pos.x = 0;
+ glyph->pos.y = 0;
+ glyph->image = NULL;
+ glyph->index = FT_Get_Char_Index( face, letter );
+
+ /* compute glyph origin */
+ if ( use_kerning && previous && glyph->index ) {
+ FT_Vector kerning;
+ FT_Get_Kerning (face, previous, glyph->index,
+ ft_kerning_default, &kerning);
+ ft_pen.x += kerning.x;
+ ft_pen.y += kerning.y;
+ }
+
+ /* load the glyph image (in its native format) */
+ /* for now, we take a monochrome glyph bitmap */
+ error = FT_Load_Glyph (face, glyph->index, size > canvas->font_aa_threshold ?
+ canvas->aa_type == AA_NORMAL ? FT_LOAD_TARGET_NORMAL :
+ canvas->aa_type == AA_LIGHT ? FT_LOAD_TARGET_LIGHT :
+ FT_LOAD_TARGET_MONO : FT_LOAD_TARGET_MONO);
+ if (error) {
+ RRDPRINTF("couldn't load glyph: %c\n", letter)
+ continue;
+ }
+ error = FT_Get_Glyph (slot, &glyph->image);
+ if (error) {
+ RRDPRINTF("couldn't get glyph %c from slot %d\n", letter, (int)slot)
+ continue;
+ }
+ /* if we are in tabbing mode, we replace the tab with a space and shift the position
+ of the space so that its left edge is where the tab was supposed to land us */
+ if (gottab){
+ /* we are in gridfitting mode so the calculations happen in 1/64 pixles */
+ ft_pen.x = tabwidth*64.0 * (float)(1 + (long)(ft_pen.x / (tabwidth * 64.0))) - slot->advance.x;
+ }
+ /* store current pen position */
+ glyph->pos.x = ft_pen.x;
+ glyph->pos.y = ft_pen.y;
+
+
+ ft_pen.x += slot->advance.x;
+ ft_pen.y += slot->advance.y;
+
+ /* rotate glyph */
+ vec = glyph->pos;
+ FT_Vector_Transform (&vec, &string->transform);
+ error = FT_Glyph_Transform (glyph->image, &string->transform, &vec);
+ if (error) {
+ RRDPRINTF("couldn't transform glyph id %d\n", letter)
+ continue;
+ }
+
+ /* convert to a bitmap - destroy native image */
+ error = FT_Glyph_To_Bitmap (&glyph->image, size > canvas->font_aa_threshold ?
+ canvas->aa_type == AA_NORMAL ? FT_RENDER_MODE_NORMAL :
+ canvas->aa_type == AA_LIGHT ? FT_RENDER_MODE_LIGHT :
+ FT_RENDER_MODE_MONO : FT_RENDER_MODE_MONO, 0, 1);
+ if (error) {
+ RRDPRINTF("couldn't convert glyph id %d to bitmap\n", letter)
+ continue;
+ }
+
+ /* increment number of glyphs */
+ previous = glyph->index;
+ string->num_glyphs++;
+ }
+ free(cstr);
+/* printf ("number of glyphs = %d\n", string->num_glyphs);*/
+ compute_string_bbox( string );
+ /* the last character was a tab */
+ /* if (gottab) { */
+ string->width = ft_pen.x;
+ /* } else {
+ string->width = string->bbox.xMax - string->bbox.xMin;
+ } */
+ string->height = string->bbox.yMax - string->bbox.yMin;
+ return string;
+}
+
+
+static int gfx_save_png (art_u8 *buffer, FILE *fp,
+ long width, long height, long bytes_per_pixel);
+/* render grafics into png image */
+
+int gfx_render_png (gfx_canvas_t *canvas,
+ art_u32 width, art_u32 height,
+ gfx_color_t background, FILE *fp){
+
+
+ FT_Library library;
+ gfx_node_t *node = canvas->firstnode;
+ /*
+ art_u8 red = background >> 24, green = (background >> 16) & 0xff;
+ art_u8 blue = (background >> 8) & 0xff, alpha = ( background & 0xff );
+ */
+ unsigned long pys_width = width * canvas->zoom;
+ unsigned long pys_height = height * canvas->zoom;
+ const int bytes_per_pixel = 4;
+ unsigned long rowstride = pys_width*bytes_per_pixel; /* bytes per pixel */
+
+ /* fill that buffer with out background color */
+ gfx_color_t *buffp = art_new (gfx_color_t, pys_width*pys_height);
+ art_u8 *buffer = (art_u8 *)buffp;
+ unsigned long i;
+ for (i=0;i<pys_width*pys_height;
+ i++){
+ *(buffp++)=background;
+ }
+ FT_Init_FreeType( &library );
+ while(node){
+ switch (node->type) {
+ case GFX_LINE:
+ case GFX_AREA: {
+ ArtVpath *vec;
+ double dst[6];
+ ArtSVP *svp;
+ art_affine_scale(dst,canvas->zoom,canvas->zoom);
+ vec = art_vpath_affine_transform(node->path,dst);
+ if (node->closed_path)
+ gfx_libart_close_path(node, &vec);
+ /* gfx_round_scaled_coordinates(vec); */
+ /* pvec = art_vpath_perturb(vec);
+ art_free(vec); */
+ if(node->type == GFX_LINE){
+ svp = art_svp_vpath_stroke ( vec, ART_PATH_STROKE_JOIN_ROUND,
+ ART_PATH_STROKE_CAP_ROUND,
+ node->size*canvas->zoom,4,0.25);
+ } else {
+ svp = art_svp_from_vpath ( vec );
+ /* this takes time and is unnecessary since we make
+ sure elsewhere that the areas are going clock-whise */
+ /* svpt = art_svp_uncross( svp );
+ art_svp_free(svp);
+ svp = art_svp_rewind_uncrossed(svpt,ART_WIND_RULE_NONZERO);
+ art_svp_free(svpt);
+ */
+ }
+ art_free(vec);
+ /* this is from gnome since libart does not have this yet */
+ gnome_print_art_rgba_svp_alpha (svp ,0,0, pys_width, pys_height,
+ node->color, buffer, rowstride, NULL);
+ art_svp_free(svp);
+ break;
+ }
+ case GFX_TEXT: {
+ unsigned int n;
+ int error;
+ art_u8 fcolor[4],falpha;
+ FT_Face face;
+ gfx_char glyph;
+ gfx_string string;
+ FT_Vector vec; /* 26.6 */
+
+ float pen_x = 0.0 , pen_y = 0.0;
+ /* double x,y; */
+ long ix,iy;
+
+ fcolor[0] = node->color >> 24;
+ fcolor[1] = (node->color >> 16) & 0xff;
+ fcolor[2] = (node->color >> 8) & 0xff;
+ falpha = node->color & 0xff;
+ error = FT_New_Face( library,
+ (char *)node->filename,
+ 0,
+ &face );
+ if ( error ) {
+ rrd_set_error("failed to load %s",node->filename);
+
+ break;
+ }
+ error = FT_Set_Char_Size(face, /* handle to face object */
+ (long)(node->size*64),
+ (long)(node->size*64),
+ (long)(100*canvas->zoom),
+ (long)(100*canvas->zoom));
+ if ( error ) {
+ FT_Done_Face(face);
+ break;
+ }
+ pen_x = node->x * canvas->zoom;
+ pen_y = node->y * canvas->zoom;
+
+ string = gfx_string_create (canvas, face, node->text, node->angle, node->tabwidth, node->size);
+ FT_Done_Face(face);
+
+ switch(node->halign){
+ case GFX_H_RIGHT: vec.x = -string->bbox.xMax;
+ break;
+ case GFX_H_CENTER: vec.x = abs(string->bbox.xMax) >= abs(string->bbox.xMin) ?
+ -string->bbox.xMax/2:-string->bbox.xMin/2;
+ break;
+ case GFX_H_LEFT: vec.x = -string->bbox.xMin;
+ break;
+ case GFX_H_NULL: vec.x = 0;
+ break;
+ }
+
+ switch(node->valign){
+ case GFX_V_TOP: vec.y = string->bbox.yMax;
+ break;
+ case GFX_V_CENTER: vec.y = abs(string->bbox.yMax) >= abs(string->bbox.yMin) ?
+ string->bbox.yMax/2:string->bbox.yMin/2;
+ break;
+ case GFX_V_BOTTOM: vec.y = 0;
+ break;
+ case GFX_V_NULL: vec.y = 0;
+ break;
+ }
+ pen_x += vec.x/64;
+ pen_y += vec.y/64;
+ glyph = string->glyphs;
+ for(n=0; n<string->num_glyphs; n++, glyph++) {
+ int gr;
+ FT_Glyph image;
+ FT_BitmapGlyph bit;
+ /* long buf_x,comp_n; */
+ /* make copy to transform */
+ if (! glyph->image) {
+ RRDPRINTF("no image\n")
+ continue;
+ }
+ error = FT_Glyph_Copy (glyph->image, &image);
+ if (error) {
+ RRDPRINTF("couldn't copy image\n")
+ continue;
+ }
+
+ /* transform it */
+ vec = glyph->pos;
+ FT_Vector_Transform (&vec, &string->transform);
+
+ bit = (FT_BitmapGlyph) image;
+ gr = bit->bitmap.num_grays -1;
+/*
+ buf_x = (pen_x + 0.5) + (double)bit->left;
+ comp_n = buf_x + bit->bitmap.width > pys_width ? pys_width - buf_x : bit->bitmap.width;
+ if (buf_x < 0 || buf_x >= (long)pys_width) continue;
+ buf_x *= bytes_per_pixel ;
+ for (iy=0; iy < bit->bitmap.rows; iy++){
+ long buf_y = iy+(pen_y+0.5)-(double)bit->top;
+ if (buf_y < 0 || buf_y >= (long)pys_height) continue;
+ buf_y *= rowstride;
+ for (ix=0;ix < bit->bitmap.width;ix++){
+ *(letter + (ix*bytes_per_pixel+3)) = *(bit->bitmap.buffer + iy * bit->bitmap.width + ix);
+ }
+ art_rgba_rgba_composite(buffer + buf_y + buf_x ,letter,comp_n);
+ }
+ art_free(letter);
+*/
+ switch ( bit->bitmap.pixel_mode ) {
+ case FT_PIXEL_MODE_GRAY:
+ for (iy=0; iy < bit->bitmap.rows; iy++){
+ long buf_y = iy+(pen_y+0.5)-bit->top;
+ if (buf_y < 0 || buf_y >= (long)pys_height) continue;
+ buf_y *= rowstride;
+ for (ix=0;ix < bit->bitmap.width;ix++){
+ long buf_x = ix + (pen_x + 0.5) + (double)bit->left ;
+ art_u8 font_alpha;
+
+ if (buf_x < 0 || buf_x >= (long)pys_width) continue;
+ buf_x *= bytes_per_pixel ;
+ font_alpha = *(bit->bitmap.buffer + iy * bit->bitmap.pitch + ix);
+ if (font_alpha > 0){
+ fcolor[3] = (art_u8)((double)font_alpha / gr * falpha);
+ art_rgba_rgba_composite(buffer + buf_y + buf_x ,fcolor,1);
+ }
+ }
+ }
+ break;
+
+ case FT_PIXEL_MODE_MONO:
+ for (iy=0; iy < bit->bitmap.rows; iy++){
+ long buf_y = iy+(pen_y+0.5)-bit->top;
+ if (buf_y < 0 || buf_y >= (long)pys_height) continue;
+ buf_y *= rowstride;
+ for (ix=0;ix < bit->bitmap.width;ix++){
+ long buf_x = ix + (pen_x + 0.5) + (double)bit->left ;
+
+ if (buf_x < 0 || buf_x >= (long)pys_width) continue;
+ buf_x *= bytes_per_pixel ;
+ if ( (fcolor[3] = falpha * ((*(bit->bitmap.buffer + iy * bit->bitmap.pitch + ix/8) >> (7 - (ix % 8))) & 1)) > 0 )
+ art_rgba_rgba_composite(buffer + buf_y + buf_x ,fcolor,1);
+ }
+ }
+ break;
+
+ default:
+ rrd_set_error("unknown freetype pixel mode: %d", bit->bitmap.pixel_mode);
+ break;
+ }
+
+/*
+ for (iy=0; iy < bit->bitmap.rows; iy++){
+ long buf_y = iy+(pen_y+0.5)-bit->top;
+ if (buf_y < 0 || buf_y >= (long)pys_height) continue;
+ buf_y *= rowstride;
+ for (ix=0;ix < bit->bitmap.width;ix++){
+ long buf_x = ix + (pen_x + 0.5) + (double)bit->left ;
+ art_u8 font_alpha;
+
+ if (buf_x < 0 || buf_x >= (long)pys_width) continue;
+ buf_x *= bytes_per_pixel ;
+ font_alpha = *(bit->bitmap.buffer + iy * bit->bitmap.width + ix);
+ font_alpha = (art_u8)((double)font_alpha / gr * falpha);
+ for (iz = 0; iz < 3; iz++){
+ art_u8 *orig = buffer + buf_y + buf_x + iz;
+ *orig = (art_u8)((double)*orig / gr * ( gr - font_alpha) +
+ (double)fcolor[iz] / gr * (font_alpha));
+ }
+ }
+ }
+*/
+ FT_Done_Glyph (image);
+ }
+ gfx_string_destroy(string);
+ }
+ }
+ node = node->next;
+ }
+ gfx_save_png(buffer,fp , pys_width,pys_height,bytes_per_pixel);
+ art_free(buffer);
+ FT_Done_FreeType( library );
+ return 0;
+}
+
+/* free memory used by nodes this will also remove memory required for
+ associated paths and svcs ... but not for text strings */
+int
+gfx_destroy (gfx_canvas_t *canvas){
+ gfx_node_t *next,*node = canvas->firstnode;
+ while(node){
+ next = node->next;
+ art_free(node->path);
+ free(node->text);
+ free(node->filename);
+ art_free(node);
+ node = next;
+ }
+ art_free(canvas);
+ return 0;
+}
+
+static int gfx_save_png (art_u8 *buffer, FILE *fp, long width, long height, long bytes_per_pixel){
+ png_structp png_ptr = NULL;
+ png_infop info_ptr = NULL;
+ int i;
+ png_bytep *row_pointers;
+ int rowstride = width * bytes_per_pixel;
+ png_text text[2];
+
+ if (fp == NULL)
+ return (1);
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL);
+ if (png_ptr == NULL)
+ {
+ return (1);
+ }
+ row_pointers = (png_bytepp)png_malloc(png_ptr,
+ height*sizeof(png_bytep));
+
+ info_ptr = png_create_info_struct(png_ptr);
+
+ if (info_ptr == NULL)
+ {
+ png_free(png_ptr,row_pointers);
+ png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
+ return (1);
+ }
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ /* If we get here, we had a problem writing the file */
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ return (1);
+ }
+
+ png_init_io(png_ptr, fp);
+ png_set_IHDR (png_ptr, info_ptr,width, height,
+ 8, PNG_COLOR_TYPE_RGB_ALPHA,
+ PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+
+ text[0].key = "Software";
+ text[0].text = "RRDtool, Tobias Oetiker <tobi@oetike.ch>, http://tobi.oetiker.ch";
+ text[0].compression = PNG_TEXT_COMPRESSION_NONE;
+ png_set_text (png_ptr, info_ptr, text, 1);
+
+ /* lets make this fast while ending up with some increass in image size */
+ png_set_filter(png_ptr,0,PNG_FILTER_NONE);
+ /* png_set_filter(png_ptr,0,PNG_FILTER_SUB); */
+ png_set_compression_level(png_ptr,1);
+ /* png_set_compression_strategy(png_ptr,Z_HUFFMAN_ONLY); */
+ /*
+ png_set_filter(png_ptr,PNG_FILTER_TYPE_BASE,PNG_FILTER_SUB);
+ png_set_compression_strategy(png_ptr,Z_HUFFMAN_ONLY);
+ png_set_compression_level(png_ptr,Z_BEST_SPEED); */
+
+ /* Write header data */
+ png_write_info (png_ptr, info_ptr);
+ for (i = 0; i < height; i++)
+ row_pointers[i] = (png_bytep) (buffer + i*rowstride);
+
+ png_write_image(png_ptr, row_pointers);
+ png_write_end(png_ptr, info_ptr);
+ png_free(png_ptr,row_pointers);
+ png_destroy_write_struct(&png_ptr, &info_ptr);
+ return 1;
+}
+
+
+/* ----- COMMON ROUTINES for pdf, svg and eps */
+#define min3(a, b, c) (a < b ? (a < c ? a : c) : (b < c ? b : c))
+#define max3(a, b, c) (a > b ? (a > c ? a : c) : (b > c ? b : c))
+
+#define PDF_CALC_DEBUG 0
+
+typedef struct pdf_point
+{
+ double x, y;
+} pdf_point;
+
+typedef struct
+{
+ double ascender, descender, baselineY;
+ pdf_point sizep, minp, maxp;
+ double x, y, tdx, tdy;
+ double r, cos_r, sin_r;
+ double ma, mb, mc, md, mx, my; /* pdf coord matrix */
+ double tmx, tmy; /* last 2 coords of text coord matrix */
+#if PDF_CALC_DEBUG
+ int debug;
+#endif
+} pdf_coords;
+
+#if PDF_CALC_DEBUG
+static void pdf_dump_calc(gfx_node_t *node, pdf_coords *g)
+{
+ fprintf(stderr, "PDF CALC =============================\n");
+ fprintf(stderr, " '%s' at %f pt\n", node->text, node->size);
+ fprintf(stderr, " align h = %s, v = %s, sizep = %f, %f\n",
+ (node->halign == GFX_H_RIGHT ? "r" :
+ (node->halign == GFX_H_CENTER ? "c" :
+ (node->halign == GFX_H_LEFT ? "l" : "N"))),
+ (node->valign == GFX_V_TOP ? "t" :
+ (node->valign == GFX_V_CENTER ? "c" :
+ (node->valign == GFX_V_BOTTOM ? "b" : "N"))),
+ g->sizep.x, g->sizep.y);
+ fprintf(stderr, " r = %f = %f, cos = %f, sin = %f\n",
+ g->r, node->angle, g->cos_r, g->sin_r);
+ fprintf(stderr, " ascender = %f, descender = %f, baselineY = %f\n",
+ g->ascender, g->descender, g->baselineY);
+ fprintf(stderr, " sizep: %f, %f\n", g->sizep.x, g->sizep.y);
+ fprintf(stderr, " minp: %f, %f maxp = %f, %f\n",
+ g->minp.x, g->minp.y, g->maxp.x, g->maxp.y);
+ fprintf(stderr, " x = %f, y = %f\n", g->x, g->y);
+ fprintf(stderr, " tdx = %f, tdy = %f\n", g->tdx, g->tdy);
+ fprintf(stderr, " GM = %f, %f, %f, %f, %f, %f\n",
+ g->ma, g->mb, g->mc, g->md, g->mx, g->my);
+ fprintf(stderr, " TM = %f, %f, %f, %f, %f, %f\n",
+ g->ma, g->mb, g->mc, g->md, g->tmx, g->tmy);
+}
+#endif
+
+#if PDF_CALC_DEBUG
+#define PDF_DD(x) if (g->debug) x;
+#else
+#define PDF_DD(x)
+#endif
+
+static void pdf_rotate(pdf_coords *g, pdf_point *p)
+{
+ double x2 = g->cos_r * p->x - g->sin_r * p->y;
+ double y2 = g->sin_r * p->x + g->cos_r * p->y;
+ PDF_DD( fprintf(stderr, " rotate(%f, %f) -> %f, %f\n", p->x, p->y, x2, y2))
+ p->x = x2;
+ p->y = y2;
+}
+
+
+static void pdf_calc(int page_height, gfx_node_t *node, pdf_coords *g)
+{
+ pdf_point a, b, c;
+#if PDF_CALC_DEBUG
+ /* g->debug = !!strstr(node->text, "RevProxy-1") || !!strstr(node->text, "08:00"); */
+ g->debug = !!strstr(node->text, "sekunder") || !!strstr(node->text, "Web");
+#endif
+ g->x = node->x;
+ g->y = page_height - node->y;
+ if (node->angle) {
+ g->r = 2 * M_PI * node->angle / 360.0;
+ g->cos_r = cos(g->r);
+ g->sin_r = sin(g->r);
+ } else {
+ g->r = 0;
+ g->cos_r = 1;
+ g->sin_r = 0;
+ }
+ g->ascender = afm_get_ascender(node->filename, node->size);
+ g->descender = afm_get_descender(node->filename, node->size);
+ g->sizep.x = afm_get_text_width(0, node->filename, node->size, node->tabwidth, node->text);
+ /* seems like libart ignores the descender when doing vertial-align = bottom,
+ so we do that too, to get labels v-aligning properly */
+ g->sizep.y = -g->ascender; /* + afm_get_descender(font->ps_font, node->size); */
+ g->baselineY = -g->ascender - g->sizep.y / 2;
+ a.x = g->sizep.x; a.y = g->sizep.y;
+ b.x = g->sizep.x; b.y = 0;
+ c.x = 0; c.y = g->sizep.y;
+ if (node->angle) {
+ pdf_rotate(g, &a);
+ pdf_rotate(g, &b);
+ pdf_rotate(g, &c);
+ }
+ g->minp.x = min3(a.x, b.x, c.x);
+ g->minp.y = min3(a.y, b.y, c.y);
+ g->maxp.x = max3(a.x, b.x, c.x);
+ g->maxp.y = max3(a.y, b.y, c.y);
+ /* The alignment parameters in node->valign and node->halign
+ specifies the alignment in the non-rotated coordinate system
+ (very unlike pdf/postscript), which complicates matters.
+ */
+ switch (node->halign) {
+ case GFX_H_RIGHT: g->tdx = -g->maxp.x; break;
+ case GFX_H_CENTER: g->tdx = -(g->maxp.x + g->minp.x) / 2; break;
+ case GFX_H_LEFT: g->tdx = -g->minp.x; break;
+ case GFX_H_NULL: g->tdx = 0; break;
+ }
+ switch(node->valign){
+ case GFX_V_TOP: g->tdy = -g->maxp.y; break;
+ case GFX_V_CENTER: g->tdy = -(g->maxp.y + g->minp.y) / 2; break;
+ case GFX_V_BOTTOM: g->tdy = -g->minp.y; break;
+ case GFX_V_NULL: g->tdy = 0; break;
+ }
+ g->ma = g->cos_r;
+ g->mb = g->sin_r;
+ g->mc = -g->sin_r;
+ g->md = g->cos_r;
+ g->mx = g->x + g->tdx;
+ g->my = g->y + g->tdy;
+ g->tmx = g->mx - g->ascender * g->mc;
+ g->tmy = g->my - g->ascender * g->md;
+ PDF_DD(pdf_dump_calc(node, g))
+}
+
+/* ------- SVG -------
+ SVG reference:
+ http://www.w3.org/TR/SVG/
+*/
+static int svg_indent = 0;
+static int svg_single_line = 0;
+static const char *svg_default_font = "-dummy-";
+typedef struct svg_dash
+{
+ int dash_enable;
+ double dash_adjust, dash_len, dash_offset;
+ double adjusted_on, adjusted_off;
+} svg_dash;
+
+
+static void svg_print_indent(FILE *fp)
+{
+ int i;
+ for (i = svg_indent - svg_single_line; i > 0; i--) {
+ putc(' ', fp);
+ putc(' ', fp);
+ }
+}
+
+static void svg_start_tag(FILE *fp, const char *name)
+{
+ svg_print_indent(fp);
+ putc('<', fp);
+ fputs(name, fp);
+ svg_indent++;
+}
+
+static void svg_close_tag_single_line(FILE *fp)
+{
+ svg_single_line++;
+ putc('>', fp);
+}
+
+static void svg_close_tag(FILE *fp)
+{
+ putc('>', fp);
+ if (!svg_single_line)
+ putc('\n', fp);
+}
+
+static void svg_end_tag(FILE *fp, const char *name)
+{
+ /* name is NULL if closing empty-node tag */
+ svg_indent--;
+ if (svg_single_line)
+ svg_single_line--;
+ else if (name)
+ svg_print_indent(fp);
+ if (name != NULL) {
+ fputs("</", fp);
+ fputs(name, fp);
+ } else {
+ putc('/', fp);
+ }
+ svg_close_tag(fp);
+}
+
+static void svg_close_tag_empty_node(FILE *fp)
+{
+ svg_end_tag(fp, NULL);
+}
+
+static void svg_write_text(FILE *fp, const char *text)
+{
+#ifdef HAVE_MBSTOWCS
+ size_t clen;
+ wchar_t *p, *cstr, ch;
+ int text_count;
+ if (!text)
+ return;
+ clen = strlen(text) + 1;
+ cstr = malloc(sizeof(wchar_t) * clen);
+ text_count = mbstowcs(cstr, text, clen);
+ if (text_count == -1)
+ text_count = mbstowcs(cstr, "Enc-Err", 6);
+ p = cstr;
+#else
+ unsigned char *p = text;
+ unsigned char *cstr;
+ char ch;
+ if (!p)
+ return;
+#endif
+ while (1) {
+ ch = *p++;
+ ch = afm_fix_osx_charset(ch); /* unsafe macro */
+ switch (ch) {
+ case 0:
+#ifdef HAVE_MBSTOWCS
+ free(cstr);
+#endif
+ return;
+ case '&': fputs("&", fp); break;
+ case '<': fputs("<", fp); break;
+ case '>': fputs(">", fp); break;
+ case '"': fputs(""", fp); break;
+ default:
+ if (ch == 32) {
+#ifdef HAVE_MBSTOWCS
+ if (p <= cstr + 1 || !*p || *p == 32)
+ fputs(" ", fp); /* non-breaking space in unicode */
+ else
+#endif
+ fputc(32, fp);
+ } else if (ch < 32 || ch >= 127)
+ fprintf(fp, "&#%d;", (int)ch);
+ else
+ putc((char)ch, fp);
+ }
+ }
+}
+
+static void svg_format_number(char *buf, int bufsize, double d)
+{
+ /* omit decimals if integer to reduce filesize */
+ char *p;
+ snprintf(buf, bufsize, "%.2f", d);
+ p = buf; /* doesn't trust snprintf return value */
+ while (*p)
+ p++;
+ while (--p > buf) {
+ char ch = *p;
+ if (ch == '0') {
+ *p = '\0'; /* zap trailing zeros */
+ continue;
+ }
+ if (ch == '.')
+ *p = '\0'; /* zap trailing dot */
+ break;
+ }
+}
+
+static void svg_write_number(FILE *fp, double d)
+{
+ char buf[60];
+ svg_format_number(buf, sizeof(buf), d);
+ fputs(buf, fp);
+}
+
+static int svg_color_is_black(int c)
+{
+ /* gfx_color_t is RRGGBBAA */
+ return c == 0x000000FF;
+}
+
+static void svg_write_color(FILE *fp, gfx_color_t c, const char *attr)
+{
+ /* gfx_color_t is RRGGBBAA, svg can use #RRGGBB and #RGB like html */
+ gfx_color_t rrggbb = (int)((c >> 8) & 0xFFFFFF);
+ gfx_color_t opacity = c & 0xFF;
+ fprintf(fp, " %s=\"", attr);
+ if ((rrggbb & 0x0F0F0F) == ((rrggbb >> 4) & 0x0F0F0F)) {
+ /* css2 short form, #rgb is #rrggbb, not #r0g0b0 */
+ fprintf(fp, "#%03lX",
+ ( ((rrggbb >> 8) & 0xF00)
+ | ((rrggbb >> 4) & 0x0F0)
+ | ( rrggbb & 0x00F)));
+ } else {
+ fprintf(fp, "#%06lX", rrggbb);
+ }
+ fputs("\"", fp);
+ if (opacity != 0xFF) {
+ fprintf(fp, " opacity=\"");
+ svg_write_number(fp, opacity / 255.0);
+ fputs("\"", fp);
+ }
+}
+
+static void svg_get_dash(gfx_node_t *node, svg_dash *d)
+{
+ double offset;
+ int mult;
+ if (node->dash_on <= 0 || node->dash_off <= 0) {
+ d->dash_enable = 0;
+ return;
+ }
+ d->dash_enable = 1;
+ d->dash_len = node->dash_on + node->dash_off;
+ /* dash on/off adjustment due to round caps */
+ d->dash_adjust = 0.8 * node->size;
+ d->adjusted_on = node->dash_on - d->dash_adjust;
+ if (d->adjusted_on < 0.01)
+ d->adjusted_on = 0.01;
+ d->adjusted_off = d->dash_len - d->adjusted_on;
+ /* dash offset calc */
+ if (node->path[0].x == node->path[1].x) /* only good for horz/vert lines */
+ offset = node->path[0].y;
+ else
+ offset = node->path[0].x;
+ mult = (int)fabs(offset / d->dash_len);
+ d->dash_offset = offset - mult * d->dash_len;
+ if (node->path[0].x < node->path[1].x || node->path[0].y < node->path[1].y)
+ d->dash_offset = d->dash_len - d->dash_offset;
+}
+
+static int svg_dash_equal(svg_dash *a, svg_dash *b)
+{
+ if (a->dash_enable != b->dash_enable)
+ return 0;
+ if (a->adjusted_on != b->adjusted_on)
+ return 0;
+ if (a->adjusted_off != b->adjusted_off)
+ return 0;
+ /* rest of properties will be the same when on+off are */
+ return 1;
+}
+
+static void svg_common_path_attributes(FILE *fp, gfx_node_t *node)
+{
+ svg_dash dash_info;
+ svg_get_dash(node, &dash_info);
+ fputs(" stroke-width=\"", fp);
+ svg_write_number(fp, node->size);
+ fputs("\"", fp);
+ svg_write_color(fp, node->color, "stroke");
+ fputs(" fill=\"none\"", fp);
+ if (dash_info.dash_enable) {
+ if (dash_info.dash_offset != 0) {
+ fputs(" stroke-dashoffset=\"", fp);
+ svg_write_number(fp, dash_info.dash_offset);
+ fputs("\"", fp);
+ }
+ fputs(" stroke-dasharray=\"", fp);
+ svg_write_number(fp, dash_info.adjusted_on);
+ fputs(",", fp);
+ svg_write_number(fp, dash_info.adjusted_off);
+ fputs("\"", fp);
+ }
+}
+
+static int svg_is_int_step(double a, double b)
+{
+ double diff = fabs(a - b);
+ return floor(diff) == diff;
+}
+
+static int svg_path_straight_segment(FILE *fp,
+ double lastA, double currentA, double currentB,
+ gfx_node_t *node,
+ int segment_idx, int isx, char absChar, char relChar)
+{
+ if (!svg_is_int_step(lastA, currentA)) {
+ putc(absChar, fp);
+ svg_write_number(fp, currentA);
+ return 0;
+ }
+ if (segment_idx < node->points - 1) {
+ ArtVpath *vec = node->path + segment_idx + 1;
+ if (vec->code == ART_LINETO) {
+ double nextA = (isx ? vec->x : vec->y) - LINEOFFSET;
+ double nextB = (isx ? vec->y : vec->x) - LINEOFFSET;
+ if (nextB == currentB
+ && ((currentA >= lastA) == (nextA >= currentA))
+ && svg_is_int_step(currentA, nextA)) {
+ return 1; /* skip to next as it is a straight line */
+ }
+ }
+ }
+ putc(relChar, fp);
+ svg_write_number(fp, currentA - lastA);
+ return 0;
+}
+
+static void svg_path(FILE *fp, gfx_node_t *node, int multi)
+{
+ int i;
+ double lastX = 0, lastY = 0;
+ /* for straight lines <path..> tags take less space than
+ <line..> tags because of the efficient packing
+ in the 'd' attribute */
+ svg_start_tag(fp, "path");
+ if (!multi)
+ svg_common_path_attributes(fp, node);
+ fputs(" d=\"", fp);
+ /* specification of the 'd' attribute: */
+ /* http://www.w3.org/TR/SVG/paths.html#PathDataGeneralInformation */
+ for (i = 0; i < node->points; i++) {
+ ArtVpath *vec = node->path + i;
+ double x = vec->x - LINEOFFSET;
+ double y = vec->y - LINEOFFSET;
+ switch (vec->code) {
+ case ART_MOVETO_OPEN: /* fall-through */
+ case ART_MOVETO:
+ putc('M', fp);
+ svg_write_number(fp, x);
+ putc(',', fp);
+ svg_write_number(fp, y);
+ break;
+ case ART_LINETO:
+ /* try optimize filesize by using minimal lineto commands */
+ /* without introducing rounding errors. */
+ if (x == lastX) {
+ if (svg_path_straight_segment(fp, lastY, y, x, node, i, 0, 'V', 'v'))
+ continue;
+ } else if (y == lastY) {
+ if (svg_path_straight_segment(fp, lastX, x, y, node, i, 1, 'H', 'h'))
+ continue;
+ } else {
+ putc('L', fp);
+ svg_write_number(fp, x);
+ putc(',', fp);
+ svg_write_number(fp, y);
+ }
+ break;
+ case ART_CURVETO: break; /* unsupported */
+ case ART_END: break; /* nop */
+ }
+ lastX = x;
+ lastY = y;
+ }
+ if (node->closed_path)
+ fputs(" Z", fp);
+ fputs("\"", fp);
+ svg_close_tag_empty_node(fp);
+}
+
+static void svg_multi_path(FILE *fp, gfx_node_t **nodeR)
+{
+ /* optimize for multiple paths with the same color, penwidth, etc. */
+ int num = 1;
+ gfx_node_t *node = *nodeR;
+ gfx_node_t *next = node->next;
+ while (next) {
+ if (next->type != node->type
+ || next->size != node->size
+ || next->color != node->color
+ || next->dash_on != node->dash_on
+ || next->dash_off != node->dash_off)
+ break;
+ next = next->next;
+ num++;
+ }
+ if (num == 1) {
+ svg_path(fp, node, 0);
+ return;
+ }
+ svg_start_tag(fp, "g");
+ svg_common_path_attributes(fp, node);
+ svg_close_tag(fp);
+ while (num && node) {
+ svg_path(fp, node, 1);
+ if (!--num)
+ break;
+ node = node->next;
+ *nodeR = node;
+ }
+ svg_end_tag(fp, "g");
+}
+
+static void svg_area(FILE *fp, gfx_node_t *node)
+{
+ int i;
+ double startX = 0, startY = 0;
+ svg_start_tag(fp, "polygon");
+ fputs(" ", fp);
+ svg_write_color(fp, node->color, "fill");
+ fputs(" points=\"", fp);
+ for (i = 0; i < node->points; i++) {
+ ArtVpath *vec = node->path + i;
+ double x = vec->x - LINEOFFSET;
+ double y = vec->y - LINEOFFSET;
+ switch (vec->code) {
+ case ART_MOVETO_OPEN: /* fall-through */
+ case ART_MOVETO:
+ svg_write_number(fp, x);
+ putc(',', fp);
+ svg_write_number(fp, y);
+ startX = x;
+ startY = y;
+ break;
+ case ART_LINETO:
+ if (i == node->points - 2
+ && node->path[i + 1].code == ART_END
+ && fabs(x - startX) < 0.001 && fabs(y - startY) < 0.001) {
+ break; /* poly area always closed, no need for last point */
+ }
+ putc(' ', fp);
+ svg_write_number(fp, x);
+ putc(',', fp);
+ svg_write_number(fp, y);
+ break;
+ case ART_CURVETO: break; /* unsupported */
+ case ART_END: break; /* nop */
+ }
+ }
+ fputs("\"", fp);
+ svg_close_tag_empty_node(fp);
+}
+
+static void svg_text(FILE *fp, gfx_node_t *node)
+{
+ pdf_coords g;
+ const char *fontname;
+ /* as svg has 0,0 in top-left corner (like most screens) instead of
+ bottom-left corner like pdf and eps, we have to fake the coords
+ using offset and inverse sin(r) value */
+ int page_height = 1000;
+ pdf_calc(page_height, node, &g);
+ if (node->angle != 0) {
+ svg_start_tag(fp, "g");
+ /* can't use svg_write_number as 2 decimals is far from enough to avoid
+ skewed text */
+ fprintf(fp, " transform=\"matrix(%f,%f,%f,%f,%f,%f)\"",
+ g.ma, -g.mb, -g.mc, g.md, g.tmx, page_height - g.tmy);
+ svg_close_tag(fp);
+ }
+ svg_start_tag(fp, "text");
+ if (!node->angle) {
+ fputs(" x=\"", fp);
+ svg_write_number(fp, g.tmx);
+ fputs("\" y=\"", fp);
+ svg_write_number(fp, page_height - g.tmy);
+ fputs("\"", fp);
+ }
+ fontname = afm_get_font_name(node->filename);
+ if (strcmp(fontname, svg_default_font))
+ fprintf(fp, " font-family=\"%s\"", fontname);
+ fputs(" font-size=\"", fp);
+ svg_write_number(fp, node->size);
+ fputs("\"", fp);
+ if (!svg_color_is_black(node->color))
+ svg_write_color(fp, node->color, "fill");
+ svg_close_tag_single_line(fp);
+ /* support for node->tabwidth missing */
+ svg_write_text(fp, node->text);
+ svg_end_tag(fp, "text");
+ if (node->angle != 0)
+ svg_end_tag(fp, "g");
+}
+
+int gfx_render_svg (gfx_canvas_t *canvas,
+ art_u32 width, art_u32 height,
+ gfx_color_t background, FILE *fp){
+ gfx_node_t *node = canvas->firstnode;
+ /* Find the first font used, and assume it is the mostly used
+ one. It reduces the number of font-familty attributes. */
+ while (node) {
+ if (node->type == GFX_TEXT && node->filename) {
+ svg_default_font = afm_get_font_name(node->filename);
+ break;
+ }
+ node = node->next;
+ }
+ fputs(
+"<?xml version=\"1.0\" standalone=\"no\"?>\n"
+"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\"\n"
+" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"
+"<!--\n"
+" SVG file created by\n"
+" RRDtool " PACKAGE_VERSION " Tobias Oetiker, http://tobi.oetiker.ch\n"
+"\n"
+" The width/height attributes in the outhermost svg node\n"
+" are just default sizes for the browser which is used\n"
+" if the svg file is openened directly without being\n"
+" embedded in an html file.\n"
+" The viewBox is the local coord system for rrdtool.\n"
+"-->\n", fp);
+ svg_start_tag(fp, "svg");
+ fputs(" width=\"", fp);
+ svg_write_number(fp, width * canvas->zoom);
+ fputs("\" height=\"", fp);
+ svg_write_number(fp, height * canvas->zoom);
+ fputs("\" x=\"0\" y=\"0\" viewBox=\"", fp);
+ svg_write_number(fp, -LINEOFFSET);
+ fputs(" ", fp);
+ svg_write_number(fp, -LINEOFFSET);
+ fputs(" ", fp);
+ svg_write_number(fp, width - LINEOFFSET);
+ fputs(" ", fp);
+ svg_write_number(fp, height - LINEOFFSET);
+ fputs("\" preserveAspectRatio=\"xMidYMid\"", fp);
+ fprintf(fp, " font-family=\"%s\"", svg_default_font); /* default font */
+ fputs(" stroke-linecap=\"round\" stroke-linejoin=\"round\"", fp);
+ fputs(" xmlns=\"http://www.w3.org/2000/svg\"", fp);
+ fputs(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"", fp);
+ svg_close_tag(fp);
+ svg_start_tag(fp, "rect");
+ fprintf(fp, " x=\"0\" y=\"0\" width=\"%d\" height=\"%d\"", width, height);
+ svg_write_color(fp, background, "fill");
+ svg_close_tag_empty_node(fp);
+ node = canvas->firstnode;
+ while (node) {
+ switch (node->type) {
+ case GFX_LINE:
+ svg_multi_path(fp, &node);
+ break;
+ case GFX_AREA:
+ svg_area(fp, node);
+ break;
+ case GFX_TEXT:
+ svg_text(fp, node);
+ }
+ node = node->next;
+ }
+ svg_end_tag(fp, "svg");
+ return 0;
+}
+
+/* ------- EPS -------
+ EPS and Postscript references:
+ http://partners.adobe.com/asn/developer/technotes/postscript.html
+*/
+
+typedef struct eps_font
+{
+ const char *ps_font;
+ int id;
+ struct eps_font *next;
+} eps_font;
+
+typedef struct eps_state
+{
+ FILE *fp;
+ gfx_canvas_t *canvas;
+ art_u32 page_width, page_height;
+ eps_font *font_list;
+ /*--*/
+ gfx_color_t color;
+ const char *font;
+ double font_size;
+ double line_width;
+ int linecap, linejoin;
+ int has_dash;
+} eps_state;
+
+static void eps_set_color(eps_state *state, gfx_color_t color)
+{
+#if USE_EPS_FAKE_ALPHA
+ double a1, a2;
+#endif
+ /* gfx_color_t is RRGGBBAA */
+ if (state->color == color)
+ return;
+#if USE_EPS_FAKE_ALPHA
+ a1 = (color & 255) / 255.0;
+ a2 = 255 * (1 - a1);
+#define eps_color_calc(x) (int)( ((x) & 255) * a1 + a2)
+#else
+#define eps_color_calc(x) (int)( (x) & 255)
+#endif
+ /* gfx_color_t is RRGGBBAA */
+ if (state->color == color)
+ return;
+ fprintf(state->fp, "%d %d %d Rgb\n",
+ eps_color_calc(color >> 24),
+ eps_color_calc(color >> 16),
+ eps_color_calc(color >> 8));
+ state->color = color;
+}
+
+static int eps_add_font(eps_state *state, gfx_node_t *node)
+{
+ /* The fonts list could be postponed to the end using
+ (atend), but let's be nice and have them in the header. */
+ const char *ps_font = afm_get_font_postscript_name(node->filename);
+ eps_font *ef;
+ for (ef = state->font_list; ef; ef = ef->next) {
+ if (!strcmp(ps_font, ef->ps_font))
+ return 0;
+ }
+ ef = malloc(sizeof(eps_font));
+ if (ef == NULL) {
+ rrd_set_error("malloc for eps_font");
+ return -1;
+ }
+ ef->next = state->font_list;
+ ef->ps_font = ps_font;
+ state->font_list = ef;
+ return 0;
+}
+
+static void eps_list_fonts(eps_state *state, const char *dscName)
+{
+ eps_font *ef;
+ int lineLen = strlen(dscName);
+ if (!state->font_list)
+ return;
+ fputs(dscName, state->fp);
+ for (ef = state->font_list; ef; ef = ef->next) {
+ int nameLen = strlen(ef->ps_font);
+ if (lineLen + nameLen > 100 && lineLen) {
+ fputs("\n", state->fp);
+ fputs("%%- \n", state->fp);
+ lineLen = 5;
+ } else {
+ fputs(" ", state->fp);
+ lineLen++;
+ }
+ fputs(ef->ps_font, state->fp);
+ lineLen += nameLen;
+ }
+ fputs("\n", state->fp);
+}
+
+static void eps_define_fonts(eps_state *state)
+{
+ eps_font *ef;
+ if (!state->font_list)
+ return;
+ for (ef = state->font_list; ef; ef = ef->next) {
+ /* PostScript¨ LANGUAGE REFERENCE third edition
+ page 349 */
+ fprintf(state->fp,
+ "%%\n"
+ "/%s findfont dup length dict begin\n"
+ "{ 1 index /FID ne {def} {pop pop} ifelse } forall\n"
+ "/Encoding ISOLatin1Encoding def\n"
+ "currentdict end\n"
+ "/%s-ISOLatin1 exch definefont pop\n"
+ "/SetFont-%s { /%s-ISOLatin1 findfont exch scalefont setfont } bd\n",
+ ef->ps_font, ef->ps_font, ef->ps_font, ef->ps_font);
+ }
+}
+
+static int eps_prologue(eps_state *state)
+{
+ gfx_node_t *node;
+ fputs(
+ "%!PS-Adobe-3.0 EPSF-3.0\n"
+ "%%Creator: RRDtool " PACKAGE_VERSION " Tobias Oetiker, http://tobi.oetiker.ch\n"
+ /* can't like weird chars here */
+ "%%Title: (RRDtool output)\n"
+ "%%DocumentData: Clean7Bit\n"
+ "", state->fp);
+ fprintf(state->fp, "%%%%BoundingBox: 0 0 %d %d\n",
+ state->page_width, state->page_height);
+ for (node = state->canvas->firstnode; node; node = node->next) {
+ if (node->type == GFX_TEXT && eps_add_font(state, node) == -1)
+ return -1;
+ }
+ eps_list_fonts(state, "%%DocumentFonts:");
+ eps_list_fonts(state, "%%DocumentNeededFonts:");
+ fputs(
+ "%%EndComments\n"
+ "%%BeginProlog\n"
+ "%%EndProlog\n" /* must have, or BoundingBox is ignored */
+ "/bd { bind def } bind def\n"
+ "", state->fp);
+ fprintf(state->fp, "/X { %.2f add } bd\n", LINEOFFSET);
+ fputs(
+ "/X2 {X exch X exch} bd\n"
+ "/M {X2 moveto} bd\n"
+ "/L {X2 lineto} bd\n"
+ "/m {moveto} bd\n"
+ "/l {lineto} bd\n"
+ "/S {stroke} bd\n"
+ "/CP {closepath} bd\n"
+ "/WS {setlinewidth stroke} bd\n"
+ "/F {fill} bd\n"
+ "/T1 {gsave} bd\n"
+ "/T2 {concat 0 0 moveto show grestore} bd\n"
+ "/T {moveto show} bd\n"
+ "/Rgb { 255.0 div 3 1 roll\n"
+ " 255.0 div 3 1 roll \n"
+ " 255.0 div 3 1 roll setrgbcolor } bd\n"
+ "", state->fp);
+ eps_define_fonts(state);
+ return 0;
+}
+
+static void eps_clear_dash(eps_state *state)
+{
+ if (!state->has_dash)
+ return;
+ state->has_dash = 0;
+ fputs("[1 0] 0 setdash\n", state->fp);
+}
+
+static void eps_write_linearea(eps_state *state, gfx_node_t *node)
+{
+ int i;
+ FILE *fp = state->fp;
+ int useOffset = 0;
+ int clearDashIfAny = 1;
+ eps_set_color(state, node->color);
+ if (node->type == GFX_LINE) {
+ svg_dash dash_info;
+ if (state->linecap != 1) {
+ fputs("1 setlinecap\n", fp);
+ state->linecap = 1;
+ }
+ if (state->linejoin != 1) {
+ fputs("1 setlinejoin\n", fp);
+ state->linejoin = 1;
+ }
+ svg_get_dash(node, &dash_info);
+ if (dash_info.dash_enable) {
+ clearDashIfAny = 0;
+ state->has_dash = 1;
+ fputs("[", fp);
+ svg_write_number(fp, dash_info.adjusted_on);
+ fputs(" ", fp);
+ svg_write_number(fp, dash_info.adjusted_off);
+ fputs("] ", fp);
+ svg_write_number(fp, dash_info.dash_offset);
+ fputs(" setdash\n", fp);
+ }
+ }
+ if (clearDashIfAny)
+ eps_clear_dash(state);
+ for (i = 0; i < node->points; i++) {
+ ArtVpath *vec = node->path + i;
+ double x = vec->x;
+ double y = state->page_height - vec->y;
+ if (vec->code == ART_MOVETO_OPEN || vec->code == ART_MOVETO)
+ useOffset = (fabs(x - floor(x) - 0.5) < 0.01 && fabs(y - floor(y) - 0.5) < 0.01);
+ if (useOffset) {
+ x -= LINEOFFSET;
+ y -= LINEOFFSET;
+ }
+ switch (vec->code) {
+ case ART_MOVETO_OPEN: /* fall-through */
+ case ART_MOVETO:
+ svg_write_number(fp, x);
+ fputc(' ', fp);
+ svg_write_number(fp, y);
+ fputc(' ', fp);
+ fputs(useOffset ? "M\n" : "m\n", fp);
+ break;
+ case ART_LINETO:
+ svg_write_number(fp, x);
+ fputc(' ', fp);
+ svg_write_number(fp, y);
+ fputc(' ', fp);
+ fputs(useOffset ? "L\n" : "l\n", fp);
+ break;
+ case ART_CURVETO: break; /* unsupported */
+ case ART_END: break; /* nop */
+ }
+ }
+ if (node->type == GFX_LINE) {
+ if (node->closed_path)
+ fputs("CP ", fp);
+ if (node->size != state->line_width) {
+ state->line_width = node->size;
+ svg_write_number(fp, state->line_width);
+ fputs(" WS\n", fp);
+ } else {
+ fputs("S\n", fp);
+ }
+ } else {
+ fputs("F\n", fp);
+ }
+}
+
+static void eps_write_text(eps_state *state, gfx_node_t *node)
+{
+ FILE *fp = state->fp;
+ const char *ps_font = afm_get_font_postscript_name(node->filename);
+ int lineLen = 0;
+ pdf_coords g;
+#ifdef HAVE_MBSTOWCS
+ size_t clen;
+ wchar_t *p, *cstr, ch;
+ int text_count;
+ if (!node->text)
+ return;
+ clen = strlen(node->text) + 1;
+ cstr = malloc(sizeof(wchar_t) * clen);
+ text_count = mbstowcs(cstr, node->text, clen);
+ if (text_count == -1)
+ text_count = mbstowcs(cstr, "Enc-Err", 6);
+ p = cstr;
+#else
+ const unsigned char *p = node->text;
+ unsigned char ch;
+ if (!p)
+ return;
+#endif
+ pdf_calc(state->page_height, node, &g);
+ eps_set_color(state, node->color);
+ if (strcmp(ps_font, state->font) || node->size != state->font_size) {
+ state->font = ps_font;
+ state->font_size = node->size;
+ svg_write_number(fp, state->font_size);
+ fprintf(fp, " SetFont-%s\n", state->font);
+ }
+ if (node->angle)
+ fputs("T1 ", fp);
+ fputs("(", fp);
+ lineLen = 20;
+ while (1) {
+ ch = *p;
+ if (!ch)
+ break;
+ ch = afm_fix_osx_charset(ch); /* unsafe macro */
+ if (++lineLen > 70) {
+ fputs("\\\n", fp); /* backslash and \n */
+ lineLen = 0;
+ }
+ switch (ch) {
+ case '%':
+ case '(':
+ case ')':
+ case '\\':
+ fputc('\\', fp);
+ fputc(ch, fp);
+ break;
+ case '\n':
+ fputs("\\n", fp);
+ break;
+ case '\r':
+ fputs("\\r", fp);
+ break;
+ case '\t':
+ fputs("\\t", fp);
+ break;
+ default:
+ if (ch > 255) {
+ fputc('?', fp);
+ } else if (ch >= 126 || ch < 32) {
+ fprintf(fp, "\\%03o", (unsigned int)ch);
+ lineLen += 3;
+ } else {
+ fputc(ch, fp);
+ }
+ }
+ p++;
+ }
+#ifdef HAVE_MBSTOWCS
+ free(cstr);
+#endif
+ if (node->angle) {
+ /* can't use svg_write_number as 2 decimals is far from enough to avoid
+ skewed text */
+ fprintf(fp, ") [%f %f %f %f %f %f] T2\n",
+ g.ma, g.mb, g.mc, g.md, g.tmx, g.tmy);
+ } else {
+ fputs(") ", fp);
+ svg_write_number(fp, g.tmx);
+ fputs(" ", fp);
+ svg_write_number(fp, g.tmy);
+ fputs(" T\n", fp);
+ }
+}
+
+static int eps_write_content(eps_state *state)
+{
+ gfx_node_t *node;
+ fputs("%\n", state->fp);
+ for (node = state->canvas->firstnode; node; node = node->next) {
+ switch (node->type) {
+ case GFX_LINE:
+ case GFX_AREA:
+ eps_write_linearea(state, node);
+ break;
+ case GFX_TEXT:
+ eps_write_text(state, node);
+ break;
+ }
+ }
+ return 0;
+}
+
+int gfx_render_eps (gfx_canvas_t *canvas,
+ art_u32 width, art_u32 height,
+ gfx_color_t background, FILE *fp){
+ struct eps_state state;
+ state.fp = fp;
+ state.canvas = canvas;
+ state.page_width = width;
+ state.page_height = height;
+ state.font = "no-default-font";
+ state.font_size = -1;
+ state.color = 0; /* black */
+ state.font_list = NULL;
+ state.linecap = -1;
+ state.linejoin = -1;
+ state.has_dash = 0;
+ state.line_width = 1;
+ if (eps_prologue(&state) == -1)
+ return -1;
+ eps_set_color(&state, background);
+ fprintf(fp, "0 0 M 0 %d L %d %d L %d 0 L fill\n",
+ height, width, height, width);
+ if (eps_write_content(&state) == -1)
+ return 0;
+ fputs("showpage\n", fp);
+ fputs("%%EOF\n", fp);
+ while (state.font_list) {
+ eps_font *next = state.font_list->next;
+ free(state.font_list);
+ state.font_list = next;
+ }
+ return 0;
+}
+
+/* ------- PDF -------
+ PDF references page:
+ http://partners.adobe.com/public/developer/pdf/index_reference.html
+*/
+
+typedef struct pdf_buffer
+{
+ int id, is_obj, is_dict, is_stream, pdf_file_pos;
+ char *data;
+ int alloc_size, current_size;
+ struct pdf_buffer *previous_buffer, *next_buffer;
+ struct pdf_state *state;
+} pdf_buffer;
+
+typedef struct pdf_font
+{
+ const char *ps_font;
+ pdf_buffer obj;
+ struct pdf_font *next;
+} pdf_font;
+
+typedef struct pdf_state
+{
+ FILE *fp;
+ gfx_canvas_t *canvas;
+ art_u32 page_width, page_height;
+ pdf_font *font_list;
+ pdf_buffer *first_buffer, *last_buffer;
+ int pdf_file_pos;
+ int has_failed;
+ /*--*/
+ gfx_color_t stroke_color, fill_color;
+ int font_id;
+ double font_size;
+ double line_width;
+ svg_dash dash;
+ int linecap, linejoin;
+ int last_obj_id;
+ /*--*/
+ pdf_buffer pdf_header;
+ pdf_buffer info_obj, catalog_obj, pages_obj, page1_obj;
+ pdf_buffer fontsdict_obj;
+ pdf_buffer graph_stream;
+} pdf_state;
+
+static void pdf_init_buffer(pdf_state *state, pdf_buffer *buf)
+{
+ int initial_size = 32;
+ buf->state = state;
+ buf->id = -42;
+ buf->alloc_size = 0;
+ buf->current_size = 0;
+ buf->data = (char*)malloc(initial_size);
+ buf->is_obj = 0;
+ buf->previous_buffer = NULL;
+ buf->next_buffer = NULL;
+ if (buf->data == NULL) {
+ rrd_set_error("malloc for pdf_buffer data");
+ state->has_failed = 1;
+ return;
+ }
+ buf->alloc_size = initial_size;
+ if (state->last_buffer)
+ state->last_buffer->next_buffer = buf;
+ if (state->first_buffer == NULL)
+ state->first_buffer = buf;
+ buf->previous_buffer = state->last_buffer;
+ state->last_buffer = buf;
+}
+
+static void pdf_put(pdf_buffer *buf, const char *text, int len)
+{
+ if (len <= 0)
+ return;
+ if (buf->alloc_size < buf->current_size + len) {
+ int new_size = buf->alloc_size;
+ char *new_buf;
+ while (new_size < buf->current_size + len)
+ new_size *= 4;
+ new_buf = (char*)malloc(new_size);
+ if (new_buf == NULL) {
+ rrd_set_error("re-malloc for pdf_buffer data");
+ buf->state->has_failed = 1;
+ return;
+ }
+ memcpy(new_buf, buf->data, buf->current_size);
+ free(buf->data);
+ buf->data = new_buf;
+ buf->alloc_size = new_size;
+ }
+ memcpy(buf->data + buf->current_size, text, len);
+ buf->current_size += len;
+}
+
+static void pdf_put_char(pdf_buffer *buf, char c)
+{
+ if (buf->alloc_size >= buf->current_size + 1) {
+ buf->data[buf->current_size++] = c;
+ } else {
+ char tmp[1];
+ tmp[0] = (char)c;
+ pdf_put(buf, tmp, 1);
+ }
+}
+
+static void pdf_puts(pdf_buffer *buf, const char *text)
+{
+ pdf_put(buf, text, strlen(text));
+}
+
+static void pdf_indent(pdf_buffer *buf)
+{
+ pdf_puts(buf, "\t");
+}
+
+static void pdf_putsi(pdf_buffer *buf, const char *text)
+{
+ pdf_indent(buf);
+ pdf_puts(buf, text);
+}
+
+static void pdf_putint(pdf_buffer *buf, int i)
+{
+ char tmp[20];
+ sprintf(tmp, "%d", i);
+ pdf_puts(buf, tmp);
+}
+
+static void pdf_putnumber(pdf_buffer *buf, double d)
+{
+ char tmp[50];
+ svg_format_number(tmp, sizeof(tmp), d);
+ pdf_puts(buf, tmp);
+}
+
+static void pdf_put_string_contents_wide(pdf_buffer *buf, const afm_char *text)
+{
+ const afm_char *p = text;
+ while (1) {
+ afm_char ch = *p;
+ ch = afm_fix_osx_charset(ch); /* unsafe macro */
+ switch (ch) {
+ case 0:
+ return;
+ case '(':
+ pdf_puts(buf, "\\(");
+ break;
+ case ')':
+ pdf_puts(buf, "\\)");
+ break;
+ case '\\':
+ pdf_puts(buf, "\\\\");
+ break;
+ case '\n':
+ pdf_puts(buf, "\\n");
+ break;
+ case '\r':
+ pdf_puts(buf, "\\r");
+ break;
+ case '\t':
+ pdf_puts(buf, "\\t");
+ break;
+ default:
+ if (ch > 255) {
+ pdf_put_char(buf, '?');
+ } else if (ch > 125 || ch < 32) {
+ pdf_put_char(buf, ch);
+ } else {
+ char tmp[10];
+ snprintf(tmp, sizeof(tmp), "\\%03o", (int)ch);
+ pdf_puts(buf, tmp);
+ }
+ }
+ p++;
+ }
+}
+
+static void pdf_put_string_contents(pdf_buffer *buf, const char *text)
+{
+#ifdef HAVE_MBSTOWCS
+ size_t clen = strlen(text) + 1;
+ wchar_t *cstr = malloc(sizeof(wchar_t) * clen);
+ int text_count = mbstowcs(cstr, text, clen);
+ if (text_count == -1)
+ text_count = mbstowcs(cstr, "Enc-Err", 6);
+ pdf_put_string_contents_wide(buf, cstr);
+#if 0
+ if (*text == 'W') {
+ fprintf(stderr, "Decoding utf8 for '%s'\n", text);
+ wchar_t *p = cstr;
+ char *pp = text;
+ fprintf(stderr, "sz wc = %d\n", sizeof(wchar_t));
+ while (*p) {
+ fprintf(stderr, " %d = %c versus %d = %c\n", *p, (char)*p, 255 & (int)*pp, *pp);
+ p++;
+ pp++;
+ }
+ }
+#endif
+ free(cstr);
+#else
+ pdf_put_string_contents_wide(buf, text);
+#endif
+}
+
+static void pdf_init_object(pdf_state *state, pdf_buffer *buf)
+{
+ pdf_init_buffer(state, buf);
+ buf->id = ++state->last_obj_id;
+ buf->is_obj = 1;
+ buf->is_stream = 0;
+}
+
+static void pdf_init_dict(pdf_state *state, pdf_buffer *buf)
+{
+ pdf_init_object(state, buf);
+ buf->is_dict = 1;
+}
+
+static void pdf_set_color(pdf_buffer *buf, gfx_color_t color,
+ gfx_color_t *current_color, const char *op)
+{
+#if USE_PDF_FAKE_ALPHA
+ double a1, a2;
+#endif
+ /* gfx_color_t is RRGGBBAA */
+ if (*current_color == color)
+ return;
+#if USE_PDF_FAKE_ALPHA
+ a1 = (color & 255) / 255.0;
+ a2 = 1 - a1;
+#define pdf_color_calc(x) ( ((x) & 255) / 255.0 * a1 + a2)
+#else
+#define pdf_color_calc(x) ( ((x) & 255) / 255.0)
+#endif
+ pdf_putnumber(buf, pdf_color_calc(color >> 24));
+ pdf_puts(buf, " ");
+ pdf_putnumber(buf, pdf_color_calc(color >> 16));
+ pdf_puts(buf, " ");
+ pdf_putnumber(buf, pdf_color_calc(color >> 8));
+ pdf_puts(buf, " ");
+ pdf_puts(buf, op);
+ pdf_puts(buf, "\n");
+ *current_color = color;
+}
+
+static void pdf_set_stroke_color(pdf_buffer *buf, gfx_color_t color)
+{
+ pdf_set_color(buf, color, &buf->state->stroke_color, "RG");
+}
+
+static void pdf_set_fill_color(pdf_buffer *buf, gfx_color_t color)
+{
+ pdf_set_color(buf, color, &buf->state->fill_color, "rg");
+}
+
+static pdf_font *pdf_find_font(pdf_state *state, gfx_node_t *node)
+{
+ const char *ps_font = afm_get_font_postscript_name(node->filename);
+ pdf_font *ef;
+ for (ef = state->font_list; ef; ef = ef->next) {
+ if (!strcmp(ps_font, ef->ps_font))
+ return ef;
+ }
+ return NULL;
+}
+
+static void pdf_add_font(pdf_state *state, gfx_node_t *node)
+{
+ pdf_font *ef = pdf_find_font(state, node);
+ if (ef)
+ return;
+ ef = malloc(sizeof(pdf_font));
+ if (ef == NULL) {
+ rrd_set_error("malloc for pdf_font");
+ state->has_failed = 1;
+ return;
+ }
+ pdf_init_dict(state, &ef->obj);
+ ef->next = state->font_list;
+ ef->ps_font = afm_get_font_postscript_name(node->filename);
+ state->font_list = ef;
+ /* fonts dict */
+ pdf_putsi(&state->fontsdict_obj, "/F");
+ pdf_putint(&state->fontsdict_obj, ef->obj.id);
+ pdf_puts(&state->fontsdict_obj, " ");
+ pdf_putint(&state->fontsdict_obj, ef->obj.id);
+ pdf_puts(&state->fontsdict_obj, " 0 R\n");
+ /* fonts def */
+ pdf_putsi(&ef->obj, "/Type /Font\n");
+ pdf_putsi(&ef->obj, "/Subtype /Type1\n");
+ pdf_putsi(&ef->obj, "/Name /F");
+ pdf_putint(&ef->obj, ef->obj.id);
+ pdf_puts(&ef->obj, "\n");
+ pdf_putsi(&ef->obj, "/BaseFont /");
+ pdf_puts(&ef->obj, ef->ps_font);
+ pdf_puts(&ef->obj, "\n");
+ pdf_putsi(&ef->obj, "/Encoding /WinAnsiEncoding\n");
+ /* 'Cp1252' (this is latin 1 extended with 27 characters;
+ the encoding is also known as 'winansi')
+ http://www.lowagie.com/iText/tutorial/ch09.html */
+}
+
+static void pdf_create_fonts(pdf_state *state)
+{
+ gfx_node_t *node;
+ for (node = state->canvas->firstnode; node; node = node->next) {
+ if (node->type == GFX_TEXT)
+ pdf_add_font(state, node);
+ }
+}
+
+static void pdf_write_linearea(pdf_state *state, gfx_node_t *node)
+{
+ int i;
+ pdf_buffer *s = &state->graph_stream;
+ if (node->type == GFX_LINE) {
+ svg_dash dash_info;
+ svg_get_dash(node, &dash_info);
+ if (!svg_dash_equal(&dash_info, &state->dash)) {
+ state->dash = dash_info;
+ if (dash_info.dash_enable) {
+ pdf_puts(s, "[");
+ pdf_putnumber(s, dash_info.adjusted_on);
+ pdf_puts(s, " ");
+ pdf_putnumber(s, dash_info.adjusted_off);
+ pdf_puts(s, "] ");
+ pdf_putnumber(s, dash_info.dash_offset);
+ pdf_puts(s, " d\n");
+ } else {
+ pdf_puts(s, "[] 0 d\n");
+ }
+ }
+ pdf_set_stroke_color(s, node->color);
+ if (state->linecap != 1) {
+ pdf_puts(s, "1 j\n");
+ state->linecap = 1;
+ }
+ if (state->linejoin != 1) {
+ pdf_puts(s, "1 J\n");
+ state->linejoin = 1;
+ }
+ if (node->size != state->line_width) {
+ state->line_width = node->size;
+ pdf_putnumber(s, state->line_width);
+ pdf_puts(s, " w\n");
+ }
+ } else {
+ pdf_set_fill_color(s, node->color);
+ }
+ for (i = 0; i < node->points; i++) {
+ ArtVpath *vec = node->path + i;
+ double x = vec->x;
+ double y = state->page_height - vec->y;
+ if (node->type == GFX_AREA) {
+ x += LINEOFFSET; /* adjust for libart handling of areas */
+ y -= LINEOFFSET;
+ }
+ switch (vec->code) {
+ case ART_MOVETO_OPEN: /* fall-through */
+ case ART_MOVETO:
+ pdf_putnumber(s, x);
+ pdf_puts(s, " ");
+ pdf_putnumber(s, y);
+ pdf_puts(s, " m\n");
+ break;
+ case ART_LINETO:
+ pdf_putnumber(s, x);
+ pdf_puts(s, " ");
+ pdf_putnumber(s, y);
+ pdf_puts(s, " l\n");
+ break;
+ case ART_CURVETO: break; /* unsupported */
+ case ART_END: break; /* nop */
+ }
+ }
+ if (node->type == GFX_LINE) {
+ pdf_puts(s, node->closed_path ? "s\n" : "S\n");
+ } else {
+ pdf_puts(s, "f\n");
+ }
+}
+
+
+static void pdf_write_matrix(pdf_state *state, gfx_node_t *node, pdf_coords *g, int useTM)
+{
+ char tmp[150];
+ pdf_buffer *s = &state->graph_stream;
+ if (node->angle == 0) {
+ pdf_puts(s, "1 0 0 1 ");
+ pdf_putnumber(s, useTM ? g->tmx : g->mx);
+ pdf_puts(s, " ");
+ pdf_putnumber(s, useTM ? g->tmy : g->my);
+ } else {
+ /* can't use svg_write_number as 2 decimals is far from enough to avoid
+ skewed text */
+ sprintf(tmp, "%f %f %f %f %f %f",
+ g->ma, g->mb, g->mc, g->md,
+ useTM ? g->tmx : g->mx,
+ useTM ? g->tmy : g->my);
+ pdf_puts(s, tmp);
+ }
+}
+
+static void pdf_write_text(pdf_state *state, gfx_node_t *node,
+ int last_was_text, int next_is_text)
+{
+ pdf_coords g;
+ pdf_buffer *s = &state->graph_stream;
+ pdf_font *font = pdf_find_font(state, node);
+ if (font == NULL) {
+ rrd_set_error("font disappeared");
+ state->has_failed = 1;
+ return;
+ }
+ pdf_calc(state->page_height, node, &g);
+#if PDF_CALC_DEBUG
+ pdf_puts(s, "q % debug green box\n");
+ pdf_write_matrix(state, node, &g, 0);
+ pdf_puts(s, " cm\n");
+ pdf_set_fill_color(s, 0x90FF9000);
+ pdf_puts(s, "0 0.4 0 rg\n");
+ pdf_puts(s, "0 0 ");
+ pdf_putnumber(s, g.sizep.x);
+ pdf_puts(s, " ");
+ pdf_putnumber(s, g.sizep.y);
+ pdf_puts(s, " re\n");
+ pdf_puts(s, "f\n");
+ pdf_puts(s, "Q\n");
+#endif
+ pdf_set_fill_color(s, node->color);
+ if (PDF_CALC_DEBUG || !last_was_text)
+ pdf_puts(s, "BT\n");
+ if (state->font_id != font->obj.id || node->size != state->font_size) {
+ state->font_id = font->obj.id;
+ state->font_size = node->size;
+ pdf_puts(s, "/F");
+ pdf_putint(s, font->obj.id);
+ pdf_puts(s, " ");
+ pdf_putnumber(s, node->size);
+ pdf_puts(s, " Tf\n");
+ }
+ pdf_write_matrix(state, node, &g, 1);
+ pdf_puts(s, " Tm\n");
+ pdf_puts(s, "(");
+ pdf_put_string_contents(s, node->text);
+ pdf_puts(s, ") Tj\n");
+ if (PDF_CALC_DEBUG || !next_is_text)
+ pdf_puts(s, "ET\n");
+}
+
+static void pdf_write_content(pdf_state *state)
+{
+ gfx_node_t *node;
+ int last_was_text = 0, next_is_text;
+ for (node = state->canvas->firstnode; node; node = node->next) {
+ switch (node->type) {
+ case GFX_LINE:
+ case GFX_AREA:
+ pdf_write_linearea(state, node);
+ break;
+ case GFX_TEXT:
+ next_is_text = node->next && node->next->type == GFX_TEXT;
+ pdf_write_text(state, node, last_was_text, next_is_text);
+ break;
+ }
+ last_was_text = node->type == GFX_TEXT;
+ }
+}
+
+static void pdf_init_document(pdf_state *state)
+{
+ pdf_init_buffer(state, &state->pdf_header);
+ pdf_init_dict(state, &state->catalog_obj);
+ pdf_init_dict(state, &state->info_obj);
+ pdf_init_dict(state, &state->pages_obj);
+ pdf_init_dict(state, &state->page1_obj);
+ pdf_init_dict(state, &state->fontsdict_obj);
+ pdf_create_fonts(state);
+ if (state->has_failed)
+ return;
+ /* make stream last object in file */
+ pdf_init_object(state, &state->graph_stream);
+ state->graph_stream.is_stream = 1;
+}
+
+static void pdf_setup_document(pdf_state *state)
+{
+ const char *creator = "RRDtool " PACKAGE_VERSION " Tobias Oetiker, http://tobi.oetiker.ch";
+ /* all objects created by now, so init code can reference them */
+ /* HEADER */
+ pdf_puts(&state->pdf_header, "%PDF-1.3\n");
+ /* following 8 bit comment is recommended by Adobe for
+ indicating binary file to file transfer applications */
+ pdf_puts(&state->pdf_header, "%\xE2\xE3\xCF\xD3\n");
+ /* INFO */
+ pdf_putsi(&state->info_obj, "/Creator (");
+ pdf_put_string_contents(&state->info_obj, creator);
+ pdf_puts(&state->info_obj, ")\n");
+ /* CATALOG */
+ pdf_putsi(&state->catalog_obj, "/Type /Catalog\n");
+ pdf_putsi(&state->catalog_obj, "/Pages ");
+ pdf_putint(&state->catalog_obj, state->pages_obj.id);
+ pdf_puts(&state->catalog_obj, " 0 R\n");
+ /* PAGES */
+ pdf_putsi(&state->pages_obj, "/Type /Pages\n");
+ pdf_putsi(&state->pages_obj, "/Kids [");
+ pdf_putint(&state->pages_obj, state->page1_obj.id);
+ pdf_puts(&state->pages_obj, " 0 R]\n");
+ pdf_putsi(&state->pages_obj, "/Count 1\n");
+ /* PAGE 1 */
+ pdf_putsi(&state->page1_obj, "/Type /Page\n");
+ pdf_putsi(&state->page1_obj, "/Parent ");
+ pdf_putint(&state->page1_obj, state->pages_obj.id);
+ pdf_puts(&state->page1_obj, " 0 R\n");
+ pdf_putsi(&state->page1_obj, "/MediaBox [0 0 ");
+ pdf_putint(&state->page1_obj, state->page_width);
+ pdf_puts(&state->page1_obj, " ");
+ pdf_putint(&state->page1_obj, state->page_height);
+ pdf_puts(&state->page1_obj, "]\n");
+ pdf_putsi(&state->page1_obj, "/Contents ");
+ pdf_putint(&state->page1_obj, state->graph_stream.id);
+ pdf_puts(&state->page1_obj, " 0 R\n");
+ pdf_putsi(&state->page1_obj, "/Resources << /Font ");
+ pdf_putint(&state->page1_obj, state->fontsdict_obj.id);
+ pdf_puts(&state->page1_obj, " 0 R >>\n");
+}
+
+static void pdf_write_string_to_file(pdf_state *state, const char *text)
+{
+ fputs(text, state->fp);
+ state->pdf_file_pos += strlen(text);
+}
+
+static void pdf_write_buf_to_file(pdf_state *state, pdf_buffer *buf)
+{
+ char tmp[40];
+ buf->pdf_file_pos = state->pdf_file_pos;
+ if (buf->is_obj) {
+ snprintf(tmp, sizeof(tmp), "%d 0 obj\n", buf->id);
+ pdf_write_string_to_file(state, tmp);
+ }
+ if (buf->is_dict)
+ pdf_write_string_to_file(state, "<<\n");
+ if (buf->is_stream) {
+ snprintf(tmp, sizeof(tmp), "<< /Length %d >>\n", buf->current_size);
+ pdf_write_string_to_file(state, tmp);
+ pdf_write_string_to_file(state, "stream\n");
+ }
+ fwrite(buf->data, 1, buf->current_size, state->fp);
+ state->pdf_file_pos += buf->current_size;
+ if (buf->is_stream)
+ pdf_write_string_to_file(state, "endstream\n");
+ if (buf->is_dict)
+ pdf_write_string_to_file(state, ">>\n");
+ if (buf->is_obj)
+ pdf_write_string_to_file(state, "endobj\n");
+}
+
+static void pdf_write_to_file(pdf_state *state)
+{
+ pdf_buffer *buf = state->first_buffer;
+ int xref_pos;
+ state->pdf_file_pos = 0;
+ pdf_write_buf_to_file(state, &state->pdf_header);
+ while (buf) {
+ if (buf->is_obj)
+ pdf_write_buf_to_file(state, buf);
+ buf = buf->next_buffer;
+ }
+ xref_pos = state->pdf_file_pos;
+ fprintf(state->fp, "xref\n");
+ fprintf(state->fp, "%d %d\n", 0, state->last_obj_id + 1);
+ /* TOC lines must be exactly 20 bytes including \n */
+ fprintf(state->fp, "%010d %05d f\x20\n", 0, 65535);
+ for (buf = state->first_buffer; buf; buf = buf->next_buffer) {
+ if (buf->is_obj)
+ fprintf(state->fp, "%010d %05d n\x20\n", buf->pdf_file_pos, 0);
+ }
+ fprintf(state->fp, "trailer\n");
+ fprintf(state->fp, "<<\n");
+ fprintf(state->fp, "\t/Size %d\n", state->last_obj_id + 1);
+ fprintf(state->fp, "\t/Root %d 0 R\n", state->catalog_obj.id);
+ fprintf(state->fp, "\t/Info %d 0 R\n", state->info_obj.id);
+ fprintf(state->fp, ">>\n");
+ fprintf(state->fp, "startxref\n");
+ fprintf(state->fp, "%d\n", xref_pos);
+ fputs("%%EOF\n", state->fp);
+}
+
+static void pdf_free_resources(pdf_state *state)
+{
+ pdf_buffer *buf = state->first_buffer;
+ while (buf) {
+ free(buf->data);
+ buf->data = NULL;
+ buf->alloc_size = buf->current_size = 0;
+ buf = buf->next_buffer;
+ }
+ while (state->font_list) {
+ pdf_font *next = state->font_list->next;
+ free(state->font_list);
+ state->font_list = next;
+ }
+}
+
+int gfx_render_pdf (gfx_canvas_t *canvas,
+ art_u32 width, art_u32 height,
+ gfx_color_t UNUSED(background), FILE *fp){
+ struct pdf_state state;
+ memset(&state, 0, sizeof(pdf_state));
+ state.fp = fp;
+ state.canvas = canvas;
+ state.page_width = width;
+ state.page_height = height;
+ state.font_id = -1;
+ state.font_size = -1;
+ state.font_list = NULL;
+ state.linecap = -1;
+ state.linejoin = -1;
+ pdf_init_document(&state);
+ /*
+ pdf_set_color(&state, background);
+ fprintf(fp, "0 0 M 0 %d L %d %d L %d 0 L fill\n",
+ height, width, height, width);
+ */
+ if (!state.has_failed)
+ pdf_write_content(&state);
+ if (!state.has_failed)
+ pdf_setup_document(&state);
+ if (!state.has_failed)
+ pdf_write_to_file(&state);
+ pdf_free_resources(&state);
+ return state.has_failed ? -1 : 0;
+}
+
diff --git a/program/src/rrd_gfx.h b/program/src/rrd_gfx.h
--- /dev/null
+++ b/program/src/rrd_gfx.h
@@ -0,0 +1,135 @@
+/****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd_gfx.h generic graphics adapter library
+ ****************************************************************************/
+
+#ifndef RRD_GFX_H
+#define RRD_GFX_H
+#define LIBART_COMPILATION
+
+#define y0 libart_y0
+#define y1 libart_y1
+#define gamma libart_gamma
+#include <libart_lgpl/libart.h>
+#include <libart_lgpl/art_rgba.h>
+#include "art_rgba_svp.h"
+#undef gamma
+#undef y0
+#undef y1
+
+
+enum gfx_if_en {IF_PNG=0,IF_SVG,IF_EPS,IF_PDF};
+enum gfx_en { GFX_LINE=0,GFX_AREA,GFX_TEXT };
+enum gfx_h_align_en { GFX_H_NULL=0, GFX_H_LEFT, GFX_H_RIGHT, GFX_H_CENTER };
+enum gfx_v_align_en { GFX_V_NULL=0, GFX_V_TOP, GFX_V_BOTTOM, GFX_V_CENTER };
+enum gfx_aa_type_en {AA_NORMAL=0,AA_LIGHT,AA_NONE};
+typedef unsigned long gfx_color_t;
+
+typedef struct gfx_node_t {
+ enum gfx_en type; /* type of graph element */
+ gfx_color_t color; /* color of element 0xRRGGBBAA alpha 0xff is solid*/
+ double size; /* font size, line width */
+ double dash_on, dash_off; /* dash line fragments lengths */
+ int closed_path;
+ int points;
+ int points_max;
+ char *filename; /* font or image filename */
+ char *text;
+ ArtVpath *path; /* path */
+ double x,y; /* position */
+ double angle; /* text angle */
+ enum gfx_h_align_en halign; /* text alignement */
+ enum gfx_v_align_en valign; /* text alignement */
+ double tabwidth;
+ struct gfx_node_t *next;
+} gfx_node_t;
+
+
+typedef struct gfx_canvas_t
+{
+ struct gfx_node_t *firstnode;
+ struct gfx_node_t *lastnode;
+ enum gfx_if_en imgformat; /* image format */
+ int interlaced; /* will the graph be interlaced? */
+ double zoom; /* zoom for graph */
+ double font_aa_threshold; /* no anti-aliasing for sizes <= */
+ enum gfx_aa_type_en aa_type; /* anti-aliasing type (normal/light/none) */
+} gfx_canvas_t;
+
+gfx_canvas_t *gfx_new_canvas (void);
+
+/* create a new line */
+gfx_node_t *gfx_new_line (gfx_canvas_t *canvas,
+ double X0, double Y0,
+ double X1, double Y1,
+ double width, gfx_color_t color);
+
+gfx_node_t *gfx_new_dashed_line (gfx_canvas_t *canvas,
+ double X0, double Y0,
+ double X1, double Y1,
+ double width, gfx_color_t color,
+ double dash_on, double dash_off);
+
+/* create a new area */
+gfx_node_t *gfx_new_area (gfx_canvas_t *canvas,
+ double X0, double Y0,
+ double X1, double Y1,
+ double X2, double Y2,
+ gfx_color_t color);
+
+/* add a point to a line or to an area */
+int gfx_add_point (gfx_node_t *node, double x, double y);
+
+/* close current path so it ends at the same point as it started */
+void gfx_close_path (gfx_node_t *node);
+
+
+/* create a text node */
+gfx_node_t *gfx_new_text (gfx_canvas_t *canvas,
+ double x, double y, gfx_color_t color,
+ char* font, double size,
+ double tabwidth, double angle,
+ enum gfx_h_align_en h_align,
+ enum gfx_v_align_en v_align,
+ char* text);
+
+/* measure width of a text string */
+double gfx_get_text_width ( gfx_canvas_t *canvas,
+ double start, char* font, double size,
+ double tabwidth, char* text, int rotation);
+
+/* save image to file */
+int gfx_render (gfx_canvas_t *canvas,
+ art_u32 width, art_u32 height,
+ gfx_color_t background, FILE *fo);
+
+/* free memory used by nodes this will also remove memory required for
+ node chain and associated material */
+int gfx_destroy (gfx_canvas_t *canvas);
+
+
+/* PNG support*/
+int gfx_render_png (gfx_canvas_t *canvas,
+ art_u32 width, art_u32 height,
+ gfx_color_t background, FILE *fo);
+double gfx_get_text_width_libart ( gfx_canvas_t *canvas, double start,
+ char* font, double size, double tabwidth,
+ char* text, int rotation );
+
+/* SVG support */
+int gfx_render_svg (gfx_canvas_t *canvas,
+ art_u32 width, art_u32 height,
+ gfx_color_t background, FILE *fo);
+
+/* EPS support */
+int gfx_render_eps (gfx_canvas_t *canvas,
+ art_u32 width, art_u32 height,
+ gfx_color_t background, FILE *fo);
+
+/* PDF support */
+int gfx_render_pdf (gfx_canvas_t *canvas,
+ art_u32 width, art_u32 height,
+ gfx_color_t background, FILE *fo);
+
+#endif
diff --git a/program/src/rrd_graph.c b/program/src/rrd_graph.c
--- /dev/null
+++ b/program/src/rrd_graph.c
@@ -0,0 +1,3974 @@
+/****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd__graph.c produce graphs from data in rrdfiles
+ ****************************************************************************/
+
+
+#include <sys/stat.h>
+
+#ifdef WIN32
+#include "strftime.h"
+#endif
+#include "rrd_tool.h"
+
+#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+#include <io.h>
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_TIME_H
+#include <time.h>
+#endif
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#include "rrd_graph.h"
+
+/* some constant definitions */
+
+
+
+#ifndef RRD_DEFAULT_FONT
+/* there is special code later to pick Cour.ttf when running on windows */
+#define RRD_DEFAULT_FONT "DejaVuSansMono-Roman.ttf"
+#endif
+
+text_prop_t text_prop[] = {
+ { 8.0, RRD_DEFAULT_FONT }, /* default */
+ { 9.0, RRD_DEFAULT_FONT }, /* title */
+ { 7.0, RRD_DEFAULT_FONT }, /* axis */
+ { 8.0, RRD_DEFAULT_FONT }, /* unit */
+ { 8.0, RRD_DEFAULT_FONT } /* legend */
+};
+
+xlab_t xlab[] = {
+ {0, 0, TMT_SECOND,30, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"},
+ {2, 0, TMT_MINUTE,1, TMT_MINUTE,5, TMT_MINUTE,5, 0,"%H:%M"},
+ {5, 0, TMT_MINUTE,2, TMT_MINUTE,10, TMT_MINUTE,10, 0,"%H:%M"},
+ {10, 0, TMT_MINUTE,5, TMT_MINUTE,20, TMT_MINUTE,20, 0,"%H:%M"},
+ {30, 0, TMT_MINUTE,10, TMT_HOUR,1, TMT_HOUR,1, 0,"%H:%M"},
+ {60, 0, TMT_MINUTE,30, TMT_HOUR,2, TMT_HOUR,2, 0,"%H:%M"},
+ {60, 24*3600, TMT_MINUTE,30, TMT_HOUR,2, TMT_HOUR,4, 0,"%a %H:%M"},
+ {180, 0, TMT_HOUR,1, TMT_HOUR,6, TMT_HOUR,6, 0,"%H:%M"},
+ {180, 24*3600, TMT_HOUR,1, TMT_HOUR,6, TMT_HOUR,12, 0,"%a %H:%M"},
+ /*{300, 0, TMT_HOUR,3, TMT_HOUR,12, TMT_HOUR,12, 12*3600,"%a %p"}, this looks silly*/
+ {600, 0, TMT_HOUR,6, TMT_DAY,1, TMT_DAY,1, 24*3600,"%a"},
+ {1200, 0, TMT_HOUR,6, TMT_DAY,1, TMT_DAY,1, 24*3600,"%d"},
+ {1800, 0, TMT_HOUR,12, TMT_DAY,1, TMT_DAY,2, 24*3600,"%a %d"},
+ {2400, 0, TMT_HOUR,12, TMT_DAY,1, TMT_DAY,2, 24*3600,"%a"},
+ {3600, 0, TMT_DAY,1, TMT_WEEK,1, TMT_WEEK,1, 7*24*3600,"Week %V"},
+ {3*3600, 0, TMT_WEEK,1, TMT_MONTH,1, TMT_WEEK,2, 7*24*3600,"Week %V"},
+ {6*3600, 0, TMT_MONTH,1, TMT_MONTH,1, TMT_MONTH,1, 30*24*3600,"%b"},
+ {48*3600, 0, TMT_MONTH,1, TMT_MONTH,3, TMT_MONTH,3, 30*24*3600,"%b"},
+ {315360, 0, TMT_MONTH,3, TMT_YEAR,1, TMT_YEAR,1, 365*24*3600,"%Y"},
+ {10*24*3600, 0, TMT_YEAR,1, TMT_YEAR,1, TMT_YEAR,1, 365*24*3600,"%y"},
+ {-1,0,TMT_MONTH,0,TMT_MONTH,0,TMT_MONTH,0,0,""}
+};
+
+/* sensible y label intervals ...*/
+
+ylab_t ylab[]= {
+ {0.1, {1,2, 5,10}},
+ {0.2, {1,5,10,20}},
+ {0.5, {1,2, 4,10}},
+ {1.0, {1,2, 5,10}},
+ {2.0, {1,5,10,20}},
+ {5.0, {1,2, 4,10}},
+ {10.0, {1,2, 5,10}},
+ {20.0, {1,5,10,20}},
+ {50.0, {1,2, 4,10}},
+ {100.0, {1,2, 5,10}},
+ {200.0, {1,5,10,20}},
+ {500.0, {1,2, 4,10}},
+ {0.0, {0,0,0,0}}};
+
+
+gfx_color_t graph_col[] = /* default colors */
+{ 0xFFFFFFFF, /* canvas */
+ 0xF0F0F0FF, /* background */
+ 0xD0D0D0FF, /* shade A */
+ 0xA0A0A0FF, /* shade B */
+ 0x90909080, /* grid */
+ 0xE0505080, /* major grid */
+ 0x000000FF, /* font */
+ 0x802020FF, /* arrow */
+ 0x202020FF, /* axis */
+ 0x000000FF /* frame */
+};
+
+
+/* #define DEBUG */
+
+#ifdef DEBUG
+# define DPRINT(x) (void)(printf x, printf("\n"))
+#else
+# define DPRINT(x)
+#endif
+
+
+/* initialize with xtr(im,0); */
+int
+xtr(image_desc_t *im,time_t mytime){
+ static double pixie;
+ if (mytime==0){
+ pixie = (double) im->xsize / (double)(im->end - im->start);
+ return im->xorigin;
+ }
+ return (int)((double)im->xorigin
+ + pixie * ( mytime - im->start ) );
+}
+
+/* translate data values into y coordinates */
+double
+ytr(image_desc_t *im, double value){
+ static double pixie;
+ double yval;
+ if (isnan(value)){
+ if(!im->logarithmic)
+ pixie = (double) im->ysize / (im->maxval - im->minval);
+ else
+ pixie = (double) im->ysize / (log10(im->maxval) - log10(im->minval));
+ yval = im->yorigin;
+ } else if(!im->logarithmic) {
+ yval = im->yorigin - pixie * (value - im->minval);
+ } else {
+ if (value < im->minval) {
+ yval = im->yorigin;
+ } else {
+ yval = im->yorigin - pixie * (log10(value) - log10(im->minval));
+ }
+ }
+ /* make sure we don't return anything too unreasonable. GD lib can
+ get terribly slow when drawing lines outside its scope. This is
+ especially problematic in connection with the rigid option */
+ if (! im->rigid) {
+ /* keep yval as-is */
+ } else if (yval > im->yorigin) {
+ yval = im->yorigin +0.00001;
+ } else if (yval < im->yorigin - im->ysize){
+ yval = im->yorigin - im->ysize - 0.00001;
+ }
+ return yval;
+}
+
+
+
+/* conversion function for symbolic entry names */
+
+
+#define conv_if(VV,VVV) \
+ if (strcmp(#VV, string) == 0) return VVV ;
+
+enum gf_en gf_conv(char *string){
+
+ conv_if(PRINT,GF_PRINT)
+ conv_if(GPRINT,GF_GPRINT)
+ conv_if(COMMENT,GF_COMMENT)
+ conv_if(HRULE,GF_HRULE)
+ conv_if(VRULE,GF_VRULE)
+ conv_if(LINE,GF_LINE)
+ conv_if(AREA,GF_AREA)
+ conv_if(STACK,GF_STACK)
+ conv_if(TICK,GF_TICK)
+ conv_if(DEF,GF_DEF)
+ conv_if(CDEF,GF_CDEF)
+ conv_if(VDEF,GF_VDEF)
+#ifdef WITH_PIECHART
+ conv_if(PART,GF_PART)
+#endif
+ conv_if(XPORT,GF_XPORT)
+ conv_if(SHIFT,GF_SHIFT)
+
+ return (-1);
+}
+
+enum gfx_if_en if_conv(char *string){
+
+ conv_if(PNG,IF_PNG)
+ conv_if(SVG,IF_SVG)
+ conv_if(EPS,IF_EPS)
+ conv_if(PDF,IF_PDF)
+
+ return (-1);
+}
+
+enum tmt_en tmt_conv(char *string){
+
+ conv_if(SECOND,TMT_SECOND)
+ conv_if(MINUTE,TMT_MINUTE)
+ conv_if(HOUR,TMT_HOUR)
+ conv_if(DAY,TMT_DAY)
+ conv_if(WEEK,TMT_WEEK)
+ conv_if(MONTH,TMT_MONTH)
+ conv_if(YEAR,TMT_YEAR)
+ return (-1);
+}
+
+enum grc_en grc_conv(char *string){
+
+ conv_if(BACK,GRC_BACK)
+ conv_if(CANVAS,GRC_CANVAS)
+ conv_if(SHADEA,GRC_SHADEA)
+ conv_if(SHADEB,GRC_SHADEB)
+ conv_if(GRID,GRC_GRID)
+ conv_if(MGRID,GRC_MGRID)
+ conv_if(FONT,GRC_FONT)
+ conv_if(ARROW,GRC_ARROW)
+ conv_if(AXIS,GRC_AXIS)
+ conv_if(FRAME,GRC_FRAME)
+
+ return -1;
+}
+
+enum text_prop_en text_prop_conv(char *string){
+
+ conv_if(DEFAULT,TEXT_PROP_DEFAULT)
+ conv_if(TITLE,TEXT_PROP_TITLE)
+ conv_if(AXIS,TEXT_PROP_AXIS)
+ conv_if(UNIT,TEXT_PROP_UNIT)
+ conv_if(LEGEND,TEXT_PROP_LEGEND)
+ return -1;
+}
+
+
+#undef conv_if
+
+int
+im_free(image_desc_t *im)
+{
+ unsigned long i,ii;
+
+ if (im == NULL) return 0;
+ for(i=0;i<(unsigned)im->gdes_c;i++){
+ if (im->gdes[i].data_first){
+ /* careful here, because a single pointer can occur several times */
+ free (im->gdes[i].data);
+ if (im->gdes[i].ds_namv){
+ for (ii=0;ii<im->gdes[i].ds_cnt;ii++)
+ free(im->gdes[i].ds_namv[ii]);
+ free(im->gdes[i].ds_namv);
+ }
+ }
+ free (im->gdes[i].p_data);
+ free (im->gdes[i].rpnp);
+ }
+ free(im->gdes);
+ gfx_destroy(im->canvas);
+ return 0;
+}
+
+/* find SI magnitude symbol for the given number*/
+void
+auto_scale(
+ image_desc_t *im, /* image description */
+ double *value,
+ char **symb_ptr,
+ double *magfact
+ )
+{
+
+ char *symbol[] = {"a", /* 10e-18 Atto */
+ "f", /* 10e-15 Femto */
+ "p", /* 10e-12 Pico */
+ "n", /* 10e-9 Nano */
+ "u", /* 10e-6 Micro */
+ "m", /* 10e-3 Milli */
+ " ", /* Base */
+ "k", /* 10e3 Kilo */
+ "M", /* 10e6 Mega */
+ "G", /* 10e9 Giga */
+ "T", /* 10e12 Tera */
+ "P", /* 10e15 Peta */
+ "E"};/* 10e18 Exa */
+
+ int symbcenter = 6;
+ int sindex;
+
+ if (*value == 0.0 || isnan(*value) ) {
+ sindex = 0;
+ *magfact = 1.0;
+ } else {
+ sindex = floor(log(fabs(*value))/log((double)im->base));
+ *magfact = pow((double)im->base, (double)sindex);
+ (*value) /= (*magfact);
+ }
+ if ( sindex <= symbcenter && sindex >= -symbcenter) {
+ (*symb_ptr) = symbol[sindex+symbcenter];
+ }
+ else {
+ (*symb_ptr) = "?";
+ }
+}
+
+
+static char si_symbol[] = {
+ 'a', /* 10e-18 Atto */
+ 'f', /* 10e-15 Femto */
+ 'p', /* 10e-12 Pico */
+ 'n', /* 10e-9 Nano */
+ 'u', /* 10e-6 Micro */
+ 'm', /* 10e-3 Milli */
+ ' ', /* Base */
+ 'k', /* 10e3 Kilo */
+ 'M', /* 10e6 Mega */
+ 'G', /* 10e9 Giga */
+ 'T', /* 10e12 Tera */
+ 'P', /* 10e15 Peta */
+ 'E', /* 10e18 Exa */
+};
+static const int si_symbcenter = 6;
+
+/* find SI magnitude symbol for the numbers on the y-axis*/
+void
+si_unit(
+ image_desc_t *im /* image description */
+)
+{
+
+ double digits,viewdigits=0;
+
+ digits = floor( log( max( fabs(im->minval),fabs(im->maxval)))/log((double)im->base));
+
+ if (im->unitsexponent != 9999) {
+ /* unitsexponent = 9, 6, 3, 0, -3, -6, -9, etc */
+ viewdigits = floor(im->unitsexponent / 3);
+ } else {
+ viewdigits = digits;
+ }
+
+ im->magfact = pow((double)im->base , digits);
+
+#ifdef DEBUG
+ printf("digits %6.3f im->magfact %6.3f\n",digits,im->magfact);
+#endif
+
+ im->viewfactor = im->magfact / pow((double)im->base , viewdigits);
+
+ if ( ((viewdigits+si_symbcenter) < sizeof(si_symbol)) &&
+ ((viewdigits+si_symbcenter) >= 0) )
+ im->symbol = si_symbol[(int)viewdigits+si_symbcenter];
+ else
+ im->symbol = '?';
+ }
+
+/* move min and max values around to become sensible */
+
+void
+expand_range(image_desc_t *im)
+{
+ double sensiblevalues[] ={1000.0,900.0,800.0,750.0,700.0,
+ 600.0,500.0,400.0,300.0,250.0,
+ 200.0,125.0,100.0,90.0,80.0,
+ 75.0,70.0,60.0,50.0,40.0,30.0,
+ 25.0,20.0,10.0,9.0,8.0,
+ 7.0,6.0,5.0,4.0,3.5,3.0,
+ 2.5,2.0,1.8,1.5,1.2,1.0,
+ 0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1,0.0,-1};
+
+ double scaled_min,scaled_max;
+ double adj;
+ int i;
+
+
+
+#ifdef DEBUG
+ printf("Min: %6.2f Max: %6.2f MagFactor: %6.2f\n",
+ im->minval,im->maxval,im->magfact);
+#endif
+
+ if (isnan(im->ygridstep)){
+ if(im->extra_flags & ALTAUTOSCALE) {
+ /* measure the amplitude of the function. Make sure that
+ graph boundaries are slightly higher then max/min vals
+ so we can see amplitude on the graph */
+ double delt, fact;
+
+ delt = im->maxval - im->minval;
+ adj = delt * 0.1;
+ fact = 2.0 * pow(10.0,
+ floor(log10(max(fabs(im->minval), fabs(im->maxval))/im->magfact)) - 2);
+ if (delt < fact) {
+ adj = (fact - delt) * 0.55;
+#ifdef DEBUG
+ printf("Min: %6.2f Max: %6.2f delt: %6.2f fact: %6.2f adj: %6.2f\n", im->minval, im->maxval, delt, fact, adj);
+#endif
+ }
+ im->minval -= adj;
+ im->maxval += adj;
+ }
+ else if(im->extra_flags & ALTAUTOSCALE_MIN) {
+ /* measure the amplitude of the function. Make sure that
+ graph boundaries are slightly lower than min vals
+ so we can see amplitude on the graph */
+ adj = (im->maxval - im->minval) * 0.1;
+ im->minval -= adj;
+ }
+ else if(im->extra_flags & ALTAUTOSCALE_MAX) {
+ /* measure the amplitude of the function. Make sure that
+ graph boundaries are slightly higher than max vals
+ so we can see amplitude on the graph */
+ adj = (im->maxval - im->minval) * 0.1;
+ im->maxval += adj;
+ }
+ else {
+ scaled_min = im->minval / im->magfact;
+ scaled_max = im->maxval / im->magfact;
+
+ for (i=1; sensiblevalues[i] > 0; i++){
+ if (sensiblevalues[i-1]>=scaled_min &&
+ sensiblevalues[i]<=scaled_min)
+ im->minval = sensiblevalues[i]*(im->magfact);
+
+ if (-sensiblevalues[i-1]<=scaled_min &&
+ -sensiblevalues[i]>=scaled_min)
+ im->minval = -sensiblevalues[i-1]*(im->magfact);
+
+ if (sensiblevalues[i-1] >= scaled_max &&
+ sensiblevalues[i] <= scaled_max)
+ im->maxval = sensiblevalues[i-1]*(im->magfact);
+
+ if (-sensiblevalues[i-1]<=scaled_max &&
+ -sensiblevalues[i] >=scaled_max)
+ im->maxval = -sensiblevalues[i]*(im->magfact);
+ }
+ }
+ } else {
+ /* adjust min and max to the grid definition if there is one */
+ im->minval = (double)im->ylabfact * im->ygridstep *
+ floor(im->minval / ((double)im->ylabfact * im->ygridstep));
+ im->maxval = (double)im->ylabfact * im->ygridstep *
+ ceil(im->maxval /( (double)im->ylabfact * im->ygridstep));
+ }
+
+#ifdef DEBUG
+ fprintf(stderr,"SCALED Min: %6.2f Max: %6.2f Factor: %6.2f\n",
+ im->minval,im->maxval,im->magfact);
+#endif
+}
+
+void
+apply_gridfit(image_desc_t *im)
+{
+ if (isnan(im->minval) || isnan(im->maxval))
+ return;
+ ytr(im,DNAN);
+ if (im->logarithmic) {
+ double ya, yb, ypix, ypixfrac;
+ double log10_range = log10(im->maxval) - log10(im->minval);
+ ya = pow((double)10, floor(log10(im->minval)));
+ while (ya < im->minval)
+ ya *= 10;
+ if (ya > im->maxval)
+ return; /* don't have y=10^x gridline */
+ yb = ya * 10;
+ if (yb <= im->maxval) {
+ /* we have at least 2 y=10^x gridlines.
+ Make sure distance between them in pixels
+ are an integer by expanding im->maxval */
+ double y_pixel_delta = ytr(im, ya) - ytr(im, yb);
+ double factor = y_pixel_delta / floor(y_pixel_delta);
+ double new_log10_range = factor * log10_range;
+ double new_ymax_log10 = log10(im->minval) + new_log10_range;
+ im->maxval = pow(10, new_ymax_log10);
+ ytr(im,DNAN); /* reset precalc */
+ log10_range = log10(im->maxval) - log10(im->minval);
+ }
+ /* make sure first y=10^x gridline is located on
+ integer pixel position by moving scale slightly
+ downwards (sub-pixel movement) */
+ ypix = ytr(im, ya) + im->ysize; /* add im->ysize so it always is positive */
+ ypixfrac = ypix - floor(ypix);
+ if (ypixfrac > 0 && ypixfrac < 1) {
+ double yfrac = ypixfrac / im->ysize;
+ im->minval = pow(10, log10(im->minval) - yfrac * log10_range);
+ im->maxval = pow(10, log10(im->maxval) - yfrac * log10_range);
+ ytr(im,DNAN); /* reset precalc */
+ }
+ } else {
+ /* Make sure we have an integer pixel distance between
+ each minor gridline */
+ double ypos1 = ytr(im, im->minval);
+ double ypos2 = ytr(im, im->minval + im->ygrid_scale.gridstep);
+ double y_pixel_delta = ypos1 - ypos2;
+ double factor = y_pixel_delta / floor(y_pixel_delta);
+ double new_range = factor * (im->maxval - im->minval);
+ double gridstep = im->ygrid_scale.gridstep;
+ double minor_y, minor_y_px, minor_y_px_frac;
+ if (im->maxval > 0.0)
+ im->maxval = im->minval + new_range;
+ else
+ im->minval = im->maxval - new_range;
+ ytr(im,DNAN); /* reset precalc */
+ /* make sure first minor gridline is on integer pixel y coord */
+ minor_y = gridstep * floor(im->minval / gridstep);
+ while (minor_y < im->minval)
+ minor_y += gridstep;
+ minor_y_px = ytr(im, minor_y) + im->ysize; /* ensure > 0 by adding ysize */
+ minor_y_px_frac = minor_y_px - floor(minor_y_px);
+ if (minor_y_px_frac > 0 && minor_y_px_frac < 1) {
+ double yfrac = minor_y_px_frac / im->ysize;
+ double range = im->maxval - im->minval;
+ im->minval = im->minval - yfrac * range;
+ im->maxval = im->maxval - yfrac * range;
+ ytr(im,DNAN); /* reset precalc */
+ }
+ calc_horizontal_grid(im); /* recalc with changed im->maxval */
+ }
+}
+
+/* reduce data reimplementation by Alex */
+
+void
+reduce_data(
+ enum cf_en cf, /* which consolidation function ?*/
+ unsigned long cur_step, /* step the data currently is in */
+ time_t *start, /* start, end and step as requested ... */
+ time_t *end, /* ... by the application will be ... */
+ unsigned long *step, /* ... adjusted to represent reality */
+ unsigned long *ds_cnt, /* number of data sources in file */
+ rrd_value_t **data) /* two dimensional array containing the data */
+{
+ int i,reduce_factor = ceil((double)(*step) / (double)cur_step);
+ unsigned long col,dst_row,row_cnt,start_offset,end_offset,skiprows=0;
+ rrd_value_t *srcptr,*dstptr;
+
+ (*step) = cur_step*reduce_factor; /* set new step size for reduced data */
+ dstptr = *data;
+ srcptr = *data;
+ row_cnt = ((*end)-(*start))/cur_step;
+
+#ifdef DEBUG
+#define DEBUG_REDUCE
+#endif
+#ifdef DEBUG_REDUCE
+printf("Reducing %lu rows with factor %i time %lu to %lu, step %lu\n",
+ row_cnt,reduce_factor,*start,*end,cur_step);
+for (col=0;col<row_cnt;col++) {
+ printf("time %10lu: ",*start+(col+1)*cur_step);
+ for (i=0;i<*ds_cnt;i++)
+ printf(" %8.2e",srcptr[*ds_cnt*col+i]);
+ printf("\n");
+}
+#endif
+
+ /* We have to combine [reduce_factor] rows of the source
+ ** into one row for the destination. Doing this we also
+ ** need to take care to combine the correct rows. First
+ ** alter the start and end time so that they are multiples
+ ** of the new step time. We cannot reduce the amount of
+ ** time so we have to move the end towards the future and
+ ** the start towards the past.
+ */
+ end_offset = (*end) % (*step);
+ start_offset = (*start) % (*step);
+
+ /* If there is a start offset (which cannot be more than
+ ** one destination row), skip the appropriate number of
+ ** source rows and one destination row. The appropriate
+ ** number is what we do know (start_offset/cur_step) of
+ ** the new interval (*step/cur_step aka reduce_factor).
+ */
+#ifdef DEBUG_REDUCE
+printf("start_offset: %lu end_offset: %lu\n",start_offset,end_offset);
+printf("row_cnt before: %lu\n",row_cnt);
+#endif
+ if (start_offset) {
+ (*start) = (*start)-start_offset;
+ skiprows=reduce_factor-start_offset/cur_step;
+ srcptr+=skiprows* *ds_cnt;
+ for (col=0;col<(*ds_cnt);col++) *dstptr++ = DNAN;
+ row_cnt-=skiprows;
+ }
+#ifdef DEBUG_REDUCE
+printf("row_cnt between: %lu\n",row_cnt);
+#endif
+
+ /* At the end we have some rows that are not going to be
+ ** used, the amount is end_offset/cur_step
+ */
+ if (end_offset) {
+ (*end) = (*end)-end_offset+(*step);
+ skiprows = end_offset/cur_step;
+ row_cnt-=skiprows;
+ }
+#ifdef DEBUG_REDUCE
+printf("row_cnt after: %lu\n",row_cnt);
+#endif
+
+/* Sanity check: row_cnt should be multiple of reduce_factor */
+/* if this gets triggered, something is REALLY WRONG ... we die immediately */
+
+ if (row_cnt%reduce_factor) {
+ printf("SANITY CHECK: %lu rows cannot be reduced by %i \n",
+ row_cnt,reduce_factor);
+ printf("BUG in reduce_data()\n");
+ exit(1);
+ }
+
+ /* Now combine reduce_factor intervals at a time
+ ** into one interval for the destination.
+ */
+
+ for (dst_row=0;(long int)row_cnt>=reduce_factor;dst_row++) {
+ for (col=0;col<(*ds_cnt);col++) {
+ rrd_value_t newval=DNAN;
+ unsigned long validval=0;
+
+ for (i=0;i<reduce_factor;i++) {
+ if (isnan(srcptr[i*(*ds_cnt)+col])) {
+ continue;
+ }
+ validval++;
+ if (isnan(newval)) newval = srcptr[i*(*ds_cnt)+col];
+ else {
+ switch (cf) {
+ case CF_HWPREDICT:
+ case CF_DEVSEASONAL:
+ case CF_DEVPREDICT:
+ case CF_SEASONAL:
+ case CF_AVERAGE:
+ newval += srcptr[i*(*ds_cnt)+col];
+ break;
+ case CF_MINIMUM:
+ newval = min (newval,srcptr[i*(*ds_cnt)+col]);
+ break;
+ case CF_FAILURES:
+ /* an interval contains a failure if any subintervals contained a failure */
+ case CF_MAXIMUM:
+ newval = max (newval,srcptr[i*(*ds_cnt)+col]);
+ break;
+ case CF_LAST:
+ newval = srcptr[i*(*ds_cnt)+col];
+ break;
+ }
+ }
+ }
+ if (validval == 0){newval = DNAN;} else{
+ switch (cf) {
+ case CF_HWPREDICT:
+ case CF_DEVSEASONAL:
+ case CF_DEVPREDICT:
+ case CF_SEASONAL:
+ case CF_AVERAGE:
+ newval /= validval;
+ break;
+ case CF_MINIMUM:
+ case CF_FAILURES:
+ case CF_MAXIMUM:
+ case CF_LAST:
+ break;
+ }
+ }
+ *dstptr++=newval;
+ }
+ srcptr+=(*ds_cnt)*reduce_factor;
+ row_cnt-=reduce_factor;
+ }
+ /* If we had to alter the endtime, we didn't have enough
+ ** source rows to fill the last row. Fill it with NaN.
+ */
+ if (end_offset) for (col=0;col<(*ds_cnt);col++) *dstptr++ = DNAN;
+#ifdef DEBUG_REDUCE
+ row_cnt = ((*end)-(*start))/ *step;
+ srcptr = *data;
+ printf("Done reducing. Currently %lu rows, time %lu to %lu, step %lu\n",
+ row_cnt,*start,*end,*step);
+for (col=0;col<row_cnt;col++) {
+ printf("time %10lu: ",*start+(col+1)*(*step));
+ for (i=0;i<*ds_cnt;i++)
+ printf(" %8.2e",srcptr[*ds_cnt*col+i]);
+ printf("\n");
+}
+#endif
+}
+
+
+/* get the data required for the graphs from the
+ relevant rrds ... */
+
+int
+data_fetch(image_desc_t *im )
+{
+ int i,ii;
+ int skip;
+
+ /* pull the data from the rrd files ... */
+ for (i=0;i< (int)im->gdes_c;i++){
+ /* only GF_DEF elements fetch data */
+ if (im->gdes[i].gf != GF_DEF)
+ continue;
+
+ skip=0;
+ /* do we have it already ?*/
+ for (ii=0;ii<i;ii++) {
+ if (im->gdes[ii].gf != GF_DEF)
+ continue;
+ if ((strcmp(im->gdes[i].rrd, im->gdes[ii].rrd) == 0)
+ && (im->gdes[i].cf == im->gdes[ii].cf)
+ && (im->gdes[i].cf_reduce == im->gdes[ii].cf_reduce)
+ && (im->gdes[i].start_orig == im->gdes[ii].start_orig)
+ && (im->gdes[i].end_orig == im->gdes[ii].end_orig)
+ && (im->gdes[i].step_orig == im->gdes[ii].step_orig)) {
+ /* OK, the data is already there.
+ ** Just copy the header portion
+ */
+ im->gdes[i].start = im->gdes[ii].start;
+ im->gdes[i].end = im->gdes[ii].end;
+ im->gdes[i].step = im->gdes[ii].step;
+ im->gdes[i].ds_cnt = im->gdes[ii].ds_cnt;
+ im->gdes[i].ds_namv = im->gdes[ii].ds_namv;
+ im->gdes[i].data = im->gdes[ii].data;
+ im->gdes[i].data_first = 0;
+ skip=1;
+ }
+ if (skip)
+ break;
+ }
+ if (! skip) {
+ unsigned long ft_step = im->gdes[i].step ; /* ft_step will record what we got from fetch */
+
+ if((rrd_fetch_fn(im->gdes[i].rrd,
+ im->gdes[i].cf,
+ &im->gdes[i].start,
+ &im->gdes[i].end,
+ &ft_step,
+ &im->gdes[i].ds_cnt,
+ &im->gdes[i].ds_namv,
+ &im->gdes[i].data)) == -1){
+ return -1;
+ }
+ im->gdes[i].data_first = 1;
+
+ if (ft_step < im->gdes[i].step) {
+ reduce_data(im->gdes[i].cf_reduce,
+ ft_step,
+ &im->gdes[i].start,
+ &im->gdes[i].end,
+ &im->gdes[i].step,
+ &im->gdes[i].ds_cnt,
+ &im->gdes[i].data);
+ } else {
+ im->gdes[i].step = ft_step;
+ }
+ }
+
+ /* lets see if the required data source is really there */
+ for(ii=0;ii<(int)im->gdes[i].ds_cnt;ii++){
+ if(strcmp(im->gdes[i].ds_namv[ii],im->gdes[i].ds_nam) == 0){
+ im->gdes[i].ds=ii; }
+ }
+ if (im->gdes[i].ds== -1){
+ rrd_set_error("No DS called '%s' in '%s'",
+ im->gdes[i].ds_nam,im->gdes[i].rrd);
+ return -1;
+ }
+
+ }
+ return 0;
+}
+
+/* evaluate the expressions in the CDEF functions */
+
+/*************************************************************
+ * CDEF stuff
+ *************************************************************/
+
+long
+find_var_wrapper(void *arg1, char *key)
+{
+ return find_var((image_desc_t *) arg1, key);
+}
+
+/* find gdes containing var*/
+long
+find_var(image_desc_t *im, char *key){
+ long ii;
+ for(ii=0;ii<im->gdes_c-1;ii++){
+ if((im->gdes[ii].gf == GF_DEF
+ || im->gdes[ii].gf == GF_VDEF
+ || im->gdes[ii].gf == GF_CDEF)
+ && (strcmp(im->gdes[ii].vname,key) == 0)){
+ return ii;
+ }
+ }
+ return -1;
+}
+
+/* find the largest common denominator for all the numbers
+ in the 0 terminated num array */
+long
+lcd(long *num){
+ long rest;
+ int i;
+ for (i=0;num[i+1]!=0;i++){
+ do {
+ rest=num[i] % num[i+1];
+ num[i]=num[i+1]; num[i+1]=rest;
+ } while (rest!=0);
+ num[i+1] = num[i];
+ }
+/* return i==0?num[i]:num[i-1]; */
+ return num[i];
+}
+
+/* run the rpn calculator on all the VDEF and CDEF arguments */
+int
+data_calc( image_desc_t *im){
+
+ int gdi;
+ int dataidx;
+ long *steparray, rpi;
+ int stepcnt;
+ time_t now;
+ rpnstack_t rpnstack;
+
+ rpnstack_init(&rpnstack);
+
+ for (gdi=0;gdi<im->gdes_c;gdi++){
+ /* Look for GF_VDEF and GF_CDEF in the same loop,
+ * so CDEFs can use VDEFs and vice versa
+ */
+ switch (im->gdes[gdi].gf) {
+ case GF_XPORT:
+ break;
+ case GF_SHIFT: {
+ graph_desc_t *vdp = &im->gdes[im->gdes[gdi].vidx];
+
+ /* remove current shift */
+ vdp->start -= vdp->shift;
+ vdp->end -= vdp->shift;
+
+ /* vdef */
+ if (im->gdes[gdi].shidx >= 0)
+ vdp->shift = im->gdes[im->gdes[gdi].shidx].vf.val;
+ /* constant */
+ else
+ vdp->shift = im->gdes[gdi].shval;
+
+ /* normalize shift to multiple of consolidated step */
+ vdp->shift = (vdp->shift / (long)vdp->step) * (long)vdp->step;
+
+ /* apply shift */
+ vdp->start += vdp->shift;
+ vdp->end += vdp->shift;
+ break;
+ }
+ case GF_VDEF:
+ /* A VDEF has no DS. This also signals other parts
+ * of rrdtool that this is a VDEF value, not a CDEF.
+ */
+ im->gdes[gdi].ds_cnt = 0;
+ if (vdef_calc(im,gdi)) {
+ rrd_set_error("Error processing VDEF '%s'"
+ ,im->gdes[gdi].vname
+ );
+ rpnstack_free(&rpnstack);
+ return -1;
+ }
+ break;
+ case GF_CDEF:
+ im->gdes[gdi].ds_cnt = 1;
+ im->gdes[gdi].ds = 0;
+ im->gdes[gdi].data_first = 1;
+ im->gdes[gdi].start = 0;
+ im->gdes[gdi].end = 0;
+ steparray=NULL;
+ stepcnt = 0;
+ dataidx=-1;
+
+ /* Find the variables in the expression.
+ * - VDEF variables are substituted by their values
+ * and the opcode is changed into OP_NUMBER.
+ * - CDEF variables are analized for their step size,
+ * the lowest common denominator of all the step
+ * sizes of the data sources involved is calculated
+ * and the resulting number is the step size for the
+ * resulting data source.
+ */
+ for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
+ if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE ||
+ im->gdes[gdi].rpnp[rpi].op == OP_PREV_OTHER){
+ long ptr = im->gdes[gdi].rpnp[rpi].ptr;
+ if (im->gdes[ptr].ds_cnt == 0) { /* this is a VDEF data source */
+#if 0
+ printf("DEBUG: inside CDEF '%s' processing VDEF '%s'\n",
+ im->gdes[gdi].vname,
+ im->gdes[ptr].vname);
+ printf("DEBUG: value from vdef is %f\n",im->gdes[ptr].vf.val);
+#endif
+ im->gdes[gdi].rpnp[rpi].val = im->gdes[ptr].vf.val;
+ im->gdes[gdi].rpnp[rpi].op = OP_NUMBER;
+ } else { /* normal variables and PREF(variables) */
+
+ /* add one entry to the array that keeps track of the step sizes of the
+ * data sources going into the CDEF. */
+ if ((steparray =
+ rrd_realloc(steparray,
+ (++stepcnt+1)*sizeof(*steparray)))==NULL){
+ rrd_set_error("realloc steparray");
+ rpnstack_free(&rpnstack);
+ return -1;
+ };
+
+ steparray[stepcnt-1] = im->gdes[ptr].step;
+
+ /* adjust start and end of cdef (gdi) so
+ * that it runs from the latest start point
+ * to the earliest endpoint of any of the
+ * rras involved (ptr)
+ */
+
+ if(im->gdes[gdi].start < im->gdes[ptr].start)
+ im->gdes[gdi].start = im->gdes[ptr].start;
+
+ if(im->gdes[gdi].end == 0 ||
+ im->gdes[gdi].end > im->gdes[ptr].end)
+ im->gdes[gdi].end = im->gdes[ptr].end;
+
+ /* store pointer to the first element of
+ * the rra providing data for variable,
+ * further save step size and data source
+ * count of this rra
+ */
+ im->gdes[gdi].rpnp[rpi].data = im->gdes[ptr].data + im->gdes[ptr].ds;
+ im->gdes[gdi].rpnp[rpi].step = im->gdes[ptr].step;
+ im->gdes[gdi].rpnp[rpi].ds_cnt = im->gdes[ptr].ds_cnt;
+
+ /* backoff the *.data ptr; this is done so
+ * rpncalc() function doesn't have to treat
+ * the first case differently
+ */
+ } /* if ds_cnt != 0 */
+ } /* if OP_VARIABLE */
+ } /* loop through all rpi */
+
+ /* move the data pointers to the correct period */
+ for(rpi=0;im->gdes[gdi].rpnp[rpi].op != OP_END;rpi++){
+ if(im->gdes[gdi].rpnp[rpi].op == OP_VARIABLE ||
+ im->gdes[gdi].rpnp[rpi].op == OP_PREV_OTHER){
+ long ptr = im->gdes[gdi].rpnp[rpi].ptr;
+ long diff = im->gdes[gdi].start - im->gdes[ptr].start;
+
+ if(diff > 0)
+ im->gdes[gdi].rpnp[rpi].data += (diff / im->gdes[ptr].step) * im->gdes[ptr].ds_cnt;
+ }
+ }
+
+ if(steparray == NULL){
+ rrd_set_error("rpn expressions without DEF"
+ " or CDEF variables are not supported");
+ rpnstack_free(&rpnstack);
+ return -1;
+ }
+ steparray[stepcnt]=0;
+ /* Now find the resulting step. All steps in all
+ * used RRAs have to be visited
+ */
+ im->gdes[gdi].step = lcd(steparray);
+ free(steparray);
+ if((im->gdes[gdi].data = malloc((
+ (im->gdes[gdi].end-im->gdes[gdi].start)
+ / im->gdes[gdi].step)
+ * sizeof(double)))==NULL){
+ rrd_set_error("malloc im->gdes[gdi].data");
+ rpnstack_free(&rpnstack);
+ return -1;
+ }
+
+ /* Step through the new cdef results array and
+ * calculate the values
+ */
+ for (now = im->gdes[gdi].start + im->gdes[gdi].step;
+ now<=im->gdes[gdi].end;
+ now += im->gdes[gdi].step)
+ {
+ rpnp_t *rpnp = im -> gdes[gdi].rpnp;
+
+ /* 3rd arg of rpn_calc is for OP_VARIABLE lookups;
+ * in this case we are advancing by timesteps;
+ * we use the fact that time_t is a synonym for long
+ */
+ if (rpn_calc(rpnp,&rpnstack,(long) now,
+ im->gdes[gdi].data,++dataidx) == -1) {
+ /* rpn_calc sets the error string */
+ rpnstack_free(&rpnstack);
+ return -1;
+ }
+ } /* enumerate over time steps within a CDEF */
+ break;
+ default:
+ continue;
+ }
+ } /* enumerate over CDEFs */
+ rpnstack_free(&rpnstack);
+ return 0;
+}
+
+/* massage data so, that we get one value for each x coordinate in the graph */
+int
+data_proc( image_desc_t *im ){
+ long i,ii;
+ double pixstep = (double)(im->end-im->start)
+ /(double)im->xsize; /* how much time
+ passes in one pixel */
+ double paintval;
+ double minval=DNAN,maxval=DNAN;
+
+ unsigned long gr_time;
+
+ /* memory for the processed data */
+ for(i=0;i<im->gdes_c;i++) {
+ if((im->gdes[i].gf==GF_LINE) ||
+ (im->gdes[i].gf==GF_AREA) ||
+ (im->gdes[i].gf==GF_TICK)) {
+ if((im->gdes[i].p_data = malloc((im->xsize +1)
+ * sizeof(rrd_value_t)))==NULL){
+ rrd_set_error("malloc data_proc");
+ return -1;
+ }
+ }
+ }
+
+ for (i=0;i<im->xsize;i++) { /* for each pixel */
+ long vidx;
+ gr_time = im->start+pixstep*i; /* time of the current step */
+ paintval=0.0;
+
+ for (ii=0;ii<im->gdes_c;ii++) {
+ double value;
+ switch (im->gdes[ii].gf) {
+ case GF_LINE:
+ case GF_AREA:
+ case GF_TICK:
+ if (!im->gdes[ii].stack)
+ paintval = 0.0;
+ value = im->gdes[ii].yrule;
+ if (isnan(value) || (im->gdes[ii].gf == GF_TICK)) {
+ /* The time of the data doesn't necessarily match
+ ** the time of the graph. Beware.
+ */
+ vidx = im->gdes[ii].vidx;
+ if (im->gdes[vidx].gf == GF_VDEF) {
+ value = im->gdes[vidx].vf.val;
+ } else if (((long int)gr_time >= (long int)im->gdes[vidx].start) &&
+ ((long int)gr_time <= (long int)im->gdes[vidx].end) ) {
+ value = im->gdes[vidx].data[
+ (unsigned long) floor(
+ (double)(gr_time - im->gdes[vidx].start)
+ / im->gdes[vidx].step)
+ * im->gdes[vidx].ds_cnt
+ + im->gdes[vidx].ds
+ ];
+ } else {
+ value = DNAN;
+ }
+ };
+
+ if (! isnan(value)) {
+ paintval += value;
+ im->gdes[ii].p_data[i] = paintval;
+ /* GF_TICK: the data values are not
+ ** relevant for min and max
+ */
+ if (finite(paintval) && im->gdes[ii].gf != GF_TICK ) {
+ if ((isnan(minval) || paintval < minval ) &&
+ ! (im->logarithmic && paintval <= 0.0))
+ minval = paintval;
+ if (isnan(maxval) || paintval > maxval)
+ maxval = paintval;
+ }
+ } else {
+ im->gdes[ii].p_data[i] = DNAN;
+ }
+ break;
+ case GF_STACK:
+ rrd_set_error("STACK should already be turned into LINE or AREA here");
+ return -1;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /* if min or max have not been asigned a value this is because
+ there was no data in the graph ... this is not good ...
+ lets set these to dummy values then ... */
+
+ if (im->logarithmic) {
+ if (isnan(minval)) minval = 0.2;
+ if (isnan(maxval)) maxval = 5.1;
+ }
+ else {
+ if (isnan(minval)) minval = 0.0;
+ if (isnan(maxval)) maxval = 1.0;
+ }
+
+ /* adjust min and max values */
+ if (isnan(im->minval)
+ /* don't adjust low-end with log scale */ /* why not? */
+ || ((!im->rigid) && im->minval > minval)
+ ) {
+ if (im->logarithmic)
+ im->minval = minval * 0.5;
+ else
+ im->minval = minval;
+ }
+ if (isnan(im->maxval)
+ || (!im->rigid && im->maxval < maxval)
+ ) {
+ if (im->logarithmic)
+ im->maxval = maxval * 2.0;
+ else
+ im->maxval = maxval;
+ }
+ /* make sure min is smaller than max */
+ if (im->minval > im->maxval) {
+ im->minval = 0.99 * im->maxval;
+ }
+
+ /* make sure min and max are not equal */
+ if (im->minval == im->maxval) {
+ im->maxval *= 1.01;
+ if (! im->logarithmic) {
+ im->minval *= 0.99;
+ }
+ /* make sure min and max are not both zero */
+ if (im->maxval == 0.0) {
+ im->maxval = 1.0;
+ }
+ }
+ return 0;
+}
+
+
+
+/* identify the point where the first gridline, label ... gets placed */
+
+time_t
+find_first_time(
+ time_t start, /* what is the initial time */
+ enum tmt_en baseint, /* what is the basic interval */
+ long basestep /* how many if these do we jump a time */
+ )
+{
+ struct tm tm;
+ localtime_r(&start, &tm);
+ switch(baseint){
+ case TMT_SECOND:
+ tm.tm_sec -= tm.tm_sec % basestep; break;
+ case TMT_MINUTE:
+ tm.tm_sec=0;
+ tm.tm_min -= tm.tm_min % basestep;
+ break;
+ case TMT_HOUR:
+ tm.tm_sec=0;
+ tm.tm_min = 0;
+ tm.tm_hour -= tm.tm_hour % basestep; break;
+ case TMT_DAY:
+ /* we do NOT look at the basestep for this ... */
+ tm.tm_sec=0;
+ tm.tm_min = 0;
+ tm.tm_hour = 0; break;
+ case TMT_WEEK:
+ /* we do NOT look at the basestep for this ... */
+ tm.tm_sec=0;
+ tm.tm_min = 0;
+ tm.tm_hour = 0;
+ tm.tm_mday -= tm.tm_wday -1; /* -1 because we want the monday */
+ if (tm.tm_wday==0) tm.tm_mday -= 7; /* we want the *previous* monday */
+ break;
+ case TMT_MONTH:
+ tm.tm_sec=0;
+ tm.tm_min = 0;
+ tm.tm_hour = 0;
+ tm.tm_mday = 1;
+ tm.tm_mon -= tm.tm_mon % basestep; break;
+
+ case TMT_YEAR:
+ tm.tm_sec=0;
+ tm.tm_min = 0;
+ tm.tm_hour = 0;
+ tm.tm_mday = 1;
+ tm.tm_mon = 0;
+ tm.tm_year -= (tm.tm_year+1900) % basestep;
+
+ }
+ return mktime(&tm);
+}
+/* identify the point where the next gridline, label ... gets placed */
+time_t
+find_next_time(
+ time_t current, /* what is the initial time */
+ enum tmt_en baseint, /* what is the basic interval */
+ long basestep /* how many if these do we jump a time */
+ )
+{
+ struct tm tm;
+ time_t madetime;
+ localtime_r(¤t, &tm);
+ do {
+ switch(baseint){
+ case TMT_SECOND:
+ tm.tm_sec += basestep; break;
+ case TMT_MINUTE:
+ tm.tm_min += basestep; break;
+ case TMT_HOUR:
+ tm.tm_hour += basestep; break;
+ case TMT_DAY:
+ tm.tm_mday += basestep; break;
+ case TMT_WEEK:
+ tm.tm_mday += 7*basestep; break;
+ case TMT_MONTH:
+ tm.tm_mon += basestep; break;
+ case TMT_YEAR:
+ tm.tm_year += basestep;
+ }
+ madetime = mktime(&tm);
+ } while (madetime == -1); /* this is necessary to skip impssible times
+ like the daylight saving time skips */
+ return madetime;
+
+}
+
+
+/* calculate values required for PRINT and GPRINT functions */
+
+int
+print_calc(image_desc_t *im, char ***prdata)
+{
+ long i,ii,validsteps;
+ double printval;
+ struct tm tmvdef;
+ int graphelement = 0;
+ long vidx;
+ int max_ii;
+ double magfact = -1;
+ char *si_symb = "";
+ char *percent_s;
+ int prlines = 1;
+ /* wow initializing tmvdef is quite a task :-) */
+ time_t now = time(NULL);
+ localtime_r(&now,&tmvdef);
+ if (im->imginfo) prlines++;
+ for(i=0;i<im->gdes_c;i++){
+ vidx = im->gdes[i].vidx;
+ switch(im->gdes[i].gf){
+ case GF_PRINT:
+ prlines++;
+ if(((*prdata) = rrd_realloc((*prdata),prlines*sizeof(char *)))==NULL){
+ rrd_set_error("realloc prdata");
+ return 0;
+ }
+ case GF_GPRINT:
+ /* PRINT and GPRINT can now print VDEF generated values.
+ * There's no need to do any calculations on them as these
+ * calculations were already made.
+ */
+ if (im->gdes[vidx].gf==GF_VDEF) { /* simply use vals */
+ printval = im->gdes[vidx].vf.val;
+ localtime_r(&im->gdes[vidx].vf.when,&tmvdef);
+ } else { /* need to calculate max,min,avg etcetera */
+ max_ii =((im->gdes[vidx].end
+ - im->gdes[vidx].start)
+ / im->gdes[vidx].step
+ * im->gdes[vidx].ds_cnt);
+ printval = DNAN;
+ validsteps = 0;
+ for( ii=im->gdes[vidx].ds;
+ ii < max_ii;
+ ii+=im->gdes[vidx].ds_cnt){
+ if (! finite(im->gdes[vidx].data[ii]))
+ continue;
+ if (isnan(printval)){
+ printval = im->gdes[vidx].data[ii];
+ validsteps++;
+ continue;
+ }
+
+ switch (im->gdes[i].cf){
+ case CF_HWPREDICT:
+ case CF_DEVPREDICT:
+ case CF_DEVSEASONAL:
+ case CF_SEASONAL:
+ case CF_AVERAGE:
+ validsteps++;
+ printval += im->gdes[vidx].data[ii];
+ break;
+ case CF_MINIMUM:
+ printval = min( printval, im->gdes[vidx].data[ii]);
+ break;
+ case CF_FAILURES:
+ case CF_MAXIMUM:
+ printval = max( printval, im->gdes[vidx].data[ii]);
+ break;
+ case CF_LAST:
+ printval = im->gdes[vidx].data[ii];
+ }
+ }
+ if (im->gdes[i].cf==CF_AVERAGE || im->gdes[i].cf > CF_LAST) {
+ if (validsteps > 1) {
+ printval = (printval / validsteps);
+ }
+ }
+ } /* prepare printval */
+
+ if ((percent_s = strstr(im->gdes[i].format,"%S")) != NULL) {
+ /* Magfact is set to -1 upon entry to print_calc. If it
+ * is still less than 0, then we need to run auto_scale.
+ * Otherwise, put the value into the correct units. If
+ * the value is 0, then do not set the symbol or magnification
+ * so next the calculation will be performed again. */
+ if (magfact < 0.0) {
+ auto_scale(im,&printval,&si_symb,&magfact);
+ if (printval == 0.0)
+ magfact = -1.0;
+ } else {
+ printval /= magfact;
+ }
+ *(++percent_s) = 's';
+ } else if (strstr(im->gdes[i].format,"%s") != NULL) {
+ auto_scale(im,&printval,&si_symb,&magfact);
+ }
+
+ if (im->gdes[i].gf == GF_PRINT){
+ (*prdata)[prlines-2] = malloc((FMT_LEG_LEN+2)*sizeof(char));
+ (*prdata)[prlines-1] = NULL;
+ if (im->gdes[i].strftm){
+ strftime((*prdata)[prlines-2],FMT_LEG_LEN,im->gdes[i].format,&tmvdef);
+ } else {
+ if (bad_format(im->gdes[i].format)) {
+ rrd_set_error("bad format for PRINT in '%s'", im->gdes[i].format);
+ return -1;
+ }
+
+#ifdef HAVE_SNPRINTF
+ snprintf((*prdata)[prlines-2],FMT_LEG_LEN,im->gdes[i].format,printval,si_symb);
+#else
+ sprintf((*prdata)[prlines-2],im->gdes[i].format,printval,si_symb);
+#endif
+ }
+ } else {
+ /* GF_GPRINT */
+
+ if (im->gdes[i].strftm){
+ strftime(im->gdes[i].legend,FMT_LEG_LEN,im->gdes[i].format,&tmvdef);
+ } else {
+ if (bad_format(im->gdes[i].format)) {
+ rrd_set_error("bad format for GPRINT in '%s'", im->gdes[i].format);
+ return -1;
+ }
+#ifdef HAVE_SNPRINTF
+ snprintf(im->gdes[i].legend,FMT_LEG_LEN-2,im->gdes[i].format,printval,si_symb);
+#else
+ sprintf(im->gdes[i].legend,im->gdes[i].format,printval,si_symb);
+#endif
+ }
+ graphelement = 1;
+ }
+ break;
+ case GF_LINE:
+ case GF_AREA:
+ case GF_TICK:
+ graphelement = 1;
+ break;
+ case GF_HRULE:
+ if(isnan(im->gdes[i].yrule)) { /* we must set this here or the legend printer can not decide to print the legend */
+ im->gdes[i].yrule=im->gdes[vidx].vf.val;
+ };
+ graphelement = 1;
+ break;
+ case GF_VRULE:
+ if(im->gdes[i].xrule == 0) { /* again ... the legend printer needs it*/
+ im->gdes[i].xrule = im->gdes[vidx].vf.when;
+ };
+ graphelement = 1;
+ break;
+ case GF_COMMENT:
+ case GF_DEF:
+ case GF_CDEF:
+ case GF_VDEF:
+#ifdef WITH_PIECHART
+ case GF_PART:
+#endif
+ case GF_SHIFT:
+ case GF_XPORT:
+ break;
+ case GF_STACK:
+ rrd_set_error("STACK should already be turned into LINE or AREA here");
+ return -1;
+ break;
+ }
+ }
+ return graphelement;
+}
+
+
+/* place legends with color spots */
+int
+leg_place(image_desc_t *im)
+{
+ /* graph labels */
+ int interleg = im->text_prop[TEXT_PROP_LEGEND].size*2.0;
+ int border = im->text_prop[TEXT_PROP_LEGEND].size*2.0;
+ int fill=0, fill_last;
+ int leg_c = 0;
+ int leg_x = border, leg_y = im->yimg;
+ int leg_y_prev = im->yimg;
+ int leg_cc;
+ int glue = 0;
+ int i,ii, mark = 0;
+ char prt_fctn; /*special printfunctions */
+ int *legspace;
+
+ if( !(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH) ) {
+ if ((legspace = malloc(im->gdes_c*sizeof(int)))==NULL){
+ rrd_set_error("malloc for legspace");
+ return -1;
+ }
+
+ for(i=0;i<im->gdes_c;i++){
+ fill_last = fill;
+
+ /* hid legends for rules which are not displayed */
+
+ if(!(im->extra_flags & FORCE_RULES_LEGEND)) {
+ if (im->gdes[i].gf == GF_HRULE &&
+ (im->gdes[i].yrule < im->minval || im->gdes[i].yrule > im->maxval))
+ im->gdes[i].legend[0] = '\0';
+
+ if (im->gdes[i].gf == GF_VRULE &&
+ (im->gdes[i].xrule < im->start || im->gdes[i].xrule > im->end))
+ im->gdes[i].legend[0] = '\0';
+ }
+
+ leg_cc = strlen(im->gdes[i].legend);
+
+ /* is there a controle code ant the end of the legend string ? */
+ /* and it is not a tab \\t */
+ if (leg_cc >= 2 && im->gdes[i].legend[leg_cc-2] == '\\' && im->gdes[i].legend[leg_cc-1] != 't') {
+ prt_fctn = im->gdes[i].legend[leg_cc-1];
+ leg_cc -= 2;
+ im->gdes[i].legend[leg_cc] = '\0';
+ } else {
+ prt_fctn = '\0';
+ }
+ /* only valid control codes */
+ if (prt_fctn != 'l' &&
+ prt_fctn != 'n' && /* a synonym for l */
+ prt_fctn != 'r' &&
+ prt_fctn != 'j' &&
+ prt_fctn != 'c' &&
+ prt_fctn != 's' &&
+ prt_fctn != 't' &&
+ prt_fctn != '\0' &&
+ prt_fctn != 'g' ) {
+ free(legspace);
+ rrd_set_error("Unknown control code at the end of '%s\\%c'",im->gdes[i].legend,prt_fctn);
+ return -1;
+
+ }
+
+ /* remove exess space */
+ if ( prt_fctn == 'n' ){
+ prt_fctn='l';
+ }
+
+ while (prt_fctn=='g' &&
+ leg_cc > 0 &&
+ im->gdes[i].legend[leg_cc-1]==' '){
+ leg_cc--;
+ im->gdes[i].legend[leg_cc]='\0';
+ }
+ if (leg_cc != 0 ){
+ legspace[i]=(prt_fctn=='g' ? 0 : interleg);
+
+ if (fill > 0){
+ /* no interleg space if string ends in \g */
+ fill += legspace[i];
+ }
+ fill += gfx_get_text_width(im->canvas, fill+border,
+ im->text_prop[TEXT_PROP_LEGEND].font,
+ im->text_prop[TEXT_PROP_LEGEND].size,
+ im->tabwidth,
+ im->gdes[i].legend, 0);
+ leg_c++;
+ } else {
+ legspace[i]=0;
+ }
+ /* who said there was a special tag ... ?*/
+ if (prt_fctn=='g') {
+ prt_fctn = '\0';
+ }
+ if (prt_fctn == '\0') {
+ if (i == im->gdes_c -1 ) prt_fctn ='l';
+
+ /* is it time to place the legends ? */
+ if (fill > im->ximg - 2*border){
+ if (leg_c > 1) {
+ /* go back one */
+ i--;
+ fill = fill_last;
+ leg_c--;
+ prt_fctn = 'j';
+ } else {
+ prt_fctn = 'l';
+ }
+
+ }
+ }
+
+
+ if (prt_fctn != '\0'){
+ leg_x = border;
+ if (leg_c >= 2 && prt_fctn == 'j') {
+ glue = (im->ximg - fill - 2* border) / (leg_c-1);
+ } else {
+ glue = 0;
+ }
+ if (prt_fctn =='c') leg_x = (im->ximg - fill) / 2.0;
+ if (prt_fctn =='r') leg_x = im->ximg - fill - border;
+
+ for(ii=mark;ii<=i;ii++){
+ if(im->gdes[ii].legend[0]=='\0')
+ continue; /* skip empty legends */
+ im->gdes[ii].leg_x = leg_x;
+ im->gdes[ii].leg_y = leg_y;
+ leg_x +=
+ gfx_get_text_width(im->canvas, leg_x,
+ im->text_prop[TEXT_PROP_LEGEND].font,
+ im->text_prop[TEXT_PROP_LEGEND].size,
+ im->tabwidth,
+ im->gdes[ii].legend, 0)
+ + legspace[ii]
+ + glue;
+ }
+ leg_y_prev = leg_y;
+ /* only add y space if there was text on the line */
+ if (leg_x > border || prt_fctn == 's')
+ leg_y += im->text_prop[TEXT_PROP_LEGEND].size*1.8;
+ if (prt_fctn == 's')
+ leg_y -= im->text_prop[TEXT_PROP_LEGEND].size;
+ fill = 0;
+ leg_c = 0;
+ mark = ii;
+ }
+ }
+ im->yimg = leg_y_prev;
+ /* if we did place some legends we have to add vertical space */
+ if (leg_y != im->yimg){
+ im->yimg += im->text_prop[TEXT_PROP_LEGEND].size*1.8;
+ }
+ free(legspace);
+ }
+ return 0;
+}
+
+/* create a grid on the graph. it determines what to do
+ from the values of xsize, start and end */
+
+/* the xaxis labels are determined from the number of seconds per pixel
+ in the requested graph */
+
+
+
+int
+calc_horizontal_grid(image_desc_t *im)
+{
+ double range;
+ double scaledrange;
+ int pixel,i;
+ int gridind=0;
+ int decimals, fractionals;
+
+ im->ygrid_scale.labfact=2;
+ range = im->maxval - im->minval;
+ scaledrange = range / im->magfact;
+
+ /* does the scale of this graph make it impossible to put lines
+ on it? If so, give up. */
+ if (isnan(scaledrange)) {
+ return 0;
+ }
+
+ /* find grid spaceing */
+ pixel=1;
+ if(isnan(im->ygridstep)){
+ if(im->extra_flags & ALTYGRID) {
+ /* find the value with max number of digits. Get number of digits */
+ decimals = ceil(log10(max(fabs(im->maxval), fabs(im->minval))*im->viewfactor/im->magfact));
+ if(decimals <= 0) /* everything is small. make place for zero */
+ decimals = 1;
+
+ im->ygrid_scale.gridstep = pow((double)10, floor(log10(range*im->viewfactor/im->magfact)))/im->viewfactor*im->magfact;
+
+ if(im->ygrid_scale.gridstep == 0) /* range is one -> 0.1 is reasonable scale */
+ im->ygrid_scale.gridstep = 0.1;
+ /* should have at least 5 lines but no more then 15 */
+ if(range/im->ygrid_scale.gridstep < 5)
+ im->ygrid_scale.gridstep /= 10;
+ if(range/im->ygrid_scale.gridstep > 15)
+ im->ygrid_scale.gridstep *= 10;
+ if(range/im->ygrid_scale.gridstep > 5) {
+ im->ygrid_scale.labfact = 1;
+ if(range/im->ygrid_scale.gridstep > 8)
+ im->ygrid_scale.labfact = 2;
+ }
+ else {
+ im->ygrid_scale.gridstep /= 5;
+ im->ygrid_scale.labfact = 5;
+ }
+ fractionals = floor(log10(im->ygrid_scale.gridstep*(double)im->ygrid_scale.labfact*im->viewfactor/im->magfact));
+ if(fractionals < 0) { /* small amplitude. */
+ int len = decimals - fractionals + 1;
+ if (im->unitslength < len+2) im->unitslength = len+2;
+ sprintf(im->ygrid_scale.labfmt, "%%%d.%df%s", len, -fractionals,(im->symbol != ' ' ? " %c" : ""));
+ } else {
+ int len = decimals + 1;
+ if (im->unitslength < len+2) im->unitslength = len+2;
+ sprintf(im->ygrid_scale.labfmt, "%%%d.0f%s", len, ( im->symbol != ' ' ? " %c" : "" ));
+ }
+ }
+ else {
+ for(i=0;ylab[i].grid > 0;i++){
+ pixel = im->ysize / (scaledrange / ylab[i].grid);
+ gridind = i;
+ if (pixel > 7)
+ break;
+ }
+
+ for(i=0; i<4;i++) {
+ if (pixel * ylab[gridind].lfac[i] >= 2.5 * im->text_prop[TEXT_PROP_AXIS].size) {
+ im->ygrid_scale.labfact = ylab[gridind].lfac[i];
+ break;
+ }
+ }
+
+ im->ygrid_scale.gridstep = ylab[gridind].grid * im->magfact;
+ }
+ } else {
+ im->ygrid_scale.gridstep = im->ygridstep;
+ im->ygrid_scale.labfact = im->ylabfact;
+ }
+ return 1;
+}
+
+int draw_horizontal_grid(image_desc_t *im)
+{
+ int i;
+ double scaledstep;
+ char graph_label[100];
+ int nlabels=0;
+ double X0=im->xorigin;
+ double X1=im->xorigin+im->xsize;
+
+ int sgrid = (int)( im->minval / im->ygrid_scale.gridstep - 1);
+ int egrid = (int)( im->maxval / im->ygrid_scale.gridstep + 1);
+ double MaxY;
+ scaledstep = im->ygrid_scale.gridstep/(double)im->magfact*(double)im->viewfactor;
+ MaxY = scaledstep*(double)egrid;
+ for (i = sgrid; i <= egrid; i++){
+ double Y0=ytr(im,im->ygrid_scale.gridstep*i);
+ double YN=ytr(im,im->ygrid_scale.gridstep*(i+1));
+ if ( floor(Y0+0.5) >= im->yorigin-im->ysize
+ && floor(Y0+0.5) <= im->yorigin){
+ /* Make sure at least 2 grid labels are shown, even if it doesn't agree
+ with the chosen settings. Add a label if required by settings, or if
+ there is only one label so far and the next grid line is out of bounds. */
+ if(i % im->ygrid_scale.labfact == 0 || ( nlabels==1 && (YN < im->yorigin-im->ysize || YN > im->yorigin) )){
+ if (im->symbol == ' ') {
+ if(im->extra_flags & ALTYGRID) {
+ sprintf(graph_label,im->ygrid_scale.labfmt,scaledstep*(double)i);
+ } else {
+ if(MaxY < 10) {
+ sprintf(graph_label,"%4.1f",scaledstep*(double)i);
+ } else {
+ sprintf(graph_label,"%4.0f",scaledstep*(double)i);
+ }
+ }
+ }else {
+ char sisym = ( i == 0 ? ' ' : im->symbol);
+ if(im->extra_flags & ALTYGRID) {
+ sprintf(graph_label,im->ygrid_scale.labfmt,scaledstep*(double)i,sisym);
+ } else {
+ if(MaxY < 10){
+ sprintf(graph_label,"%4.1f %c",scaledstep*(double)i, sisym);
+ } else {
+ sprintf(graph_label,"%4.0f %c",scaledstep*(double)i, sisym);
+ }
+ }
+ }
+ nlabels++;
+
+ gfx_new_text ( im->canvas,
+ X0-im->text_prop[TEXT_PROP_AXIS].size, Y0,
+ im->graph_col[GRC_FONT],
+ im->text_prop[TEXT_PROP_AXIS].font,
+ im->text_prop[TEXT_PROP_AXIS].size,
+ im->tabwidth, 0.0, GFX_H_RIGHT, GFX_V_CENTER,
+ graph_label );
+ gfx_new_dashed_line ( im->canvas,
+ X0-2,Y0,
+ X1+2,Y0,
+ MGRIDWIDTH, im->graph_col[GRC_MGRID],
+ im->grid_dash_on, im->grid_dash_off);
+
+ } else if (!(im->extra_flags & NOMINOR)) {
+ gfx_new_dashed_line ( im->canvas,
+ X0-1,Y0,
+ X1+1,Y0,
+ GRIDWIDTH, im->graph_col[GRC_GRID],
+ im->grid_dash_on, im->grid_dash_off);
+
+ }
+ }
+ }
+ return 1;
+}
+
+/* this is frexp for base 10 */
+double frexp10(double, double *);
+double frexp10(double x, double *e) {
+ double mnt;
+ int iexp;
+
+ iexp = floor(log(fabs(x)) / log(10));
+ mnt = x / pow(10.0, iexp);
+ if(mnt >= 10.0) {
+ iexp++;
+ mnt = x / pow(10.0, iexp);
+ }
+ *e = iexp;
+ return mnt;
+}
+
+static int AlmostEqual2sComplement (float A, float B, int maxUlps)
+{
+
+ int aInt = *(int*)&A;
+ int bInt = *(int*)&B;
+ int intDiff;
+ /* Make sure maxUlps is non-negative and small enough that the
+ default NAN won't compare as equal to anything. */
+
+ /* assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); */
+
+ /* Make aInt lexicographically ordered as a twos-complement int */
+
+ if (aInt < 0)
+ aInt = 0x80000000l - aInt;
+
+ /* Make bInt lexicographically ordered as a twos-complement int */
+
+ if (bInt < 0)
+ bInt = 0x80000000l - bInt;
+
+ intDiff = abs(aInt - bInt);
+
+ if (intDiff <= maxUlps)
+ return 1;
+
+ return 0;
+}
+
+/* logaritmic horizontal grid */
+int
+horizontal_log_grid(image_desc_t *im)
+{
+ double yloglab[][10] = {
+ {1.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
+ {1.0, 5.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
+ {1.0, 2.0, 5.0, 7.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0},
+ {1.0, 2.0, 4.0, 6.0, 8.0, 10., 0.0, 0.0, 0.0, 0.0},
+ {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.},
+ {0,0,0,0,0, 0,0,0,0,0} /* last line */ };
+
+ int i, j, val_exp, min_exp;
+ double nex; /* number of decades in data */
+ double logscale; /* scale in logarithmic space */
+ int exfrac = 1; /* decade spacing */
+ int mid = -1; /* row in yloglab for major grid */
+ double mspac; /* smallest major grid spacing (pixels) */
+ int flab; /* first value in yloglab to use */
+ double value, tmp, pre_value;
+ double X0,X1,Y0;
+ char graph_label[100];
+
+ nex = log10(im->maxval / im->minval);
+ logscale = im->ysize / nex;
+
+ /* major spacing for data with high dynamic range */
+ while(logscale * exfrac < 3 * im->text_prop[TEXT_PROP_LEGEND].size) {
+ if(exfrac == 1) exfrac = 3;
+ else exfrac += 3;
+ }
+
+ /* major spacing for less dynamic data */
+ do {
+ /* search best row in yloglab */
+ mid++;
+ for(i = 0; yloglab[mid][i + 1] < 10.0; i++);
+ mspac = logscale * log10(10.0 / yloglab[mid][i]);
+ } while(mspac > 2 * im->text_prop[TEXT_PROP_LEGEND].size && yloglab[mid][0] > 0);
+ if(mid) mid--;
+
+ /* find first value in yloglab */
+ for(flab = 0; yloglab[mid][flab] < 10 && frexp10(im->minval, &tmp) > yloglab[mid][flab] ; flab++);
+ if(yloglab[mid][flab] == 10.0) {
+ tmp += 1.0;
+ flab = 0;
+ }
+ val_exp = tmp;
+ if(val_exp % exfrac) val_exp += abs(-val_exp % exfrac);
+
+ X0=im->xorigin;
+ X1=im->xorigin+im->xsize;
+
+ /* draw grid */
+ pre_value = DNAN;
+ while(1) {
+
+ value = yloglab[mid][flab] * pow(10.0, val_exp);
+ if ( AlmostEqual2sComplement(value,pre_value,4) ) break; /* it seems we are not converging */
+
+ pre_value = value;
+
+ Y0 = ytr(im, value);
+ if(floor(Y0+0.5) <= im->yorigin - im->ysize) break;
+
+ /* major grid line */
+ gfx_new_dashed_line ( im->canvas,
+ X0-2,Y0,
+ X1+2,Y0,
+ MGRIDWIDTH, im->graph_col[GRC_MGRID],
+ im->grid_dash_on, im->grid_dash_off);
+
+ /* label */
+ if (im->extra_flags & FORCE_UNITS_SI) {
+ int scale;
+ double pvalue;
+ char symbol;
+
+ scale = floor(val_exp / 3.0);
+ if( value >= 1.0 ) pvalue = pow(10.0, val_exp % 3);
+ else pvalue = pow(10.0, ((val_exp + 1) % 3) + 2);
+ pvalue *= yloglab[mid][flab];
+
+ if ( ((scale+si_symbcenter) < (int)sizeof(si_symbol)) &&
+ ((scale+si_symbcenter) >= 0) )
+ symbol = si_symbol[scale+si_symbcenter];
+ else
+ symbol = '?';
+
+ sprintf(graph_label,"%3.0f %c", pvalue, symbol);
+ } else
+ sprintf(graph_label,"%3.0e", value);
+ gfx_new_text ( im->canvas,
+ X0-im->text_prop[TEXT_PROP_AXIS].size, Y0,
+ im->graph_col[GRC_FONT],
+ im->text_prop[TEXT_PROP_AXIS].font,
+ im->text_prop[TEXT_PROP_AXIS].size,
+ im->tabwidth,0.0, GFX_H_RIGHT, GFX_V_CENTER,
+ graph_label );
+
+ /* minor grid */
+ if(mid < 4 && exfrac == 1) {
+ /* find first and last minor line behind current major line
+ * i is the first line and j tha last */
+ if(flab == 0) {
+ min_exp = val_exp - 1;
+ for(i = 1; yloglab[mid][i] < 10.0; i++);
+ i = yloglab[mid][i - 1] + 1;
+ j = 10;
+ }
+ else {
+ min_exp = val_exp;
+ i = yloglab[mid][flab - 1] + 1;
+ j = yloglab[mid][flab];
+ }
+
+ /* draw minor lines below current major line */
+ for(; i < j; i++) {
+
+ value = i * pow(10.0, min_exp);
+ if(value < im->minval) continue;
+
+ Y0 = ytr(im, value);
+ if(floor(Y0+0.5) <= im->yorigin - im->ysize) break;
+
+ /* draw lines */
+ gfx_new_dashed_line ( im->canvas,
+ X0-1,Y0,
+ X1+1,Y0,
+ GRIDWIDTH, im->graph_col[GRC_GRID],
+ im->grid_dash_on, im->grid_dash_off);
+ }
+ }
+ else if(exfrac > 1) {
+ for(i = val_exp - exfrac / 3 * 2; i < val_exp; i += exfrac / 3) {
+ value = pow(10.0, i);
+ if(value < im->minval) continue;
+
+ Y0 = ytr(im, value);
+ if(floor(Y0+0.5) <= im->yorigin - im->ysize) break;
+
+ /* draw lines */
+ gfx_new_dashed_line ( im->canvas,
+ X0-1,Y0,
+ X1+1,Y0,
+ GRIDWIDTH, im->graph_col[GRC_GRID],
+ im->grid_dash_on, im->grid_dash_off);
+ }
+ }
+
+ /* next decade */
+ if(yloglab[mid][++flab] == 10.0) {
+ flab = 0;
+ val_exp += exfrac;
+ }
+ }
+
+ /* draw minor lines after highest major line */
+ if(mid < 4 && exfrac == 1) {
+ /* find first and last minor line below current major line
+ * i is the first line and j tha last */
+ if(flab == 0) {
+ min_exp = val_exp - 1;
+ for(i = 1; yloglab[mid][i] < 10.0; i++);
+ i = yloglab[mid][i - 1] + 1;
+ j = 10;
+ }
+ else {
+ min_exp = val_exp;
+ i = yloglab[mid][flab - 1] + 1;
+ j = yloglab[mid][flab];
+ }
+
+ /* draw minor lines below current major line */
+ for(; i < j; i++) {
+
+ value = i * pow(10.0, min_exp);
+ if(value < im->minval) continue;
+
+ Y0 = ytr(im, value);
+ if(floor(Y0+0.5) <= im->yorigin - im->ysize) break;
+
+ /* draw lines */
+ gfx_new_dashed_line ( im->canvas,
+ X0-1,Y0,
+ X1+1,Y0,
+ GRIDWIDTH, im->graph_col[GRC_GRID],
+ im->grid_dash_on, im->grid_dash_off);
+ }
+ }
+ /* fancy minor gridlines */
+ else if(exfrac > 1) {
+ for(i = val_exp - exfrac / 3 * 2; i < val_exp; i += exfrac / 3) {
+ value = pow(10.0, i);
+ if(value < im->minval) continue;
+
+ Y0 = ytr(im, value);
+ if(floor(Y0+0.5) <= im->yorigin - im->ysize) break;
+
+ /* draw lines */
+ gfx_new_dashed_line ( im->canvas,
+ X0-1,Y0,
+ X1+1,Y0,
+ GRIDWIDTH, im->graph_col[GRC_GRID],
+ im->grid_dash_on, im->grid_dash_off);
+ }
+ }
+
+ return 1;
+}
+
+
+void
+vertical_grid(
+ image_desc_t *im )
+{
+ int xlab_sel; /* which sort of label and grid ? */
+ time_t ti, tilab, timajor;
+ long factor;
+ char graph_label[100];
+ double X0,Y0,Y1; /* points for filled graph and more*/
+ struct tm tm;
+
+ /* the type of time grid is determined by finding
+ the number of seconds per pixel in the graph */
+
+
+ if(im->xlab_user.minsec == -1){
+ factor=(im->end - im->start)/im->xsize;
+ xlab_sel=0;
+ while ( xlab[xlab_sel+1].minsec != -1
+ && xlab[xlab_sel+1].minsec <= factor) { xlab_sel++; } /* pick the last one */
+ while ( xlab[xlab_sel-1].minsec == xlab[xlab_sel].minsec
+ && xlab[xlab_sel].length > (im->end - im->start)) { xlab_sel--; } /* go back to the smallest size */
+ im->xlab_user.gridtm = xlab[xlab_sel].gridtm;
+ im->xlab_user.gridst = xlab[xlab_sel].gridst;
+ im->xlab_user.mgridtm = xlab[xlab_sel].mgridtm;
+ im->xlab_user.mgridst = xlab[xlab_sel].mgridst;
+ im->xlab_user.labtm = xlab[xlab_sel].labtm;
+ im->xlab_user.labst = xlab[xlab_sel].labst;
+ im->xlab_user.precis = xlab[xlab_sel].precis;
+ im->xlab_user.stst = xlab[xlab_sel].stst;
+ }
+
+ /* y coords are the same for every line ... */
+ Y0 = im->yorigin;
+ Y1 = im->yorigin-im->ysize;
+
+
+ /* paint the minor grid */
+ if (!(im->extra_flags & NOMINOR))
+ {
+ for(ti = find_first_time(im->start,
+ im->xlab_user.gridtm,
+ im->xlab_user.gridst),
+ timajor = find_first_time(im->start,
+ im->xlab_user.mgridtm,
+ im->xlab_user.mgridst);
+ ti < im->end;
+ ti = find_next_time(ti,im->xlab_user.gridtm,im->xlab_user.gridst)
+ ){
+ /* are we inside the graph ? */
+ if (ti < im->start || ti > im->end) continue;
+ while (timajor < ti) {
+ timajor = find_next_time(timajor,
+ im->xlab_user.mgridtm, im->xlab_user.mgridst);
+ }
+ if (ti == timajor) continue; /* skip as falls on major grid line */
+ X0 = xtr(im,ti);
+ gfx_new_dashed_line(im->canvas,X0,Y0+1, X0,Y1-1,GRIDWIDTH,
+ im->graph_col[GRC_GRID],
+ im->grid_dash_on, im->grid_dash_off);
+
+ }
+ }
+
+ /* paint the major grid */
+ for(ti = find_first_time(im->start,
+ im->xlab_user.mgridtm,
+ im->xlab_user.mgridst);
+ ti < im->end;
+ ti = find_next_time(ti,im->xlab_user.mgridtm,im->xlab_user.mgridst)
+ ){
+ /* are we inside the graph ? */
+ if (ti < im->start || ti > im->end) continue;
+ X0 = xtr(im,ti);
+ gfx_new_dashed_line(im->canvas,X0,Y0+3, X0,Y1-2,MGRIDWIDTH,
+ im->graph_col[GRC_MGRID],
+ im->grid_dash_on, im->grid_dash_off);
+
+ }
+ /* paint the labels below the graph */
+ for(ti = find_first_time(im->start - im->xlab_user.precis/2,
+ im->xlab_user.labtm,
+ im->xlab_user.labst);
+ ti <= im->end - im->xlab_user.precis/2;
+ ti = find_next_time(ti,im->xlab_user.labtm,im->xlab_user.labst)
+ ){
+ tilab= ti + im->xlab_user.precis/2; /* correct time for the label */
+ /* are we inside the graph ? */
+ if (tilab < im->start || tilab > im->end) continue;
+
+#if HAVE_STRFTIME
+ localtime_r(&tilab, &tm);
+ strftime(graph_label,99,im->xlab_user.stst, &tm);
+#else
+# error "your libc has no strftime I guess we'll abort the exercise here."
+#endif
+ gfx_new_text ( im->canvas,
+ xtr(im,tilab), Y0+im->text_prop[TEXT_PROP_AXIS].size*1.4+5,
+ im->graph_col[GRC_FONT],
+ im->text_prop[TEXT_PROP_AXIS].font,
+ im->text_prop[TEXT_PROP_AXIS].size,
+ im->tabwidth, 0.0, GFX_H_CENTER, GFX_V_BOTTOM,
+ graph_label );
+
+ }
+
+}
+
+
+void
+axis_paint(
+ image_desc_t *im
+ )
+{
+ /* draw x and y axis */
+ /* gfx_new_line ( im->canvas, im->xorigin+im->xsize,im->yorigin,
+ im->xorigin+im->xsize,im->yorigin-im->ysize,
+ GRIDWIDTH, im->graph_col[GRC_AXIS]);
+
+ gfx_new_line ( im->canvas, im->xorigin,im->yorigin-im->ysize,
+ im->xorigin+im->xsize,im->yorigin-im->ysize,
+ GRIDWIDTH, im->graph_col[GRC_AXIS]); */
+
+ gfx_new_line ( im->canvas, im->xorigin-4,im->yorigin,
+ im->xorigin+im->xsize+4,im->yorigin,
+ MGRIDWIDTH, im->graph_col[GRC_AXIS]);
+
+ gfx_new_line ( im->canvas, im->xorigin,im->yorigin+4,
+ im->xorigin,im->yorigin-im->ysize-4,
+ MGRIDWIDTH, im->graph_col[GRC_AXIS]);
+
+
+ /* arrow for X and Y axis direction */
+ gfx_new_area ( im->canvas,
+ im->xorigin+im->xsize+2, im->yorigin-2,
+ im->xorigin+im->xsize+2, im->yorigin+3,
+ im->xorigin+im->xsize+7, im->yorigin+0.5, /* LINEOFFSET */
+ im->graph_col[GRC_ARROW]);
+
+ gfx_new_area ( im->canvas,
+ im->xorigin-2, im->yorigin-im->ysize-2,
+ im->xorigin+3, im->yorigin-im->ysize-2,
+ im->xorigin+0.5, im->yorigin-im->ysize-7, /* LINEOFFSET */
+ im->graph_col[GRC_ARROW]);
+
+}
+
+void
+grid_paint(image_desc_t *im)
+{
+ long i;
+ int res=0;
+ double X0,Y0; /* points for filled graph and more*/
+ gfx_node_t *node;
+
+ /* draw 3d border */
+ node = gfx_new_area (im->canvas, 0,im->yimg,
+ 2,im->yimg-2,
+ 2,2,im->graph_col[GRC_SHADEA]);
+ gfx_add_point( node , im->ximg - 2, 2 );
+ gfx_add_point( node , im->ximg, 0 );
+ gfx_add_point( node , 0,0 );
+/* gfx_add_point( node , 0,im->yimg ); */
+
+ node = gfx_new_area (im->canvas, 2,im->yimg-2,
+ im->ximg-2,im->yimg-2,
+ im->ximg - 2, 2,
+ im->graph_col[GRC_SHADEB]);
+ gfx_add_point( node , im->ximg,0);
+ gfx_add_point( node , im->ximg,im->yimg);
+ gfx_add_point( node , 0,im->yimg);
+/* gfx_add_point( node , 0,im->yimg ); */
+
+
+ if (im->draw_x_grid == 1 )
+ vertical_grid(im);
+
+ if (im->draw_y_grid == 1){
+ if(im->logarithmic){
+ res = horizontal_log_grid(im);
+ } else {
+ res = draw_horizontal_grid(im);
+ }
+
+ /* dont draw horizontal grid if there is no min and max val */
+ if (! res ) {
+ char *nodata = "No Data found";
+ gfx_new_text(im->canvas,im->ximg/2, (2*im->yorigin-im->ysize) / 2,
+ im->graph_col[GRC_FONT],
+ im->text_prop[TEXT_PROP_AXIS].font,
+ im->text_prop[TEXT_PROP_AXIS].size,
+ im->tabwidth, 0.0, GFX_H_CENTER, GFX_V_CENTER,
+ nodata );
+ }
+ }
+
+ /* yaxis unit description */
+ gfx_new_text( im->canvas,
+ 10, (im->yorigin - im->ysize/2),
+ im->graph_col[GRC_FONT],
+ im->text_prop[TEXT_PROP_UNIT].font,
+ im->text_prop[TEXT_PROP_UNIT].size, im->tabwidth,
+ RRDGRAPH_YLEGEND_ANGLE,
+ GFX_H_LEFT, GFX_V_CENTER,
+ im->ylegend);
+
+ /* graph title */
+ gfx_new_text( im->canvas,
+ im->ximg/2, im->text_prop[TEXT_PROP_TITLE].size*1.3+4,
+ im->graph_col[GRC_FONT],
+ im->text_prop[TEXT_PROP_TITLE].font,
+ im->text_prop[TEXT_PROP_TITLE].size, im->tabwidth, 0.0,
+ GFX_H_CENTER, GFX_V_CENTER,
+ im->title);
+ /* rrdtool 'logo' */
+ gfx_new_text( im->canvas,
+ im->ximg-7, 7,
+ ( im->graph_col[GRC_FONT] & 0xffffff00 ) | 0x00000044,
+ im->text_prop[TEXT_PROP_AXIS].font,
+ 5.5, im->tabwidth, 270,
+ GFX_H_RIGHT, GFX_V_TOP,
+ "RRDTOOL / TOBI OETIKER");
+
+ /* graph watermark */
+ if(im->watermark[0] != '\0') {
+ gfx_new_text( im->canvas,
+ im->ximg/2, im->yimg-6,
+ ( im->graph_col[GRC_FONT] & 0xffffff00 ) | 0x00000044,
+ im->text_prop[TEXT_PROP_AXIS].font,
+ 5.5, im->tabwidth, 0,
+ GFX_H_CENTER, GFX_V_BOTTOM,
+ im->watermark);
+ }
+
+ /* graph labels */
+ if( !(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH) ) {
+ for(i=0;i<im->gdes_c;i++){
+ if(im->gdes[i].legend[0] =='\0')
+ continue;
+
+ /* im->gdes[i].leg_y is the bottom of the legend */
+ X0 = im->gdes[i].leg_x;
+ Y0 = im->gdes[i].leg_y;
+ gfx_new_text ( im->canvas, X0, Y0,
+ im->graph_col[GRC_FONT],
+ im->text_prop[TEXT_PROP_LEGEND].font,
+ im->text_prop[TEXT_PROP_LEGEND].size,
+ im->tabwidth,0.0, GFX_H_LEFT, GFX_V_BOTTOM,
+ im->gdes[i].legend );
+ /* The legend for GRAPH items starts with "M " to have
+ enough space for the box */
+ if ( im->gdes[i].gf != GF_PRINT &&
+ im->gdes[i].gf != GF_GPRINT &&
+ im->gdes[i].gf != GF_COMMENT) {
+ int boxH, boxV;
+
+ boxH = gfx_get_text_width(im->canvas, 0,
+ im->text_prop[TEXT_PROP_LEGEND].font,
+ im->text_prop[TEXT_PROP_LEGEND].size,
+ im->tabwidth,"o", 0) * 1.2;
+ boxV = boxH*1.1;
+
+ /* make sure transparent colors show up the same way as in the graph */
+ node = gfx_new_area(im->canvas,
+ X0,Y0-boxV,
+ X0,Y0,
+ X0+boxH,Y0,
+ im->graph_col[GRC_BACK]);
+ gfx_add_point ( node, X0+boxH, Y0-boxV );
+
+ node = gfx_new_area(im->canvas,
+ X0,Y0-boxV,
+ X0,Y0,
+ X0+boxH,Y0,
+ im->gdes[i].col);
+ gfx_add_point ( node, X0+boxH, Y0-boxV );
+ node = gfx_new_line(im->canvas,
+ X0,Y0-boxV,
+ X0,Y0,
+ 1.0,im->graph_col[GRC_FRAME]);
+ gfx_add_point(node,X0+boxH,Y0);
+ gfx_add_point(node,X0+boxH,Y0-boxV);
+ gfx_close_path(node);
+ }
+ }
+ }
+}
+
+
+/*****************************************************
+ * lazy check make sure we rely need to create this graph
+ *****************************************************/
+
+int lazy_check(image_desc_t *im){
+ FILE *fd = NULL;
+ int size = 1;
+ struct stat imgstat;
+
+ if (im->lazy == 0) return 0; /* no lazy option */
+ if (stat(im->graphfile,&imgstat) != 0)
+ return 0; /* can't stat */
+ /* one pixel in the existing graph is more then what we would
+ change here ... */
+ if (time(NULL) - imgstat.st_mtime >
+ (im->end - im->start) / im->xsize)
+ return 0;
+ if ((fd = fopen(im->graphfile,"rb")) == NULL)
+ return 0; /* the file does not exist */
+ switch (im->canvas->imgformat) {
+ case IF_PNG:
+ size = PngSize(fd,&(im->ximg),&(im->yimg));
+ break;
+ default:
+ size = 1;
+ }
+ fclose(fd);
+ return size;
+}
+
+#ifdef WITH_PIECHART
+void
+pie_part(image_desc_t *im, gfx_color_t color,
+ double PieCenterX, double PieCenterY, double Radius,
+ double startangle, double endangle)
+{
+ gfx_node_t *node;
+ double angle;
+ double step=M_PI/50; /* Number of iterations for the circle;
+ ** 10 is definitely too low, more than
+ ** 50 seems to be overkill
+ */
+
+ /* Strange but true: we have to work clockwise or else
+ ** anti aliasing nor transparency don't work.
+ **
+ ** This test is here to make sure we do it right, also
+ ** this makes the for...next loop more easy to implement.
+ ** The return will occur if the user enters a negative number
+ ** (which shouldn't be done according to the specs) or if the
+ ** programmers do something wrong (which, as we all know, never
+ ** happens anyway :)
+ */
+ if (endangle<startangle) return;
+
+ /* Hidden feature: Radius decreases each full circle */
+ angle=startangle;
+ while (angle>=2*M_PI) {
+ angle -= 2*M_PI;
+ Radius *= 0.8;
+ }
+
+ node=gfx_new_area(im->canvas,
+ PieCenterX+sin(startangle)*Radius,
+ PieCenterY-cos(startangle)*Radius,
+ PieCenterX,
+ PieCenterY,
+ PieCenterX+sin(endangle)*Radius,
+ PieCenterY-cos(endangle)*Radius,
+ color);
+ for (angle=endangle;angle-startangle>=step;angle-=step) {
+ gfx_add_point(node,
+ PieCenterX+sin(angle)*Radius,
+ PieCenterY-cos(angle)*Radius );
+ }
+}
+
+#endif
+
+int
+graph_size_location(image_desc_t *im, int elements
+
+#ifdef WITH_PIECHART
+, int piechart
+#endif
+
+ )
+{
+ /* The actual size of the image to draw is determined from
+ ** several sources. The size given on the command line is
+ ** the graph area but we need more as we have to draw labels
+ ** and other things outside the graph area
+ */
+
+ /* +-+-------------------------------------------+
+ ** |l|.................title.....................|
+ ** |e+--+-------------------------------+--------+
+ ** |b| b| | |
+ ** |a| a| | pie |
+ ** |l| l| main graph area | chart |
+ ** |.| .| | area |
+ ** |t| y| | |
+ ** |r+--+-------------------------------+--------+
+ ** |e| | x-axis labels | |
+ ** |v+--+-------------------------------+--------+
+ ** | |..............legends......................|
+ ** +-+-------------------------------------------+
+ ** | watermark |
+ ** +---------------------------------------------+
+ */
+ int Xvertical=0,
+ Ytitle =0,
+ Xylabel =0,
+ Xmain =0, Ymain =0,
+#ifdef WITH_PIECHART
+ Xpie =0, Ypie =0,
+#endif
+ Yxlabel =0,
+#if 0
+ Xlegend =0, Ylegend =0,
+#endif
+ Xspacing =15, Yspacing =15,
+
+ Ywatermark =4;
+
+ if (im->extra_flags & ONLY_GRAPH) {
+ im->xorigin =0;
+ im->ximg = im->xsize;
+ im->yimg = im->ysize;
+ im->yorigin = im->ysize;
+ ytr(im,DNAN);
+ return 0;
+ }
+
+ if (im->ylegend[0] != '\0' ) {
+ Xvertical = im->text_prop[TEXT_PROP_UNIT].size *2;
+ }
+
+
+ if (im->title[0] != '\0') {
+ /* The title is placed "inbetween" two text lines so it
+ ** automatically has some vertical spacing. The horizontal
+ ** spacing is added here, on each side.
+ */
+ /* don't care for the with of the title
+ Xtitle = gfx_get_text_width(im->canvas, 0,
+ im->text_prop[TEXT_PROP_TITLE].font,
+ im->text_prop[TEXT_PROP_TITLE].size,
+ im->tabwidth,
+ im->title, 0) + 2*Xspacing; */
+ Ytitle = im->text_prop[TEXT_PROP_TITLE].size*2.6+10;
+ }
+
+ if (elements) {
+ Xmain=im->xsize;
+ Ymain=im->ysize;
+ if (im->draw_x_grid) {
+ Yxlabel=im->text_prop[TEXT_PROP_AXIS].size *2.5;
+ }
+ if (im->draw_y_grid || im->forceleftspace ) {
+ Xylabel=gfx_get_text_width(im->canvas, 0,
+ im->text_prop[TEXT_PROP_AXIS].font,
+ im->text_prop[TEXT_PROP_AXIS].size,
+ im->tabwidth,
+ "0", 0) * im->unitslength;
+ }
+ }
+
+#ifdef WITH_PIECHART
+ if (piechart) {
+ im->piesize=im->xsize<im->ysize?im->xsize:im->ysize;
+ Xpie=im->piesize;
+ Ypie=im->piesize;
+ }
+#endif
+
+ /* Now calculate the total size. Insert some spacing where
+ desired. im->xorigin and im->yorigin need to correspond
+ with the lower left corner of the main graph area or, if
+ this one is not set, the imaginary box surrounding the
+ pie chart area. */
+
+ /* The legend width cannot yet be determined, as a result we
+ ** have problems adjusting the image to it. For now, we just
+ ** forget about it at all; the legend will have to fit in the
+ ** size already allocated.
+ */
+ im->ximg = Xylabel + Xmain + 2 * Xspacing;
+
+#ifdef WITH_PIECHART
+ im->ximg += Xpie;
+#endif
+
+ if (Xmain) im->ximg += Xspacing;
+#ifdef WITH_PIECHART
+ if (Xpie) im->ximg += Xspacing;
+#endif
+
+ im->xorigin = Xspacing + Xylabel;
+
+ /* the length of the title should not influence with width of the graph
+ if (Xtitle > im->ximg) im->ximg = Xtitle; */
+
+ if (Xvertical) { /* unit description */
+ im->ximg += Xvertical;
+ im->xorigin += Xvertical;
+ }
+ xtr(im,0);
+
+ /* The vertical size is interesting... we need to compare
+ ** the sum of {Ytitle, Ymain, Yxlabel, Ylegend, Ywatermark} with
+ ** Yvertical however we need to know {Ytitle+Ymain+Yxlabel}
+ ** in order to start even thinking about Ylegend or Ywatermark.
+ **
+ ** Do it in three portions: First calculate the inner part,
+ ** then do the legend, then adjust the total height of the img,
+ ** adding space for a watermark if one exists;
+ */
+
+ /* reserve space for main and/or pie */
+
+ im->yimg = Ymain + Yxlabel;
+
+#ifdef WITH_PIECHART
+ if (im->yimg < Ypie) im->yimg = Ypie;
+#endif
+
+ im->yorigin = im->yimg - Yxlabel;
+
+ /* reserve space for the title *or* some padding above the graph */
+ if (Ytitle) {
+ im->yimg += Ytitle;
+ im->yorigin += Ytitle;
+ } else {
+ im->yimg += 1.5*Yspacing;
+ im->yorigin += 1.5*Yspacing;
+ }
+ /* reserve space for padding below the graph */
+ im->yimg += Yspacing;
+
+ /* Determine where to place the legends onto the image.
+ ** Adjust im->yimg to match the space requirements.
+ */
+ if(leg_place(im)==-1)
+ return -1;
+
+ if (im->watermark[0] != '\0') {
+ im->yimg += Ywatermark;
+ }
+
+#if 0
+ if (Xlegend > im->ximg) {
+ im->ximg = Xlegend;
+ /* reposition Pie */
+ }
+#endif
+
+#ifdef WITH_PIECHART
+ /* The pie is placed in the upper right hand corner,
+ ** just below the title (if any) and with sufficient
+ ** padding.
+ */
+ if (elements) {
+ im->pie_x = im->ximg - Xspacing - Xpie/2;
+ im->pie_y = im->yorigin-Ymain+Ypie/2;
+ } else {
+ im->pie_x = im->ximg/2;
+ im->pie_y = im->yorigin-Ypie/2;
+ }
+#endif
+
+ ytr(im,DNAN);
+ return 0;
+}
+
+/* from http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm */
+/* yes we are loosing precision by doing tos with floats instead of doubles
+ but it seems more stable this way. */
+
+
+/* draw that picture thing ... */
+int
+graph_paint(image_desc_t *im, char ***calcpr)
+{
+ int i,ii;
+ int lazy = lazy_check(im);
+#ifdef WITH_PIECHART
+ int piechart = 0;
+ double PieStart=0.0;
+#endif
+ FILE *fo;
+ gfx_node_t *node;
+
+ double areazero = 0.0;
+ graph_desc_t *lastgdes = NULL;
+
+ /* if we are lazy and there is nothing to PRINT ... quit now */
+ if (lazy && im->prt_c==0) return 0;
+
+ /* pull the data from the rrd files ... */
+
+ if(data_fetch(im)==-1)
+ return -1;
+
+ /* evaluate VDEF and CDEF operations ... */
+ if(data_calc(im)==-1)
+ return -1;
+
+#ifdef WITH_PIECHART
+ /* check if we need to draw a piechart */
+ for(i=0;i<im->gdes_c;i++){
+ if (im->gdes[i].gf == GF_PART) {
+ piechart=1;
+ break;
+ }
+ }
+#endif
+
+ /* calculate and PRINT and GPRINT definitions. We have to do it at
+ * this point because it will affect the length of the legends
+ * if there are no graph elements we stop here ...
+ * if we are lazy, try to quit ...
+ */
+ i=print_calc(im,calcpr);
+ if(i<0) return -1;
+ if(((i==0)
+#ifdef WITH_PIECHART
+&&(piechart==0)
+#endif
+) || lazy) return 0;
+
+#ifdef WITH_PIECHART
+ /* If there's only the pie chart to draw, signal this */
+ if (i==0) piechart=2;
+#endif
+
+ /* get actual drawing data and find min and max values*/
+ if(data_proc(im)==-1)
+ return -1;
+
+ if(!im->logarithmic){si_unit(im);} /* identify si magnitude Kilo, Mega Giga ? */
+
+ if(!im->rigid && ! im->logarithmic)
+ expand_range(im); /* make sure the upper and lower limit are
+ sensible values */
+
+ if (!calc_horizontal_grid(im))
+ return -1;
+
+ if (im->gridfit)
+ apply_gridfit(im);
+
+
+/**************************************************************
+ *** Calculating sizes and locations became a bit confusing ***
+ *** so I moved this into a separate function. ***
+ **************************************************************/
+ if(graph_size_location(im,i
+#ifdef WITH_PIECHART
+,piechart
+#endif
+)==-1)
+ return -1;
+
+ /* the actual graph is created by going through the individual
+ graph elements and then drawing them */
+
+ node=gfx_new_area ( im->canvas,
+ 0, 0,
+ 0, im->yimg,
+ im->ximg, im->yimg,
+ im->graph_col[GRC_BACK]);
+
+ gfx_add_point(node,im->ximg, 0);
+
+#ifdef WITH_PIECHART
+ if (piechart != 2) {
+#endif
+ node=gfx_new_area ( im->canvas,
+ im->xorigin, im->yorigin,
+ im->xorigin + im->xsize, im->yorigin,
+ im->xorigin + im->xsize, im->yorigin-im->ysize,
+ im->graph_col[GRC_CANVAS]);
+
+ gfx_add_point(node,im->xorigin, im->yorigin - im->ysize);
+
+ if (im->minval > 0.0)
+ areazero = im->minval;
+ if (im->maxval < 0.0)
+ areazero = im->maxval;
+#ifdef WITH_PIECHART
+ }
+#endif
+
+#ifdef WITH_PIECHART
+ if (piechart) {
+ pie_part(im,im->graph_col[GRC_CANVAS],im->pie_x,im->pie_y,im->piesize*0.5,0,2*M_PI);
+ }
+#endif
+
+ for(i=0;i<im->gdes_c;i++){
+ switch(im->gdes[i].gf){
+ case GF_CDEF:
+ case GF_VDEF:
+ case GF_DEF:
+ case GF_PRINT:
+ case GF_GPRINT:
+ case GF_COMMENT:
+ case GF_HRULE:
+ case GF_VRULE:
+ case GF_XPORT:
+ case GF_SHIFT:
+ break;
+ case GF_TICK:
+ for (ii = 0; ii < im->xsize; ii++)
+ {
+ if (!isnan(im->gdes[i].p_data[ii]) &&
+ im->gdes[i].p_data[ii] != 0.0)
+ {
+ if (im -> gdes[i].yrule > 0 ) {
+ gfx_new_line(im->canvas,
+ im -> xorigin + ii, im->yorigin,
+ im -> xorigin + ii, im->yorigin - im -> gdes[i].yrule * im -> ysize,
+ 1.0,
+ im -> gdes[i].col );
+ } else if ( im -> gdes[i].yrule < 0 ) {
+ gfx_new_line(im->canvas,
+ im -> xorigin + ii, im->yorigin - im -> ysize,
+ im -> xorigin + ii, im->yorigin - ( 1 - im -> gdes[i].yrule ) * im -> ysize,
+ 1.0,
+ im -> gdes[i].col );
+
+ }
+ }
+ }
+ break;
+ case GF_LINE:
+ case GF_AREA:
+ /* fix data points at oo and -oo */
+ for(ii=0;ii<im->xsize;ii++){
+ if (isinf(im->gdes[i].p_data[ii])){
+ if (im->gdes[i].p_data[ii] > 0) {
+ im->gdes[i].p_data[ii] = im->maxval ;
+ } else {
+ im->gdes[i].p_data[ii] = im->minval ;
+ }
+
+ }
+ } /* for */
+
+ /* *******************************************************
+ a ___. (a,t)
+ | | ___
+ ____| | | |
+ | |___|
+ -------|--t-1--t--------------------------------
+
+ if we know the value at time t was a then
+ we draw a square from t-1 to t with the value a.
+
+ ********************************************************* */
+ if (im->gdes[i].col != 0x0){
+ /* GF_LINE and friend */
+ if(im->gdes[i].gf == GF_LINE ){
+ double last_y=0.0;
+ node = NULL;
+ for(ii=1;ii<im->xsize;ii++){
+ if (isnan(im->gdes[i].p_data[ii]) || (im->slopemode==1 && isnan(im->gdes[i].p_data[ii-1]))){
+ node = NULL;
+ continue;
+ }
+ if ( node == NULL ) {
+ last_y = ytr(im,im->gdes[i].p_data[ii]);
+ if ( im->slopemode == 0 ){
+ node = gfx_new_line(im->canvas,
+ ii-1+im->xorigin,last_y,
+ ii+im->xorigin,last_y,
+ im->gdes[i].linewidth,
+ im->gdes[i].col);
+ } else {
+ node = gfx_new_line(im->canvas,
+ ii-1+im->xorigin,ytr(im,im->gdes[i].p_data[ii-1]),
+ ii+im->xorigin,last_y,
+ im->gdes[i].linewidth,
+ im->gdes[i].col);
+ }
+ } else {
+ double new_y = ytr(im,im->gdes[i].p_data[ii]);
+ if ( im->slopemode==0 && ! AlmostEqual2sComplement(new_y,last_y,4)){
+ gfx_add_point(node,ii-1+im->xorigin,new_y);
+ };
+ last_y = new_y;
+ gfx_add_point(node,ii+im->xorigin,new_y);
+ };
+
+ }
+ } else {
+ int idxI=-1;
+ double *foreY=malloc(sizeof(double)*im->xsize*2);
+ double *foreX=malloc(sizeof(double)*im->xsize*2);
+ double *backY=malloc(sizeof(double)*im->xsize*2);
+ double *backX=malloc(sizeof(double)*im->xsize*2);
+ int drawem = 0;
+ for(ii=0;ii<=im->xsize;ii++){
+ double ybase,ytop;
+ if ( idxI > 0 && ( drawem != 0 || ii==im->xsize)){
+ int cntI=1;
+ int lastI=0;
+ while (cntI < idxI && AlmostEqual2sComplement(foreY[lastI],foreY[cntI],4) && AlmostEqual2sComplement(foreY[lastI],foreY[cntI+1],4)){cntI++;}
+ node = gfx_new_area(im->canvas,
+ backX[0],backY[0],
+ foreX[0],foreY[0],
+ foreX[cntI],foreY[cntI], im->gdes[i].col);
+ while (cntI < idxI) {
+ lastI = cntI;
+ cntI++;
+ while ( cntI < idxI && AlmostEqual2sComplement(foreY[lastI],foreY[cntI],4) && AlmostEqual2sComplement(foreY[lastI],foreY[cntI+1],4)){cntI++;}
+ gfx_add_point(node,foreX[cntI],foreY[cntI]);
+ }
+ gfx_add_point(node,backX[idxI],backY[idxI]);
+ while (idxI > 1){
+ lastI = idxI;
+ idxI--;
+ while ( idxI > 1 && AlmostEqual2sComplement(backY[lastI], backY[idxI],4) && AlmostEqual2sComplement(backY[lastI],backY[idxI-1],4)){idxI--;}
+ gfx_add_point(node,backX[idxI],backY[idxI]);
+ }
+ idxI=-1;
+ drawem = 0;
+ }
+ if (drawem != 0){
+ drawem = 0;
+ idxI=-1;
+ }
+ if (ii == im->xsize) break;
+
+ /* keep things simple for now, just draw these bars
+ do not try to build a big and complex area */
+
+
+ if ( im->slopemode == 0 && ii==0){
+ continue;
+ }
+ if ( isnan(im->gdes[i].p_data[ii]) ) {
+ drawem = 1;
+ continue;
+ }
+ ytop = ytr(im,im->gdes[i].p_data[ii]);
+ if ( lastgdes && im->gdes[i].stack ) {
+ ybase = ytr(im,lastgdes->p_data[ii]);
+ } else {
+ ybase = ytr(im,areazero);
+ }
+ if ( ybase == ytop ){
+ drawem = 1;
+ continue;
+ }
+ /* every area has to be wound clock-wise,
+ so we have to make sur base remains base */
+ if (ybase > ytop){
+ double extra = ytop;
+ ytop = ybase;
+ ybase = extra;
+ }
+ if ( im->slopemode == 0 ){
+ backY[++idxI] = ybase-0.2;
+ backX[idxI] = ii+im->xorigin-1;
+ foreY[idxI] = ytop+0.2;
+ foreX[idxI] = ii+im->xorigin-1;
+ }
+ backY[++idxI] = ybase-0.2;
+ backX[idxI] = ii+im->xorigin;
+ foreY[idxI] = ytop+0.2;
+ foreX[idxI] = ii+im->xorigin;
+ }
+ /* close up any remaining area */
+ free(foreY);
+ free(foreX);
+ free(backY);
+ free(backX);
+ } /* else GF_LINE */
+ } /* if color != 0x0 */
+ /* make sure we do not run into trouble when stacking on NaN */
+ for(ii=0;ii<im->xsize;ii++){
+ if (isnan(im->gdes[i].p_data[ii])) {
+ if (lastgdes && (im->gdes[i].stack)) {
+ im->gdes[i].p_data[ii] = lastgdes->p_data[ii];
+ } else {
+ im->gdes[i].p_data[ii] = areazero;
+ }
+ }
+ }
+ lastgdes = &(im->gdes[i]);
+ break;
+#ifdef WITH_PIECHART
+ case GF_PART:
+ if(isnan(im->gdes[i].yrule)) /* fetch variable */
+ im->gdes[i].yrule = im->gdes[im->gdes[i].vidx].vf.val;
+
+ if (finite(im->gdes[i].yrule)) { /* even the fetched var can be NaN */
+ pie_part(im,im->gdes[i].col,
+ im->pie_x,im->pie_y,im->piesize*0.4,
+ M_PI*2.0*PieStart/100.0,
+ M_PI*2.0*(PieStart+im->gdes[i].yrule)/100.0);
+ PieStart += im->gdes[i].yrule;
+ }
+ break;
+#endif
+ case GF_STACK:
+ rrd_set_error("STACK should already be turned into LINE or AREA here");
+ return -1;
+ break;
+
+ } /* switch */
+ }
+#ifdef WITH_PIECHART
+ if (piechart==2) {
+ im->draw_x_grid=0;
+ im->draw_y_grid=0;
+ }
+#endif
+
+
+ /* grid_paint also does the text */
+ if( !(im->extra_flags & ONLY_GRAPH) )
+ grid_paint(im);
+
+
+ if( !(im->extra_flags & ONLY_GRAPH) )
+ axis_paint(im);
+
+ /* the RULES are the last thing to paint ... */
+ for(i=0;i<im->gdes_c;i++){
+
+ switch(im->gdes[i].gf){
+ case GF_HRULE:
+ if(im->gdes[i].yrule >= im->minval
+ && im->gdes[i].yrule <= im->maxval)
+ gfx_new_line(im->canvas,
+ im->xorigin,ytr(im,im->gdes[i].yrule),
+ im->xorigin+im->xsize,ytr(im,im->gdes[i].yrule),
+ 1.0,im->gdes[i].col);
+ break;
+ case GF_VRULE:
+ if(im->gdes[i].xrule >= im->start
+ && im->gdes[i].xrule <= im->end)
+ gfx_new_line(im->canvas,
+ xtr(im,im->gdes[i].xrule),im->yorigin,
+ xtr(im,im->gdes[i].xrule),im->yorigin-im->ysize,
+ 1.0,im->gdes[i].col);
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ if (strcmp(im->graphfile,"-")==0) {
+ fo = im->graphhandle ? im->graphhandle : stdout;
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+ /* Change translation mode for stdout to BINARY */
+ _setmode( _fileno( fo ), O_BINARY );
+#endif
+ } else {
+ if ((fo = fopen(im->graphfile,"wb")) == NULL) {
+ rrd_set_error("Opening '%s' for write: %s",im->graphfile,
+ rrd_strerror(errno));
+ return (-1);
+ }
+ }
+ gfx_render (im->canvas,im->ximg,im->yimg,0x00000000,fo);
+ if (strcmp(im->graphfile,"-") != 0)
+ fclose(fo);
+ return 0;
+}
+
+
+/*****************************************************
+ * graph stuff
+ *****************************************************/
+
+int
+gdes_alloc(image_desc_t *im){
+
+ im->gdes_c++;
+ if ((im->gdes = (graph_desc_t *) rrd_realloc(im->gdes, (im->gdes_c)
+ * sizeof(graph_desc_t)))==NULL){
+ rrd_set_error("realloc graph_descs");
+ return -1;
+ }
+
+
+ im->gdes[im->gdes_c-1].step=im->step;
+ im->gdes[im->gdes_c-1].step_orig=im->step;
+ im->gdes[im->gdes_c-1].stack=0;
+ im->gdes[im->gdes_c-1].linewidth=0;
+ im->gdes[im->gdes_c-1].debug=0;
+ im->gdes[im->gdes_c-1].start=im->start;
+ im->gdes[im->gdes_c-1].start_orig=im->start;
+ im->gdes[im->gdes_c-1].end=im->end;
+ im->gdes[im->gdes_c-1].end_orig=im->end;
+ im->gdes[im->gdes_c-1].vname[0]='\0';
+ im->gdes[im->gdes_c-1].data=NULL;
+ im->gdes[im->gdes_c-1].ds_namv=NULL;
+ im->gdes[im->gdes_c-1].data_first=0;
+ im->gdes[im->gdes_c-1].p_data=NULL;
+ im->gdes[im->gdes_c-1].rpnp=NULL;
+ im->gdes[im->gdes_c-1].shift=0;
+ im->gdes[im->gdes_c-1].col = 0x0;
+ im->gdes[im->gdes_c-1].legend[0]='\0';
+ im->gdes[im->gdes_c-1].format[0]='\0';
+ im->gdes[im->gdes_c-1].strftm=0;
+ im->gdes[im->gdes_c-1].rrd[0]='\0';
+ im->gdes[im->gdes_c-1].ds=-1;
+ im->gdes[im->gdes_c-1].cf_reduce=CF_AVERAGE;
+ im->gdes[im->gdes_c-1].cf=CF_AVERAGE;
+ im->gdes[im->gdes_c-1].p_data=NULL;
+ im->gdes[im->gdes_c-1].yrule=DNAN;
+ im->gdes[im->gdes_c-1].xrule=0;
+ return 0;
+}
+
+/* copies input untill the first unescaped colon is found
+ or until input ends. backslashes have to be escaped as well */
+int
+scan_for_col(const char *const input, int len, char *const output)
+{
+ int inp,outp=0;
+ for (inp=0;
+ inp < len &&
+ input[inp] != ':' &&
+ input[inp] != '\0';
+ inp++){
+ if (input[inp] == '\\' &&
+ input[inp+1] != '\0' &&
+ (input[inp+1] == '\\' ||
+ input[inp+1] == ':')){
+ output[outp++] = input[++inp];
+ }
+ else {
+ output[outp++] = input[inp];
+ }
+ }
+ output[outp] = '\0';
+ return inp;
+}
+/* Some surgery done on this function, it became ridiculously big.
+** Things moved:
+** - initializing now in rrd_graph_init()
+** - options parsing now in rrd_graph_options()
+** - script parsing now in rrd_graph_script()
+*/
+int
+rrd_graph(int argc, char **argv, char ***prdata, int *xsize, int *ysize, FILE *stream, double *ymin, double *ymax)
+{
+ image_desc_t im;
+ rrd_graph_init(&im);
+ im.graphhandle = stream;
+
+ rrd_graph_options(argc,argv,&im);
+ if (rrd_test_error()) {
+ im_free(&im);
+ return -1;
+ }
+
+ if (strlen(argv[optind])>=MAXPATH) {
+ rrd_set_error("filename (including path) too long");
+ im_free(&im);
+ return -1;
+ }
+ strncpy(im.graphfile,argv[optind],MAXPATH-1);
+ im.graphfile[MAXPATH-1]='\0';
+
+ rrd_graph_script(argc,argv,&im,1);
+ if (rrd_test_error()) {
+ im_free(&im);
+ return -1;
+ }
+
+ /* Everything is now read and the actual work can start */
+
+ (*prdata)=NULL;
+ if (graph_paint(&im,prdata)==-1){
+ im_free(&im);
+ return -1;
+ }
+
+ /* The image is generated and needs to be output.
+ ** Also, if needed, print a line with information about the image.
+ */
+
+ *xsize=im.ximg;
+ *ysize=im.yimg;
+ *ymin=im.minval;
+ *ymax=im.maxval;
+ if (im.imginfo) {
+ char *filename;
+ if (!(*prdata)) {
+ /* maybe prdata is not allocated yet ... lets do it now */
+ if ((*prdata = calloc(2,sizeof(char *)))==NULL) {
+ rrd_set_error("malloc imginfo");
+ return -1;
+ };
+ }
+ if(((*prdata)[0] = malloc((strlen(im.imginfo)+200+strlen(im.graphfile))*sizeof(char)))
+ ==NULL){
+ rrd_set_error("malloc imginfo");
+ return -1;
+ }
+ filename=im.graphfile+strlen(im.graphfile);
+ while(filename > im.graphfile) {
+ if (*(filename-1)=='/' || *(filename-1)=='\\' ) break;
+ filename--;
+ }
+
+ sprintf((*prdata)[0],im.imginfo,filename,(long)(im.canvas->zoom*im.ximg),(long)(im.canvas->zoom*im.yimg));
+ }
+ im_free(&im);
+ return 0;
+}
+
+void
+rrd_graph_init(image_desc_t *im)
+{
+ unsigned int i;
+
+#ifdef HAVE_TZSET
+ tzset();
+#endif
+#ifdef HAVE_SETLOCALE
+ setlocale(LC_TIME,"");
+#ifdef HAVE_MBSTOWCS
+ setlocale(LC_CTYPE,"");
+#endif
+#endif
+ im->yorigin=0;
+ im->xorigin=0;
+ im->minval=0;
+ im->xlab_user.minsec = -1;
+ im->ximg=0;
+ im->yimg=0;
+ im->xsize = 400;
+ im->ysize = 100;
+ im->step = 0;
+ im->ylegend[0] = '\0';
+ im->title[0] = '\0';
+ im->watermark[0] = '\0';
+ im->minval = DNAN;
+ im->maxval = DNAN;
+ im->unitsexponent= 9999;
+ im->unitslength= 6;
+ im->forceleftspace = 0;
+ im->symbol = ' ';
+ im->viewfactor = 1.0;
+ im->extra_flags= 0;
+ im->rigid = 0;
+ im->gridfit = 1;
+ im->imginfo = NULL;
+ im->lazy = 0;
+ im->slopemode = 0;
+ im->logarithmic = 0;
+ im->ygridstep = DNAN;
+ im->draw_x_grid = 1;
+ im->draw_y_grid = 1;
+ im->base = 1000;
+ im->prt_c = 0;
+ im->gdes_c = 0;
+ im->gdes = NULL;
+ im->canvas = gfx_new_canvas();
+ im->grid_dash_on = 1;
+ im->grid_dash_off = 1;
+ im->tabwidth = 40.0;
+
+ for(i=0;i<DIM(graph_col);i++)
+ im->graph_col[i]=graph_col[i];
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+ {
+ char *windir;
+ char rrd_win_default_font[1000];
+ windir = getenv("windir");
+ /* %windir% is something like D:\windows or C:\winnt */
+ if (windir != NULL) {
+ strncpy(rrd_win_default_font,windir,500);
+ rrd_win_default_font[500] = '\0';
+ strcat(rrd_win_default_font,"\\fonts\\");
+ strcat(rrd_win_default_font,RRD_DEFAULT_FONT);
+ for(i=0;i<DIM(text_prop);i++){
+ strncpy(text_prop[i].font,rrd_win_default_font,sizeof(text_prop[i].font)-1);
+ text_prop[i].font[sizeof(text_prop[i].font)-1] = '\0';
+ }
+ }
+ }
+#endif
+ {
+ char *deffont;
+ deffont = getenv("RRD_DEFAULT_FONT");
+ if (deffont != NULL) {
+ for(i=0;i<DIM(text_prop);i++){
+ strncpy(text_prop[i].font,deffont,sizeof(text_prop[i].font)-1);
+ text_prop[i].font[sizeof(text_prop[i].font)-1] = '\0';
+ }
+ }
+ }
+ for(i=0;i<DIM(text_prop);i++){
+ im->text_prop[i].size = text_prop[i].size;
+ strcpy(im->text_prop[i].font,text_prop[i].font);
+ }
+}
+
+void
+rrd_graph_options(int argc, char *argv[],image_desc_t *im)
+{
+ int stroff;
+ char *parsetime_error = NULL;
+ char scan_gtm[12],scan_mtm[12],scan_ltm[12],col_nam[12];
+ time_t start_tmp=0,end_tmp=0;
+ long long_tmp;
+ struct rrd_time_value start_tv, end_tv;
+ gfx_color_t color;
+ optind = 0; opterr = 0; /* initialize getopt */
+
+ parsetime("end-24h", &start_tv);
+ parsetime("now", &end_tv);
+
+ /* defines for long options without a short equivalent. should be bytes,
+ and may not collide with (the ASCII value of) short options */
+ #define LONGOPT_UNITS_SI 255
+
+ while (1){
+ static struct option long_options[] =
+ {
+ {"start", required_argument, 0, 's'},
+ {"end", required_argument, 0, 'e'},
+ {"x-grid", required_argument, 0, 'x'},
+ {"y-grid", required_argument, 0, 'y'},
+ {"vertical-label",required_argument,0,'v'},
+ {"width", required_argument, 0, 'w'},
+ {"height", required_argument, 0, 'h'},
+ {"interlaced", no_argument, 0, 'i'},
+ {"upper-limit",required_argument, 0, 'u'},
+ {"lower-limit",required_argument, 0, 'l'},
+ {"rigid", no_argument, 0, 'r'},
+ {"base", required_argument, 0, 'b'},
+ {"logarithmic",no_argument, 0, 'o'},
+ {"color", required_argument, 0, 'c'},
+ {"font", required_argument, 0, 'n'},
+ {"title", required_argument, 0, 't'},
+ {"imginfo", required_argument, 0, 'f'},
+ {"imgformat", required_argument, 0, 'a'},
+ {"lazy", no_argument, 0, 'z'},
+ {"zoom", required_argument, 0, 'm'},
+ {"no-legend", no_argument, 0, 'g'},
+ {"force-rules-legend",no_argument,0, 'F'},
+ {"only-graph", no_argument, 0, 'j'},
+ {"alt-y-grid", no_argument, 0, 'Y'},
+ {"no-minor", no_argument, 0, 'I'},
+ {"slope-mode", no_argument, 0, 'E'},
+ {"alt-autoscale", no_argument, 0, 'A'},
+ {"alt-autoscale-min", no_argument, 0, 'J'},
+ {"alt-autoscale-max", no_argument, 0, 'M'},
+ {"no-gridfit", no_argument, 0, 'N'},
+ {"units-exponent",required_argument, 0, 'X'},
+ {"units-length",required_argument, 0, 'L'},
+ {"units", required_argument, 0, LONGOPT_UNITS_SI },
+ {"step", required_argument, 0, 'S'},
+ {"tabwidth", required_argument, 0, 'T'},
+ {"font-render-mode", required_argument, 0, 'R'},
+ {"font-smoothing-threshold", required_argument, 0, 'B'},
+ {"watermark", required_argument, 0, 'W'},
+ {"alt-y-mrtg", no_argument, 0, 1000}, /* this has no effect it is just here to save old apps from crashing when they use it */
+ {0,0,0,0}};
+ int option_index = 0;
+ int opt;
+ int col_start,col_end;
+
+ opt = getopt_long(argc, argv,
+ "s:e:x:y:v:w:h:iu:l:rb:oc:n:m:t:f:a:I:zgjFYAMEX:L:S:T:NR:B:W:",
+ long_options, &option_index);
+
+ if (opt == EOF)
+ break;
+
+ switch(opt) {
+ case 'I':
+ im->extra_flags |= NOMINOR;
+ break;
+ case 'Y':
+ im->extra_flags |= ALTYGRID;
+ break;
+ case 'A':
+ im->extra_flags |= ALTAUTOSCALE;
+ break;
+ case 'J':
+ im->extra_flags |= ALTAUTOSCALE_MIN;
+ break;
+ case 'M':
+ im->extra_flags |= ALTAUTOSCALE_MAX;
+ break;
+ case 'j':
+ im->extra_flags |= ONLY_GRAPH;
+ break;
+ case 'g':
+ im->extra_flags |= NOLEGEND;
+ break;
+ case 'F':
+ im->extra_flags |= FORCE_RULES_LEGEND;
+ break;
+ case LONGOPT_UNITS_SI:
+ if(im->extra_flags & FORCE_UNITS) {
+ rrd_set_error("--units can only be used once!");
+ return;
+ }
+ if(strcmp(optarg,"si")==0)
+ im->extra_flags |= FORCE_UNITS_SI;
+ else {
+ rrd_set_error("invalid argument for --units: %s", optarg );
+ return;
+ }
+ break;
+ case 'X':
+ im->unitsexponent = atoi(optarg);
+ break;
+ case 'L':
+ im->unitslength = atoi(optarg);
+ im->forceleftspace = 1;
+ break;
+ case 'T':
+ im->tabwidth = atof(optarg);
+ break;
+ case 'S':
+ im->step = atoi(optarg);
+ break;
+ case 'N':
+ im->gridfit = 0;
+ break;
+ case 's':
+ if ((parsetime_error = parsetime(optarg, &start_tv))) {
+ rrd_set_error( "start time: %s", parsetime_error );
+ return;
+ }
+ break;
+ case 'e':
+ if ((parsetime_error = parsetime(optarg, &end_tv))) {
+ rrd_set_error( "end time: %s", parsetime_error );
+ return;
+ }
+ break;
+ case 'x':
+ if(strcmp(optarg,"none") == 0){
+ im->draw_x_grid=0;
+ break;
+ };
+
+ if(sscanf(optarg,
+ "%10[A-Z]:%ld:%10[A-Z]:%ld:%10[A-Z]:%ld:%ld:%n",
+ scan_gtm,
+ &im->xlab_user.gridst,
+ scan_mtm,
+ &im->xlab_user.mgridst,
+ scan_ltm,
+ &im->xlab_user.labst,
+ &im->xlab_user.precis,
+ &stroff) == 7 && stroff != 0){
+ strncpy(im->xlab_form, optarg+stroff, sizeof(im->xlab_form) - 1);
+ im->xlab_form[sizeof(im->xlab_form)-1] = '\0';
+ if((int)(im->xlab_user.gridtm = tmt_conv(scan_gtm)) == -1){
+ rrd_set_error("unknown keyword %s",scan_gtm);
+ return;
+ } else if ((int)(im->xlab_user.mgridtm = tmt_conv(scan_mtm)) == -1){
+ rrd_set_error("unknown keyword %s",scan_mtm);
+ return;
+ } else if ((int)(im->xlab_user.labtm = tmt_conv(scan_ltm)) == -1){
+ rrd_set_error("unknown keyword %s",scan_ltm);
+ return;
+ }
+ im->xlab_user.minsec = 1;
+ im->xlab_user.stst = im->xlab_form;
+ } else {
+ rrd_set_error("invalid x-grid format");
+ return;
+ }
+ break;
+ case 'y':
+
+ if(strcmp(optarg,"none") == 0){
+ im->draw_y_grid=0;
+ break;
+ };
+
+ if(sscanf(optarg,
+ "%lf:%d",
+ &im->ygridstep,
+ &im->ylabfact) == 2) {
+ if(im->ygridstep<=0){
+ rrd_set_error("grid step must be > 0");
+ return;
+ } else if (im->ylabfact < 1){
+ rrd_set_error("label factor must be > 0");
+ return;
+ }
+ } else {
+ rrd_set_error("invalid y-grid format");
+ return;
+ }
+ break;
+ case 'v':
+ strncpy(im->ylegend,optarg,150);
+ im->ylegend[150]='\0';
+ break;
+ case 'u':
+ im->maxval = atof(optarg);
+ break;
+ case 'l':
+ im->minval = atof(optarg);
+ break;
+ case 'b':
+ im->base = atol(optarg);
+ if(im->base != 1024 && im->base != 1000 ){
+ rrd_set_error("the only sensible value for base apart from 1000 is 1024");
+ return;
+ }
+ break;
+ case 'w':
+ long_tmp = atol(optarg);
+ if (long_tmp < 10) {
+ rrd_set_error("width below 10 pixels");
+ return;
+ }
+ im->xsize = long_tmp;
+ break;
+ case 'h':
+ long_tmp = atol(optarg);
+ if (long_tmp < 10) {
+ rrd_set_error("height below 10 pixels");
+ return;
+ }
+ im->ysize = long_tmp;
+ break;
+ case 'i':
+ im->canvas->interlaced = 1;
+ break;
+ case 'r':
+ im->rigid = 1;
+ break;
+ case 'f':
+ im->imginfo = optarg;
+ break;
+ case 'a':
+ if((int)(im->canvas->imgformat = if_conv(optarg)) == -1) {
+ rrd_set_error("unsupported graphics format '%s'",optarg);
+ return;
+ }
+ break;
+ case 'z':
+ im->lazy = 1;
+ break;
+ case 'E':
+ im->slopemode = 1;
+ break;
+
+ case 'o':
+ im->logarithmic = 1;
+ break;
+ case 'c':
+ if(sscanf(optarg,
+ "%10[A-Z]#%n%8lx%n",
+ col_nam,&col_start,&color,&col_end) == 2){
+ int ci;
+ int col_len = col_end - col_start;
+ switch (col_len){
+ case 3:
+ color = (
+ ((color & 0xF00) * 0x110000) |
+ ((color & 0x0F0) * 0x011000) |
+ ((color & 0x00F) * 0x001100) |
+ 0x000000FF
+ );
+ break;
+ case 4:
+ color = (
+ ((color & 0xF000) * 0x11000) |
+ ((color & 0x0F00) * 0x01100) |
+ ((color & 0x00F0) * 0x00110) |
+ ((color & 0x000F) * 0x00011)
+ );
+ break;
+ case 6:
+ color = (color << 8) + 0xff /* shift left by 8 */;
+ break;
+ case 8:
+ break;
+ default:
+ rrd_set_error("the color format is #RRGGBB[AA]");
+ return;
+ }
+ if((ci=grc_conv(col_nam)) != -1){
+ im->graph_col[ci]=color;
+ } else {
+ rrd_set_error("invalid color name '%s'",col_nam);
+ return;
+ }
+ } else {
+ rrd_set_error("invalid color def format");
+ return;
+ }
+ break;
+ case 'n':{
+ char prop[15];
+ double size = 1;
+ char font[1024] = "";
+
+ if(sscanf(optarg,
+ "%10[A-Z]:%lf:%1000s",
+ prop,&size,font) >= 2){
+ int sindex,propidx;
+ if((sindex=text_prop_conv(prop)) != -1){
+ for (propidx=sindex;propidx<TEXT_PROP_LAST;propidx++){
+ if (size > 0){
+ im->text_prop[propidx].size=size;
+ }
+ if (strlen(font) > 0){
+ strcpy(im->text_prop[propidx].font,font);
+ }
+ if (propidx==sindex && sindex != 0) break;
+ }
+ } else {
+ rrd_set_error("invalid fonttag '%s'",prop);
+ return;
+ }
+ } else {
+ rrd_set_error("invalid text property format");
+ return;
+ }
+ break;
+ }
+ case 'm':
+ im->canvas->zoom = atof(optarg);
+ if (im->canvas->zoom <= 0.0) {
+ rrd_set_error("zoom factor must be > 0");
+ return;
+ }
+ break;
+ case 't':
+ strncpy(im->title,optarg,150);
+ im->title[150]='\0';
+ break;
+
+ case 'R':
+ if ( strcmp( optarg, "normal" ) == 0 )
+ im->canvas->aa_type = AA_NORMAL;
+ else if ( strcmp( optarg, "light" ) == 0 )
+ im->canvas->aa_type = AA_LIGHT;
+ else if ( strcmp( optarg, "mono" ) == 0 )
+ im->canvas->aa_type = AA_NONE;
+ else
+ {
+ rrd_set_error("unknown font-render-mode '%s'", optarg );
+ return;
+ }
+ break;
+
+ case 'B':
+ im->canvas->font_aa_threshold = atof(optarg);
+ break;
+
+ case 'W':
+ strncpy(im->watermark,optarg,100);
+ im->watermark[99]='\0';
+ break;
+
+ case '?':
+ if (optopt != 0)
+ rrd_set_error("unknown option '%c'", optopt);
+ else
+ rrd_set_error("unknown option '%s'",argv[optind-1]);
+ return;
+ }
+ }
+
+ if (optind >= argc) {
+ rrd_set_error("missing filename");
+ return;
+ }
+
+ if (im->logarithmic == 1 && im->minval <= 0){
+ rrd_set_error("for a logarithmic yaxis you must specify a lower-limit > 0");
+ return;
+ }
+
+ if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
+ /* error string is set in parsetime.c */
+ return;
+ }
+
+ if (start_tmp < 3600*24*365*10){
+ rrd_set_error("the first entry to fetch should be after 1980 (%ld)",start_tmp);
+ return;
+ }
+
+ if (end_tmp < start_tmp) {
+ rrd_set_error("start (%ld) should be less than end (%ld)",
+ start_tmp, end_tmp);
+ return;
+ }
+
+ im->start = start_tmp;
+ im->end = end_tmp;
+ im->step = max((long)im->step, (im->end-im->start)/im->xsize);
+}
+
+int
+rrd_graph_color(image_desc_t *im, char *var, char *err, int optional)
+{
+ char *color;
+ graph_desc_t *gdp=&im->gdes[im->gdes_c-1];
+
+ color=strstr(var,"#");
+ if (color==NULL) {
+ if (optional==0) {
+ rrd_set_error("Found no color in %s",err);
+ return 0;
+ }
+ return 0;
+ } else {
+ int n=0;
+ char *rest;
+ gfx_color_t col;
+
+ rest=strstr(color,":");
+ if (rest!=NULL)
+ n=rest-color;
+ else
+ n=strlen(color);
+
+ switch (n) {
+ case 7:
+ sscanf(color,"#%6lx%n",&col,&n);
+ col = (col << 8) + 0xff /* shift left by 8 */;
+ if (n!=7) rrd_set_error("Color problem in %s",err);
+ break;
+ case 9:
+ sscanf(color,"#%8lx%n",&col,&n);
+ if (n==9) break;
+ default:
+ rrd_set_error("Color problem in %s",err);
+ }
+ if (rrd_test_error()) return 0;
+ gdp->col = col;
+ return n;
+ }
+}
+
+
+int bad_format(char *fmt) {
+ char *ptr;
+ int n=0;
+ ptr = fmt;
+ while (*ptr != '\0')
+ if (*ptr++ == '%') {
+
+ /* line cannot end with percent char */
+ if (*ptr == '\0') return 1;
+
+ /* '%s', '%S' and '%%' are allowed */
+ if (*ptr == 's' || *ptr == 'S' || *ptr == '%') ptr++;
+
+ /* %c is allowed (but use only with vdef!) */
+ else if (*ptr == 'c') {
+ ptr++;
+ n=1;
+ }
+
+ /* or else '% 6.2lf' and such are allowed */
+ else {
+ /* optional padding character */
+ if (*ptr == ' ' || *ptr == '+' || *ptr == '-') ptr++;
+
+ /* This should take care of 'm.n' with all three optional */
+ while (*ptr >= '0' && *ptr <= '9') ptr++;
+ if (*ptr == '.') ptr++;
+ while (*ptr >= '0' && *ptr <= '9') ptr++;
+
+ /* Either 'le', 'lf' or 'lg' must follow here */
+ if (*ptr++ != 'l') return 1;
+ if (*ptr == 'e' || *ptr == 'f' || *ptr == 'g') ptr++;
+ else return 1;
+ n++;
+ }
+ }
+
+ return (n!=1);
+}
+
+
+int
+vdef_parse(gdes,str)
+struct graph_desc_t *gdes;
+const char *const str;
+{
+ /* A VDEF currently is either "func" or "param,func"
+ * so the parsing is rather simple. Change if needed.
+ */
+ double param;
+ char func[30];
+ int n;
+
+ n=0;
+ sscanf(str,"%le,%29[A-Z]%n",¶m,func,&n);
+ if (n== (int)strlen(str)) { /* matched */
+ ;
+ } else {
+ n=0;
+ sscanf(str,"%29[A-Z]%n",func,&n);
+ if (n== (int)strlen(str)) { /* matched */
+ param=DNAN;
+ } else {
+ rrd_set_error("Unknown function string '%s' in VDEF '%s'"
+ ,str
+ ,gdes->vname
+ );
+ return -1;
+ }
+ }
+ if (!strcmp("PERCENT",func)) gdes->vf.op = VDEF_PERCENT;
+ else if (!strcmp("MAXIMUM",func)) gdes->vf.op = VDEF_MAXIMUM;
+ else if (!strcmp("AVERAGE",func)) gdes->vf.op = VDEF_AVERAGE;
+ else if (!strcmp("MINIMUM",func)) gdes->vf.op = VDEF_MINIMUM;
+ else if (!strcmp("TOTAL", func)) gdes->vf.op = VDEF_TOTAL;
+ else if (!strcmp("FIRST", func)) gdes->vf.op = VDEF_FIRST;
+ else if (!strcmp("LAST", func)) gdes->vf.op = VDEF_LAST;
+ else if (!strcmp("LSLSLOPE", func)) gdes->vf.op = VDEF_LSLSLOPE;
+ else if (!strcmp("LSLINT", func)) gdes->vf.op = VDEF_LSLINT;
+ else if (!strcmp("LSLCORREL",func)) gdes->vf.op = VDEF_LSLCORREL;
+ else {
+ rrd_set_error("Unknown function '%s' in VDEF '%s'\n"
+ ,func
+ ,gdes->vname
+ );
+ return -1;
+ };
+
+ switch (gdes->vf.op) {
+ case VDEF_PERCENT:
+ if (isnan(param)) { /* no parameter given */
+ rrd_set_error("Function '%s' needs parameter in VDEF '%s'\n"
+ ,func
+ ,gdes->vname
+ );
+ return -1;
+ };
+ if (param>=0.0 && param<=100.0) {
+ gdes->vf.param = param;
+ gdes->vf.val = DNAN; /* undefined */
+ gdes->vf.when = 0; /* undefined */
+ } else {
+ rrd_set_error("Parameter '%f' out of range in VDEF '%s'\n"
+ ,param
+ ,gdes->vname
+ );
+ return -1;
+ };
+ break;
+ case VDEF_MAXIMUM:
+ case VDEF_AVERAGE:
+ case VDEF_MINIMUM:
+ case VDEF_TOTAL:
+ case VDEF_FIRST:
+ case VDEF_LAST:
+ case VDEF_LSLSLOPE:
+ case VDEF_LSLINT:
+ case VDEF_LSLCORREL:
+ if (isnan(param)) {
+ gdes->vf.param = DNAN;
+ gdes->vf.val = DNAN;
+ gdes->vf.when = 0;
+ } else {
+ rrd_set_error("Function '%s' needs no parameter in VDEF '%s'\n"
+ ,func
+ ,gdes->vname
+ );
+ return -1;
+ };
+ break;
+ };
+ return 0;
+}
+
+
+int
+vdef_calc(im,gdi)
+image_desc_t *im;
+int gdi;
+{
+ graph_desc_t *src,*dst;
+ rrd_value_t *data;
+ long step,steps;
+
+ dst = &im->gdes[gdi];
+ src = &im->gdes[dst->vidx];
+ data = src->data + src->ds;
+ steps = (src->end - src->start) / src->step;
+
+#if 0
+printf("DEBUG: start == %lu, end == %lu, %lu steps\n"
+ ,src->start
+ ,src->end
+ ,steps
+ );
+#endif
+
+ switch (dst->vf.op) {
+ case VDEF_PERCENT: {
+ rrd_value_t * array;
+ int field;
+
+
+ if ((array = malloc(steps*sizeof(double)))==NULL) {
+ rrd_set_error("malloc VDEV_PERCENT");
+ return -1;
+ }
+ for (step=0;step < steps; step++) {
+ array[step]=data[step*src->ds_cnt];
+ }
+ qsort(array,step,sizeof(double),vdef_percent_compar);
+
+ field = (steps-1)*dst->vf.param/100;
+ dst->vf.val = array[field];
+ dst->vf.when = 0; /* no time component */
+ free(array);
+#if 0
+for(step=0;step<steps;step++)
+printf("DEBUG: %3li:%10.2f %c\n",step,array[step],step==field?'*':' ');
+#endif
+ }
+ break;
+ case VDEF_MAXIMUM:
+ step=0;
+ while (step != steps && isnan(data[step*src->ds_cnt])) step++;
+ if (step == steps) {
+ dst->vf.val = DNAN;
+ dst->vf.when = 0;
+ } else {
+ dst->vf.val = data[step*src->ds_cnt];
+ dst->vf.when = src->start + (step+1)*src->step;
+ }
+ while (step != steps) {
+ if (finite(data[step*src->ds_cnt])) {
+ if (data[step*src->ds_cnt] > dst->vf.val) {
+ dst->vf.val = data[step*src->ds_cnt];
+ dst->vf.when = src->start + (step+1)*src->step;
+ }
+ }
+ step++;
+ }
+ break;
+ case VDEF_TOTAL:
+ case VDEF_AVERAGE: {
+ int cnt=0;
+ double sum=0.0;
+ for (step=0;step<steps;step++) {
+ if (finite(data[step*src->ds_cnt])) {
+ sum += data[step*src->ds_cnt];
+ cnt ++;
+ };
+ }
+ if (cnt) {
+ if (dst->vf.op == VDEF_TOTAL) {
+ dst->vf.val = sum*src->step;
+ dst->vf.when = 0; /* no time component */
+ } else {
+ dst->vf.val = sum/cnt;
+ dst->vf.when = 0; /* no time component */
+ };
+ } else {
+ dst->vf.val = DNAN;
+ dst->vf.when = 0;
+ }
+ }
+ break;
+ case VDEF_MINIMUM:
+ step=0;
+ while (step != steps && isnan(data[step*src->ds_cnt])) step++;
+ if (step == steps) {
+ dst->vf.val = DNAN;
+ dst->vf.when = 0;
+ } else {
+ dst->vf.val = data[step*src->ds_cnt];
+ dst->vf.when = src->start + (step+1)*src->step;
+ }
+ while (step != steps) {
+ if (finite(data[step*src->ds_cnt])) {
+ if (data[step*src->ds_cnt] < dst->vf.val) {
+ dst->vf.val = data[step*src->ds_cnt];
+ dst->vf.when = src->start + (step+1)*src->step;
+ }
+ }
+ step++;
+ }
+ break;
+ case VDEF_FIRST:
+ /* The time value returned here is one step before the
+ * actual time value. This is the start of the first
+ * non-NaN interval.
+ */
+ step=0;
+ while (step != steps && isnan(data[step*src->ds_cnt])) step++;
+ if (step == steps) { /* all entries were NaN */
+ dst->vf.val = DNAN;
+ dst->vf.when = 0;
+ } else {
+ dst->vf.val = data[step*src->ds_cnt];
+ dst->vf.when = src->start + step*src->step;
+ }
+ break;
+ case VDEF_LAST:
+ /* The time value returned here is the
+ * actual time value. This is the end of the last
+ * non-NaN interval.
+ */
+ step=steps-1;
+ while (step >= 0 && isnan(data[step*src->ds_cnt])) step--;
+ if (step < 0) { /* all entries were NaN */
+ dst->vf.val = DNAN;
+ dst->vf.when = 0;
+ } else {
+ dst->vf.val = data[step*src->ds_cnt];
+ dst->vf.when = src->start + (step+1)*src->step;
+ }
+ break;
+ case VDEF_LSLSLOPE:
+ case VDEF_LSLINT:
+ case VDEF_LSLCORREL:{
+ /* Bestfit line by linear least squares method */
+
+ int cnt=0;
+ double SUMx, SUMy, SUMxy, SUMxx, SUMyy, slope, y_intercept, correl ;
+ SUMx = 0; SUMy = 0; SUMxy = 0; SUMxx = 0; SUMyy = 0;
+
+ for (step=0;step<steps;step++) {
+ if (finite(data[step*src->ds_cnt])) {
+ cnt++;
+ SUMx += step;
+ SUMxx += step * step;
+ SUMxy += step * data[step*src->ds_cnt];
+ SUMy += data[step*src->ds_cnt];
+ SUMyy += data[step*src->ds_cnt]*data[step*src->ds_cnt];
+ };
+ }
+
+ slope = ( SUMx*SUMy - cnt*SUMxy ) / ( SUMx*SUMx - cnt*SUMxx );
+ y_intercept = ( SUMy - slope*SUMx ) / cnt;
+ correl = (SUMxy - (SUMx*SUMy)/cnt) / sqrt((SUMxx - (SUMx*SUMx)/cnt)*(SUMyy - (SUMy*SUMy)/cnt));
+
+ if (cnt) {
+ if (dst->vf.op == VDEF_LSLSLOPE) {
+ dst->vf.val = slope;
+ dst->vf.when = 0;
+ } else if (dst->vf.op == VDEF_LSLINT) {
+ dst->vf.val = y_intercept;
+ dst->vf.when = 0;
+ } else if (dst->vf.op == VDEF_LSLCORREL) {
+ dst->vf.val = correl;
+ dst->vf.when = 0;
+ };
+
+ } else {
+ dst->vf.val = DNAN;
+ dst->vf.when = 0;
+ }
+ }
+ break;
+ }
+ return 0;
+}
+
+/* NaN < -INF < finite_values < INF */
+int
+vdef_percent_compar(a,b)
+const void *a,*b;
+{
+ /* Equality is not returned; this doesn't hurt except
+ * (maybe) for a little performance.
+ */
+
+ /* First catch NaN values. They are smallest */
+ if (isnan( *(double *)a )) return -1;
+ if (isnan( *(double *)b )) return 1;
+
+ /* NaN doesn't reach this part so INF and -INF are extremes.
+ * The sign from isinf() is compatible with the sign we return
+ */
+ if (isinf( *(double *)a )) return isinf( *(double *)a );
+ if (isinf( *(double *)b )) return isinf( *(double *)b );
+
+ /* If we reach this, both values must be finite */
+ if ( *(double *)a < *(double *)b ) return -1; else return 1;
+}
diff --git a/program/src/rrd_graph.h b/program/src/rrd_graph.h
--- /dev/null
+++ b/program/src/rrd_graph.h
@@ -0,0 +1,263 @@
+#ifndef _RRD_GRAPH_H
+#define _RRD_GRAPH_H
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include "rrd_gfx.h"
+
+#define MAX_VNAME_LEN 255
+#define DEF_NAM_FMT "%255[-_A-Za-z0-9]"
+
+#define ALTYGRID 0x01 /* use alternative y grid algorithm */
+#define ALTAUTOSCALE 0x02 /* use alternative algorithm to find lower and upper bounds */
+#define ALTAUTOSCALE_MIN 0x04 /* use alternative algorithm to find lower bounds */
+#define ALTAUTOSCALE_MAX 0x08 /* use alternative algorithm to find upper bounds */
+#define NOLEGEND 0x10 /* use no legend */
+#define NOMINOR 0x20 /* Turn off minor gridlines */
+#define ONLY_GRAPH 0x40 /* use only graph */
+#define FORCE_RULES_LEGEND 0x80 /* force printing of HRULE and VRULE legend */
+
+#define FORCE_UNITS 0x100 /* mask for all FORCE_UNITS_* flags */
+#define FORCE_UNITS_SI 0x100 /* force use of SI units in Y axis (no effect in linear graph, SI instead of E in log graph) */
+
+enum tmt_en {TMT_SECOND=0,TMT_MINUTE,TMT_HOUR,TMT_DAY,
+ TMT_WEEK,TMT_MONTH,TMT_YEAR};
+
+enum grc_en {GRC_CANVAS=0,GRC_BACK,GRC_SHADEA,GRC_SHADEB,
+ GRC_GRID,GRC_MGRID,GRC_FONT,GRC_ARROW,GRC_AXIS,GRC_FRAME,__GRC_END__};
+
+#define MGRIDWIDTH 0.6
+#define GRIDWIDTH 0.4
+
+enum gf_en {GF_PRINT=0,GF_GPRINT,GF_COMMENT,GF_HRULE,GF_VRULE,GF_LINE,
+ GF_AREA,GF_STACK,GF_TICK,
+ GF_DEF, GF_CDEF, GF_VDEF, GF_SHIFT,
+#ifdef WITH_PIECHART
+ GF_PART,
+#endif
+ GF_XPORT};
+
+enum vdef_op_en {
+ VDEF_MAXIMUM=0 /* like the MAX in (G)PRINT */
+ ,VDEF_MINIMUM /* like the MIN in (G)PRINT */
+ ,VDEF_AVERAGE /* like the AVERAGE in (G)PRINT */
+ ,VDEF_PERCENT /* Nth percentile */
+ ,VDEF_TOTAL /* average multiplied by time */
+ ,VDEF_FIRST /* first non-unknown value and time */
+ ,VDEF_LAST /* last non-unknown value and time */
+ ,VDEF_LSLSLOPE /* least squares line slope */
+ ,VDEF_LSLINT /* least squares line y_intercept */
+ ,VDEF_LSLCORREL /* least squares line correlation coefficient */
+ };
+enum text_prop_en { TEXT_PROP_DEFAULT=0, /* default settings */
+ TEXT_PROP_TITLE, /* properties for the title */
+ TEXT_PROP_AXIS, /* for the numbers next to the axis */
+ TEXT_PROP_UNIT, /* for the vertical unit description */
+ TEXT_PROP_LEGEND, /* fot the legend below the graph */
+ TEXT_PROP_LAST };
+
+typedef struct text_prop_t {
+ double size;
+ char font[1024];
+} text_prop_t;
+
+
+typedef struct vdef_t {
+ enum vdef_op_en op;
+ double param; /* parameter for function, if applicable */
+ double val; /* resulting value */
+ time_t when; /* timestamp, if applicable */
+} vdef_t;
+
+typedef struct xlab_t {
+ long minsec; /* minimum sec per pix */
+ long length; /* number of secs on the image */
+ enum tmt_en gridtm; /* grid interval in what ?*/
+ long gridst; /* how many whats per grid*/
+ enum tmt_en mgridtm; /* label interval in what ?*/
+ long mgridst; /* how many whats per label*/
+ enum tmt_en labtm; /* label interval in what ?*/
+ long labst; /* how many whats per label*/
+ long precis; /* label precision -> label placement*/
+ char *stst; /* strftime string*/
+} xlab_t;
+
+typedef struct ygrid_scale_t { /* y axis grid scaling info */
+ double gridstep;
+ int labfact;
+ char labfmt[64];
+} ygrid_scale_t;
+
+/* sensible y label intervals ...*/
+
+typedef struct ylab_t {
+ double grid; /* grid spacing */
+ int lfac[4]; /* associated label spacing*/
+} ylab_t;
+
+
+/* this structure describes the elements which can make up a graph.
+ because they are quite diverse, not all elements will use all the
+ possible parts of the structure. */
+#ifdef HAVE_SNPRINTF
+#define FMT_LEG_LEN 200
+#else
+#define FMT_LEG_LEN 2000
+#endif
+
+typedef struct graph_desc_t {
+ enum gf_en gf; /* graphing function */
+ int stack; /* boolean */
+ int debug; /* boolean */
+ char vname[MAX_VNAME_LEN+1]; /* name of the variable */
+ long vidx; /* gdes reference */
+ char rrd[1024]; /* name of the rrd_file containing data */
+ char ds_nam[DS_NAM_SIZE]; /* data source name */
+ long ds; /* data source number */
+ enum cf_en cf; /* consolidation function */
+ enum cf_en cf_reduce; /* consolidation function for reduce_data() */
+ gfx_color_t col; /* graph color */
+ char format[FMT_LEG_LEN+5]; /* format for PRINT AND GPRINT */
+ char legend[FMT_LEG_LEN+5]; /* legend*/
+ int strftm; /* should the VDEF legend be formated with strftime */
+ double leg_x,leg_y; /* location of legend */
+ double yrule; /* value for y rule line and for VDEF */
+ time_t xrule; /* time for x rule line and for VDEF */
+ vdef_t vf; /* instruction for VDEF function */
+ rpnp_t *rpnp; /* instructions for CDEF function */
+
+ /* SHIFT implementation */
+ int shidx; /* gdes reference for offset (-1 --> constant) */
+ time_t shval; /* offset if shidx is -1 */
+ time_t shift; /* current shift applied */
+
+ /* description of data fetched for the graph element */
+ time_t start,end; /* timestaps for first and last data element */
+ time_t start_orig,end_orig; /* timestaps for first and last data element */
+ unsigned long step; /* time between samples */
+ unsigned long step_orig; /* time between samples */
+ unsigned long ds_cnt; /* how many data sources are there in the fetch */
+ long data_first; /* first pointer to this data */
+ char **ds_namv; /* name of datasources in the fetch. */
+ rrd_value_t *data; /* the raw data drawn from the rrd */
+ rrd_value_t *p_data; /* processed data, xsize elments */
+ double linewidth; /* linewideth */
+} graph_desc_t;
+
+typedef struct image_desc_t {
+
+ /* configuration of graph */
+
+ char graphfile[MAXPATH]; /* filename for graphic */
+ FILE *graphhandle; /* FILE to use if filename is "-" */
+ long xsize,ysize; /* graph area size in pixels */
+#ifdef WITH_PIECHART
+ long piesize; /* size of the piechart */
+#endif
+ gfx_color_t graph_col[__GRC_END__]; /* real colors for the graph */
+ text_prop_t text_prop[TEXT_PROP_LAST]; /* text properties */
+ char ylegend[210]; /* legend along the yaxis */
+ char title[210]; /* title for graph */
+ char watermark[110]; /* watermark for graph */
+ int draw_x_grid; /* no x-grid at all */
+ int draw_y_grid; /* no x-grid at all */
+ double grid_dash_on, grid_dash_off;
+ xlab_t xlab_user; /* user defined labeling for xaxis */
+ char xlab_form[210]; /* format for the label on the xaxis */
+
+ double ygridstep; /* user defined step for y grid */
+ int ylabfact; /* every how many y grid shall a label be written ? */
+ double tabwidth; /* tabwdith */
+ time_t start,end; /* what time does the graph cover */
+ unsigned long step; /* any preference for the default step ? */
+ rrd_value_t minval,maxval; /* extreme values in the data */
+ int rigid; /* do not expand range even with
+ values outside */
+ ygrid_scale_t ygrid_scale; /* calculated y axis grid info */
+ int gridfit; /* adjust y-axis range etc so all
+ grindlines falls in integer pixel values */
+ char* imginfo; /* construct an <IMG ... tag and return
+ as first retval */
+ int lazy; /* only update the image if there is
+ reasonable probablility that the
+ existing one is out of date */
+ int slopemode; /* connect the dots of the curve directly, not using a stair */
+ int logarithmic; /* scale the yaxis logarithmic */
+
+ /* status information */
+
+ long xorigin,yorigin;/* where is (0,0) of the graph */
+#ifdef WITH_PIECHART
+ long pie_x,pie_y; /* where is the centerpoint */
+#endif
+ long ximg,yimg; /* total size of the image */
+ double magfact; /* numerical magnitude*/
+ long base; /* 1000 or 1024 depending on what we graph */
+ char symbol; /* magnitude symbol for y-axis */
+ float viewfactor; /* how should the numbers on the y-axis be scaled for viewing ? */
+ int unitsexponent; /* 10*exponent for units on y-asis */
+ int unitslength; /* width of the yaxis labels */
+ int forceleftspace; /* do not kill the space to the left of the y-axis if there is no grid */
+
+ int extra_flags; /* flags for boolean options */
+ /* data elements */
+
+ long prt_c; /* number of print elements */
+ long gdes_c; /* number of graphics elements */
+ graph_desc_t *gdes; /* points to an array of graph elements */
+ gfx_canvas_t *canvas; /* graphics library */
+} image_desc_t;
+
+/* Prototypes */
+int xtr(image_desc_t *,time_t);
+double ytr(image_desc_t *, double);
+enum gf_en gf_conv(char *);
+enum gfx_if_en if_conv(char *);
+enum tmt_en tmt_conv(char *);
+enum grc_en grc_conv(char *);
+enum text_prop_en text_prop_conv(char *);
+int im_free(image_desc_t *);
+void auto_scale( image_desc_t *, double *, char **, double *);
+void si_unit( image_desc_t *);
+void expand_range(image_desc_t *);
+void apply_gridfit(image_desc_t *);
+void reduce_data( enum cf_en, unsigned long, time_t *, time_t *, unsigned long *, unsigned long *, rrd_value_t **);
+int data_fetch( image_desc_t *);
+long find_var(image_desc_t *, char *);
+long find_var_wrapper(void *arg1, char *key);
+long lcd(long *);
+int data_calc( image_desc_t *);
+int data_proc( image_desc_t *);
+time_t find_first_time( time_t, enum tmt_en, long);
+time_t find_next_time( time_t, enum tmt_en, long);
+int print_calc(image_desc_t *, char ***);
+int leg_place(image_desc_t *);
+int calc_horizontal_grid(image_desc_t *);
+int draw_horizontal_grid(image_desc_t *);
+int horizontal_log_grid(image_desc_t *);
+void vertical_grid(image_desc_t *);
+void axis_paint(image_desc_t *);
+void grid_paint(image_desc_t *);
+int lazy_check(image_desc_t *);
+int graph_paint(image_desc_t *, char ***);
+#ifdef WITH_PIECHART
+void pie_part(image_desc_t *, gfx_color_t, double, double, double, double, double);
+#endif
+int gdes_alloc(image_desc_t *);
+int scan_for_col(const char *const , int, char *const);
+int rrd_graph(int, char **, char ***, int *, int *, FILE *, double *, double *);
+void rrd_graph_init(image_desc_t *);
+void rrd_graph_options(int, char **, image_desc_t *);
+void rrd_graph_script(int, char **, image_desc_t *, int);
+int rrd_graph_color(image_desc_t *, char *, char *, int);
+int bad_format(char *);
+int vdef_parse(struct graph_desc_t *,const char *const);
+int vdef_calc(image_desc_t *, int);
+int vdef_percent_compar(const void *,const void *);
+int graph_size_location(image_desc_t *, int
+#ifdef WITH_PIECHART
+ ,int
+#endif
+);
+
+#endif
diff --git a/program/src/rrd_graph_helper.c b/program/src/rrd_graph_helper.c
--- /dev/null
@@ -0,0 +1,866 @@
+/****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd_graph_helper.c commandline parser functions
+ * this code initially written by Alex van den Bogaerdt
+ ****************************************************************************/
+
+#include "rrd_graph.h"
+
+#define dprintf if (gdp->debug) printf
+
+/* NOTE ON PARSING:
+ *
+ * we use the following:
+ *
+ * i=0; sscanf(&line[*eaten], "what to find%n", variables, &i)
+ *
+ * Usually you want to find a separator as well. Example:
+ * i=0; sscanf(&line[*eaten], "%li:%n", &someint, &i)
+ *
+ * When the separator is not found, i is not set and thus remains zero.
+ * Another way would be to compare strlen() to i
+ *
+ * Why is this important? Because 12345abc should not be matched as
+ * integer 12345 ...
+ */
+
+/* NOTE ON VNAMES:
+ *
+ * "if ((gdp->vidx=find_var(im, l))!=-1)" is not good enough, at least
+ * not by itself.
+ *
+ * A vname as a result of a VDEF is quite different from a vname
+ * resulting of a DEF or CDEF.
+ */
+
+/* NOTE ON VNAMES:
+ *
+ * A vname called "123" is not to be parsed as the number 123
+ */
+
+
+/* Define prototypes for the parsing methods.
+ Inputs:
+ const char *const line - a fixed pointer to a fixed string
+ unsigned int *const eaten - a fixed pointer to a changing index in that line
+ graph_desc_t *const gdp - a fixed pointer to a changing graph description
+ image_desc_t *const im - a fixed pointer to a changing image description
+*/
+
+int rrd_parse_find_gf (const char * const, unsigned int *const, graph_desc_t *const);
+int rrd_parse_legend (const char * const, unsigned int *const, graph_desc_t *const);
+int rrd_parse_color (const char * const, graph_desc_t *const);
+int rrd_parse_CF (const char * const, unsigned int *const, graph_desc_t *const, enum cf_en *const);
+int rrd_parse_print (const char * const, unsigned int *const, graph_desc_t *const, image_desc_t *const);
+int rrd_parse_shift (const char * const, unsigned int *const, graph_desc_t *const, image_desc_t *const);
+int rrd_parse_xport (const char * const, unsigned int *const, graph_desc_t *const, image_desc_t *const);
+int rrd_parse_PVHLAST (const char * const, unsigned int *const, graph_desc_t *const, image_desc_t *const);
+int rrd_parse_make_vname (const char * const, unsigned int *const, graph_desc_t *const, image_desc_t *const);
+int rrd_parse_find_vname (const char * const, unsigned int *const, graph_desc_t *const, image_desc_t *const);
+int rrd_parse_def (const char * const, unsigned int *const, graph_desc_t *const, image_desc_t *const);
+int rrd_parse_vdef (const char * const, unsigned int *const, graph_desc_t *const, image_desc_t *const);
+int rrd_parse_cdef (const char * const, unsigned int *const, graph_desc_t *const, image_desc_t *const);
+
+
+
+int
+rrd_parse_find_gf(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp) {
+ char funcname[11],c1=0;
+ int i=0;
+
+ /* start an argument with DEBUG to be able to see how it is parsed */
+ sscanf(&line[*eaten], "DEBUG%n", &i);
+ if (i) {
+ gdp->debug=1;
+ (*eaten)+=i;
+ i=0;
+ dprintf("Scanning line '%s'\n",&line[*eaten]);
+ }
+ i=0;c1='\0';
+ sscanf(&line[*eaten], "%10[A-Z]%n%c", funcname, &i, &c1);
+ if (!i) {
+ rrd_set_error("Could not make sense out of '%s'",line);
+ return 1;
+ }
+ (*eaten)+=i;
+ if ((int)(gdp->gf=gf_conv(funcname)) == -1) {
+ rrd_set_error("'%s' is not a valid function name", funcname);
+ return 1;
+ } else {
+ dprintf("- found function name '%s'\n",funcname);
+ }
+
+ if (c1 == '\0') {
+ rrd_set_error("Function %s needs parameters. Line: %s\n",funcname,line);
+ return 1;
+ }
+ if (c1 == ':') (*eaten)++;
+
+ /* Some commands have a parameter before the colon
+ * (currently only LINE)
+ */
+ switch (gdp->gf) {
+ case GF_LINE:
+ if (c1 == ':') {
+ gdp->linewidth=1;
+ dprintf("- - using default width of 1\n");
+ } else {
+ i=0;sscanf(&line[*eaten],"%lf:%n",&gdp->linewidth,&i);
+ if (!i) {
+ rrd_set_error("Cannot parse line width '%s' in line '%s'\n",&line[*eaten],line);
+ return 1;
+ } else {
+ dprintf("- - scanned width %f\n",gdp->linewidth);
+ if (isnan(gdp->linewidth)) {
+ rrd_set_error("LINE width '%s' is not a number in line '%s'\n",&line[*eaten],line);
+ return 1;
+ }
+ if (isinf(gdp->linewidth)) {
+ rrd_set_error("LINE width '%s' is out of range in line '%s'\n",&line[*eaten],line);
+ return 1;
+ }
+ if (gdp->linewidth<0) {
+ rrd_set_error("LINE width '%s' is less than 0 in line '%s'\n",&line[*eaten],line);
+ return 1;
+ }
+ }
+ (*eaten)+=i;
+ }
+ break;
+ default:
+ if (c1 == ':') break;
+ rrd_set_error("Malformed '%s' command in line '%s'\n",&line[*eaten],line);
+ return 1;
+ }
+ if (line[*eaten] == '\0') {
+ rrd_set_error("Expected some arguments after '%s'\n",line);
+ return 1;
+ }
+ return 0;
+}
+
+int
+rrd_parse_legend(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp) {
+ int i;
+
+ if (line[*eaten]=='\0' || line[*eaten]==':') {
+ dprintf("- no (or: empty) legend found\n");
+ return 0;
+ }
+
+ i=scan_for_col(&line[*eaten],FMT_LEG_LEN,gdp->legend);
+
+ (*eaten)+=i;
+
+ if (line[*eaten]!='\0' && line[*eaten]!=':') {
+ rrd_set_error("Legend too long");
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+int
+rrd_parse_color(const char *const string, graph_desc_t *const gdp) {
+ unsigned int r=0,g=0,b=0,a=0,i;
+
+ /* matches the following formats:
+ ** RGB
+ ** RGBA
+ ** RRGGBB
+ ** RRGGBBAA
+ */
+
+ i=0;
+ while (string[i] && isxdigit((unsigned int)string[i])) i++;
+ if (string[i] != '\0') return 1; /* garbage follows hexdigits */
+ switch (i) {
+ case 3:
+ case 4:
+ sscanf(string, "%1x%1x%1x%1x",&r,&g,&b,&a);
+ r *= 0x11;
+ g *= 0x11;
+ b *= 0x11;
+ a *= 0x11;
+ if (i==3) a=0xFF;
+ break;
+ case 6:
+ case 8:
+ sscanf(string, "%02x%02x%02x%02x",&r,&g,&b,&a);
+ if (i==6) a=0xFF;
+ break;
+ default:
+ return 1; /* wrong number of digits */
+ }
+ gdp->col = r<<24|g<<16|b<<8|a;
+ return 0;
+}
+
+int
+rrd_parse_CF(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, enum cf_en *cf) {
+ char symname[CF_NAM_SIZE];
+ int i=0;
+
+ sscanf(&line[*eaten], CF_NAM_FMT "%n", symname,&i);
+ if ((!i)||((line[(*eaten)+i]!='\0')&&(line[(*eaten)+i]!=':'))) {
+ rrd_set_error("Cannot parse CF in '%s'",line);
+ return 1;
+ }
+ (*eaten)+=i;
+ dprintf("- using CF '%s'\n",symname);
+
+ if ((int)(*cf = cf_conv(symname))==-1) {
+ rrd_set_error("Unknown CF '%s' in '%s'",symname,line);
+ return 1;
+ }
+
+ if (line[*eaten]!='\0') (*eaten)++;
+ return 0;
+}
+
+/* Try to match next token as a vname.
+ *
+ * Returns:
+ * -1 an error occured and the error string is set
+ * other the vname index number
+ *
+ * *eaten is incremented only when a vname is found.
+ */
+int
+rrd_parse_find_vname(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, image_desc_t *const im) {
+ char tmpstr[MAX_VNAME_LEN+1];
+ int i;
+ long vidx;
+
+ i=0;sscanf(&line[*eaten], DEF_NAM_FMT "%n", tmpstr,&i);
+ if (!i) {
+ rrd_set_error("Could not parse line '%s'",line);
+ return -1;
+ }
+ if (line[*eaten+i]!=':' && line[*eaten+i]!='\0') {
+ rrd_set_error("Could not parse line '%s'",line);
+ return -1;
+ }
+ dprintf("- Considering '%s'\n",tmpstr);
+
+ if ((vidx=find_var(im,tmpstr))<0) {
+ dprintf("- Not a vname\n");
+ rrd_set_error("Not a valid vname: %s in line %s",tmpstr,line);
+ return -1;
+ }
+ dprintf("- Found vname '%s' vidx '%li'\n",tmpstr,gdp->vidx);
+ if (line[*eaten+i]==':') i++;
+ (*eaten)+=i;
+ return vidx;
+}
+
+/* Parsing old-style xPRINT and new-style xPRINT */
+int
+rrd_parse_print(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, image_desc_t *const im) {
+ /* vname:CF:format in case of DEF-based vname
+ ** vname:CF:format in case of CDEF-based vname
+ ** vname:format[:strftime] in case of VDEF-based vname
+ */
+ if ((gdp->vidx=rrd_parse_find_vname(line,eaten,gdp,im))<0) return 1;
+
+ switch (im->gdes[gdp->vidx].gf) {
+ case GF_DEF:
+ case GF_CDEF:
+ dprintf("- vname is of type DEF or CDEF, looking for CF\n");
+ if (rrd_parse_CF(line,eaten,gdp,&gdp->cf)) return 1;
+ break;
+ case GF_VDEF:
+ dprintf("- vname is of type VDEF\n");
+ break;
+ default:
+ rrd_set_error("Encountered unknown type variable '%s'",im->gdes[gdp->vidx].vname);
+ return 1;
+ }
+
+ if (rrd_parse_legend(line,eaten,gdp)) return 1;
+ /* for *PRINT the legend itself gets rendered later. We only
+ get the format at this juncture */
+ strcpy(gdp->format,gdp->legend);
+ gdp->legend[0]='\0';
+ /* this is a very crud test, parsing :style flags should be in a function */
+ if (im->gdes[gdp->vidx].gf == GF_VDEF && strcmp(line+(*eaten),":strftime")==0){
+ gdp->strftm = 1;
+ (*eaten)+=strlen(":strftime");
+ }
+ return 0;
+}
+
+/* SHIFT:_def_or_cdef:_vdef_or_number_
+ */
+int
+rrd_parse_shift(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, image_desc_t *const im) {
+ int i;
+
+ if ((gdp->vidx=rrd_parse_find_vname(line,eaten,gdp,im))<0) return 1;
+
+ switch (im->gdes[gdp->vidx].gf) {
+ case GF_DEF:
+ case GF_CDEF:
+ dprintf("- vname is of type DEF or CDEF, OK\n");
+ break;
+ case GF_VDEF:
+ rrd_set_error("Cannot shift a VDEF: '%s' in line '%s'\n",im->gdes[gdp->vidx].vname,line);
+ return 1;
+ default:
+ rrd_set_error("Encountered unknown type variable '%s' in line '%s'",im->gdes[gdp->vidx].vname,line);
+ return 1;
+ }
+
+ if ((gdp->shidx=rrd_parse_find_vname(line,eaten,gdp,im))>=0) {
+ switch (im->gdes[gdp->shidx].gf) {
+ case GF_DEF:
+ case GF_CDEF:
+ rrd_set_error("Offset cannot be a (C)DEF: '%s' in line '%s'\n",im->gdes[gdp->shidx].vname,line);
+ return 1;
+ case GF_VDEF:
+ dprintf("- vname is of type VDEF, OK\n");
+ break;
+ default:
+ rrd_set_error("Encountered unknown type variable '%s' in line '%s'",im->gdes[gdp->vidx].vname,line);
+ return 1;
+ }
+ } else {
+ rrd_clear_error();
+ i=0; sscanf(&line[*eaten],"%li%n",&gdp->shval,&i);
+ if (i!=(int)strlen(&line[*eaten])) {
+ rrd_set_error("Not a valid offset: %s in line %s",&line[*eaten],line);
+ return 1;
+ }
+ (*eaten)+=i;
+ dprintf("- offset is number %li\n",gdp->shval);
+ gdp->shidx = -1;
+ }
+ return 0;
+}
+
+/* XPORT:_def_or_cdef[:legend]
+ */
+int
+rrd_parse_xport(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, image_desc_t *const im) {
+ if ((gdp->vidx=rrd_parse_find_vname(line,eaten,gdp,im))<0) return 1;
+
+ switch (im->gdes[gdp->vidx].gf) {
+ case GF_DEF:
+ case GF_CDEF:
+ dprintf("- vname is of type DEF or CDEF, OK\n");
+ break;
+ case GF_VDEF:
+ rrd_set_error("Cannot xport a VDEF: '%s' in line '%s'\n",im->gdes[gdp->vidx].vname,line);
+ return 1;
+ default:
+ rrd_set_error("Encountered unknown type variable '%s' in line '%s'",im->gdes[gdp->vidx].vname,line);
+ return 1;
+ }
+ dprintf("- looking for legend in '%s'\n",&line[*eaten]);
+ if (rrd_parse_legend(line,eaten,gdp)) return 1;
+ return 0;
+}
+
+/* Parsing of PART, VRULE, HRULE, LINE, AREA, STACK and TICK
+** is done in one function.
+**
+** Stacking PART, VRULE, HRULE or TICK is not allowed.
+**
+** If a number (which is valid to enter) is more than a
+** certain amount of characters, it is caught as an error.
+** While this is arguable, so is entering fixed numbers
+** with more than MAX_VNAME_LEN significant digits.
+*/
+int
+rrd_parse_PVHLAST(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, image_desc_t *const im) {
+ int i,j,k;
+ int colorfound=0;
+ char tmpstr[MAX_VNAME_LEN + 10]; /* vname#RRGGBBAA\0 */
+ static int spacecnt = 0;
+
+ if (spacecnt == 0) {
+ float one_space = gfx_get_text_width(im->canvas, 0,
+ im->text_prop[TEXT_PROP_LEGEND].font,
+ im->text_prop[TEXT_PROP_LEGEND].size,
+ im->tabwidth," ", 0) / 4.0;
+ float target_space = gfx_get_text_width(im->canvas, 0,
+ im->text_prop[TEXT_PROP_LEGEND].font,
+ im->text_prop[TEXT_PROP_LEGEND].size,
+ im->tabwidth,"oo", 0);
+ spacecnt = target_space / one_space;
+ dprintf("- spacecnt: %i onespace: %f targspace: %f\n",spacecnt,one_space,target_space);
+ }
+
+
+ dprintf("- parsing '%s'\n",&line[*eaten]);
+
+ /* have simpler code in the drawing section */
+ if ( gdp->gf == GF_STACK ){
+ gdp->stack=1;
+ }
+
+ i=scan_for_col(&line[*eaten],MAX_VNAME_LEN+9,tmpstr);
+ if (line[*eaten+i]!='\0' && line[*eaten+i]!=':') {
+ rrd_set_error("Cannot parse line '%s'",line);
+ return 1;
+ }
+
+ j=i; while (j>0 && tmpstr[j]!='#') j--;
+
+ if (j) {
+ tmpstr[j]='\0';
+ }
+ /* We now have:
+ * tmpstr[0] containing vname
+ * tmpstr[j] if j!=0 then containing color
+ * i size of vname + color
+ * j if j!=0 then size of vname
+ */
+
+ /* Number or vname ?
+ * If it is an existing vname, that's OK, provided that it is a
+ * valid type (need time for VRULE, not a float)
+ * Else see if it parses as a number.
+ */
+ dprintf("- examining string '%s'\n",tmpstr);
+ if ((gdp->vidx=find_var(im,tmpstr))>=0) {
+ dprintf("- found vname: '%s' vidx %li\n",tmpstr,gdp->vidx);
+ switch (gdp->gf) {
+#ifdef WITH_PIECHART
+ case GF_PART:
+#endif
+ case GF_VRULE:
+ case GF_HRULE:
+ if (im->gdes[gdp->vidx].gf != GF_VDEF) {
+ rrd_set_error("Using vname %s of wrong type in line %s\n",im->gdes[gdp->gf].vname,line);
+ return 1;
+ }
+ break;
+ default:;
+ }
+ } else {
+ dprintf("- it is not an existing vname\n");
+ switch (gdp->gf) {
+ case GF_VRULE:
+ k=0;sscanf(tmpstr,"%li%n",&gdp->xrule,&k);
+ if (((j!=0)&&(k==j))||((j==0)&&(k==i))) {
+ dprintf("- found time: %li\n",gdp->xrule);
+ } else {
+ dprintf("- is is not a valid number: %li\n",gdp->xrule);
+ rrd_set_error("parameter '%s' does not represent time in line %s\n",tmpstr,line);
+ return 1;
+ }
+ default:
+ k=0;sscanf(tmpstr,"%lf%n",&gdp->yrule,&k);
+ if (((j!=0)&&(k==j))||((j==0)&&(k==i))) {
+ dprintf("- found number: %f\n",gdp->yrule);
+ } else {
+ dprintf("- is is not a valid number: %li\n",gdp->xrule);
+ rrd_set_error("parameter '%s' does not represent a number in line %s\n",tmpstr,line);
+ return 1;
+ }
+ }
+ }
+
+ if (j) {
+ j++;
+ dprintf("- examining color '%s'\n",&tmpstr[j]);
+ if (rrd_parse_color(&tmpstr[j],gdp)) {
+ rrd_set_error("Could not parse color in '%s'",&tmpstr[j]);
+ return 1;
+ }
+ dprintf("- parsed color 0x%08x\n",(unsigned int)gdp->col);
+ colorfound=1;
+ } else {
+ dprintf("- no color present in '%s'\n",tmpstr);
+ }
+
+ (*eaten) += i; /* after vname#color */
+ if (line[*eaten]!='\0') {
+ (*eaten)++; /* after colon */
+ }
+
+ if (gdp->gf == GF_TICK) {
+ dprintf("- parsing '%s'\n",&line[*eaten]);
+ dprintf("- looking for optional TICK number\n");
+ j=0;
+ sscanf(&line[*eaten],"%lf%n",&gdp->yrule,&j);
+ if (j) {
+ if (line[*eaten+j]!='\0' && line[*eaten+j]!=':') {
+ rrd_set_error("Cannot parse TICK fraction '%s'",line);
+ return 1;
+ }
+ dprintf("- found number %f\n",gdp->yrule);
+ if (gdp->yrule > 1.0 || gdp->yrule < -1.0) {
+ rrd_set_error("Tick factor should be <= 1.0");
+ return 1;
+ }
+ (*eaten)+=j;
+ } else {
+ dprintf("- not found, defaulting to 0.1\n");
+ gdp->yrule=0.1;
+ }
+ if (line[*eaten] == '\0') {
+ dprintf("- done parsing line\n");
+ return 0;
+ } else { if (line[*eaten] == ':') {
+ (*eaten)++;
+ } else {
+ rrd_set_error("Can't make sense of that TICK line");
+ return 1;
+ }
+ }
+ }
+
+ dprintf("- parsing '%s'\n",&line[*eaten]);
+
+ /* Legend is next. A legend without a color is an error.
+ ** Stacking an item without having a legend is OK however
+ ** then an empty legend should be specified.
+ ** LINE:val#color:STACK means legend is string "STACK"
+ ** LINE:val#color::STACK means no legend, and do STACK
+ ** LINE:val:STACK is an error (legend but no color)
+ ** LINE:val::STACK means no legend, and do STACK
+ */
+ if (colorfound) {
+ int err=0;
+ char *linecp = strdup(line);
+ dprintf("- looking for optional legend\n");
+
+ dprintf("- examining '%s'\n",&line[*eaten]);
+ if (linecp[*eaten] != '\0' && linecp[*eaten] != ':') {
+ int spi;
+ /* If the legend is not empty, it has to be prefixed with spacecnt ' ' characters. This then gets
+ * replaced by the color box later on. */
+ for (spi=0;spi<spacecnt && (*eaten) > 1;spi++){
+ linecp[--(*eaten)]=' ';
+ }
+ }
+
+ if (rrd_parse_legend(linecp, eaten, gdp)) err=1;
+ free(linecp);
+ if (err) return 1;
+
+ dprintf("- found legend '%s'\n", &gdp->legend[2]);
+ } else {
+ dprintf("- skipping empty legend\n");
+ if (line[*eaten] != '\0' && line[*eaten] != ':') {
+ rrd_set_error("Legend set but no color: %s",&line[*eaten]);
+ return 1;
+ }
+ }
+ if (line[*eaten]=='\0') {
+ dprintf("- done parsing line\n");
+ return 0;
+ }
+ (*eaten)++; /* after colon */
+
+ /* PART, HRULE, VRULE and TICK cannot be stacked. */
+ if ( (gdp->gf == GF_HRULE)
+ || (gdp->gf == GF_VRULE)
+#ifdef WITH_PIECHART
+ || (gdp->gf == GF_PART)
+#endif
+ || (gdp->gf == GF_TICK)
+ ) return 0;
+
+ dprintf("- parsing '%s'\n",&line[*eaten]);
+ if (line[*eaten]!='\0') {
+ dprintf("- still more, should be STACK\n");
+ j=scan_for_col(&line[*eaten],5,tmpstr);
+ if (line[*eaten+j]!='\0' && line[*eaten+j]!=':') {
+ /* not 5 chars */
+ rrd_set_error("Garbage found where STACK expected");
+ return 1;
+ }
+ if (!strcmp("STACK",tmpstr)) {
+ dprintf("- found STACK\n");
+ gdp->stack=1;
+ (*eaten)+=j;
+ } else {
+ rrd_set_error("Garbage found where STACK expected");
+ return 1;
+ }
+ }
+ if (line[*eaten]=='\0') {
+ dprintf("- done parsing line\n");
+ return 0;
+ }
+ (*eaten)++;
+ dprintf("- parsing '%s'\n",&line[*eaten]);
+
+ return 0;
+}
+
+int
+rrd_parse_make_vname(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, image_desc_t *const im) {
+ char tmpstr[MAX_VNAME_LEN + 10];
+ int i=0;
+
+ sscanf(&line[*eaten], DEF_NAM_FMT "=%n", tmpstr,&i);
+ if (!i) {
+ rrd_set_error("Cannot parse vname from '%s'",line);
+ return 1;
+ }
+ dprintf("- found candidate '%s'\n",tmpstr);
+
+ if ((gdp->vidx=find_var(im,tmpstr))>=0) {
+ rrd_set_error("Attempting to reuse '%s'",im->gdes[gdp->vidx].vname);
+ return 1;
+ }
+ strcpy(gdp->vname,tmpstr);
+ dprintf("- created vname '%s' vidx %lu\n", gdp->vname,im->gdes_c-1);
+ (*eaten)+=i;
+ return 0;
+}
+
+int
+rrd_parse_def(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, image_desc_t *const im) {
+ int i=0;
+ char command[7]; /* step, start, end, reduce */
+ char tmpstr[256];
+ struct rrd_time_value start_tv,end_tv;
+ time_t start_tmp=0,end_tmp=0;
+ char *parsetime_error=NULL;
+
+ start_tv.type = end_tv.type=ABSOLUTE_TIME;
+ start_tv.offset = end_tv.offset=0;
+ localtime_r(&gdp->start, &start_tv.tm);
+ localtime_r(&gdp->end, &end_tv.tm);
+
+ dprintf("- parsing '%s'\n",&line[*eaten]);
+ dprintf("- from line '%s'\n",line);
+
+ if (rrd_parse_make_vname(line,eaten,gdp,im)) return 1;
+ i=scan_for_col(&line[*eaten],sizeof(gdp->rrd)-1,gdp->rrd);
+ if (line[*eaten+i]!=':') {
+ rrd_set_error("Problems reading database name");
+ return 1;
+ }
+ (*eaten)+=++i;
+ dprintf("- using file '%s'\n",gdp->rrd);
+
+ i=0;
+ sscanf(&line[*eaten], DS_NAM_FMT ":%n", gdp->ds_nam,&i);
+ if (!i) {
+ rrd_set_error("Cannot parse DS in '%s'",line);
+ return 1;
+ }
+ (*eaten)+=i;
+ dprintf("- using DS '%s'\n",gdp->ds_nam);
+
+ if (rrd_parse_CF(line,eaten,gdp,&gdp->cf)) return 1;
+ gdp->cf_reduce = gdp->cf;
+
+ if (line[*eaten]=='\0') return 0;
+
+ while (1) {
+ dprintf("- optional parameter follows: %s\n", &line[*eaten]);
+ i=0;
+ sscanf(&line[*eaten], "%6[a-z]=%n", command, &i);
+ if (!i) {
+ rrd_set_error("Parse error in '%s'",line);
+ return 1;
+ }
+ (*eaten)+=i;
+ dprintf("- processing '%s'\n",command);
+ if (!strcmp("reduce",command)) {
+ if (rrd_parse_CF(line,eaten,gdp,&gdp->cf_reduce)) return 1;
+ if (line[*eaten] != '\0')
+ (*eaten)--;
+ } else if (!strcmp("step",command)) {
+ i=0;
+ sscanf(&line[*eaten],"%lu%n",&gdp->step,&i);
+ gdp->step_orig = gdp->step;
+ (*eaten)+=i;
+ dprintf("- using step %lu\n",gdp->step);
+ } else if (!strcmp("start",command)) {
+ i=scan_for_col(&line[*eaten],255,tmpstr);
+ (*eaten)+=i;
+ if ((parsetime_error = parsetime(tmpstr, &start_tv))) {
+ rrd_set_error( "start time: %s", parsetime_error );
+ return 1;
+ }
+ dprintf("- done parsing: '%s'\n",&line[*eaten]);
+ } else if (!strcmp("end",command)) {
+ i=scan_for_col(&line[*eaten],255,tmpstr);
+ (*eaten)+=i;
+ if ((parsetime_error = parsetime(tmpstr, &end_tv))) {
+ rrd_set_error( "end time: %s", parsetime_error );
+ return 1;
+ }
+ dprintf("- done parsing: '%s'\n",&line[*eaten]);
+ } else {
+ rrd_set_error("Parse error in '%s'",line);
+ return 1;
+ }
+ if (line[*eaten]=='\0') break;
+ if (line[*eaten]!=':') {
+ dprintf("- Expected to see end of string but got '%s'\n",\
+ &line[*eaten]);
+ rrd_set_error("Parse error in '%s'",line);
+ return 1;
+ }
+ (*eaten)++;
+ }
+ if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
+ /* error string is set in parsetime.c */
+ return 1;
+ }
+ if (start_tmp < 3600*24*365*10) {
+ rrd_set_error("the first entry to fetch should be "
+ "after 1980 (%ld)",start_tmp);
+ return 1;
+ }
+
+ if (end_tmp < start_tmp) {
+ rrd_set_error("start (%ld) should be less than end (%ld)",
+ start_tmp, end_tmp);
+ return 1;
+ }
+
+ gdp->start = start_tmp;
+ gdp->end = end_tmp;
+ gdp->start_orig = start_tmp;
+ gdp->end_orig = end_tmp;
+
+ dprintf("- start time %lu\n",gdp->start);
+ dprintf("- end time %lu\n",gdp->end);
+
+ return 0;
+}
+
+int
+rrd_parse_vdef(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, image_desc_t *const im) {
+ char tmpstr[MAX_VNAME_LEN+1]; /* vname\0 */
+ int i=0;
+
+ dprintf("- parsing '%s'\n",&line[*eaten]);
+ if (rrd_parse_make_vname(line,eaten,gdp,im)) return 1;
+
+ sscanf(&line[*eaten], DEF_NAM_FMT ",%n", tmpstr,&i);
+ if (!i) {
+ rrd_set_error("Cannot parse line '%s'",line);
+ return 1;
+ }
+ if ((gdp->vidx=find_var(im,tmpstr))<0) {
+ rrd_set_error("Not a valid vname: %s in line %s",tmpstr,line);
+ return 1;
+ }
+ if ( im->gdes[gdp->vidx].gf != GF_DEF
+ && im->gdes[gdp->vidx].gf != GF_CDEF) {
+ rrd_set_error("variable '%s' not DEF nor "
+ "CDEF in VDEF '%s'", tmpstr,gdp->vname);
+ return 1;
+ }
+ dprintf("- found vname: '%s' vidx %li\n",tmpstr,gdp->vidx);
+ (*eaten)+=i;
+
+ dprintf("- calling vdef_parse with param '%s'\n",&line[*eaten]);
+ vdef_parse(gdp,&line[*eaten]);
+ while (line[*eaten]!='\0'&&line[*eaten]!=':')
+ (*eaten)++;
+
+ return 0;
+}
+
+int
+rrd_parse_cdef(const char *const line, unsigned int *const eaten, graph_desc_t *const gdp, image_desc_t *const im) {
+ dprintf("- parsing '%s'\n",&line[*eaten]);
+ if (rrd_parse_make_vname(line,eaten,gdp,im)) return 1;
+ if ((gdp->rpnp = rpn_parse(
+ (void *)im,
+ &line[*eaten],
+ &find_var_wrapper)
+ )==NULL) {
+ rrd_set_error("invalid rpn expression in: %s",&line[*eaten]);
+ return 1;
+ };
+ while (line[*eaten]!='\0'&&line[*eaten]!=':')
+ (*eaten)++;
+ return 0;
+}
+
+void
+rrd_graph_script(int argc, char *argv[], image_desc_t *const im, int optno) {
+ int i;
+ /* save state for STACK backward compat function */
+ enum gf_en last_gf=GF_PRINT;
+ float last_linewidth=0.0;
+
+ for (i=optind+optno;i<argc;i++) {
+ graph_desc_t *gdp;
+ unsigned int eaten=0;
+
+ if (gdes_alloc(im)) return; /* the error string is already set */
+ gdp = &im->gdes[im->gdes_c-1];
+#ifdef DEBUG
+ gdp->debug = 1;
+#endif
+
+ if (rrd_parse_find_gf(argv[i],&eaten,gdp)) return;
+
+ switch (gdp->gf) {
+ case GF_SHIFT: /* vname:value */
+ if (rrd_parse_shift(argv[i],&eaten,gdp,im)) return;
+ break;
+ case GF_XPORT:
+ if (rrd_parse_xport(argv[i],&eaten,gdp,im)) return;
+ break;
+ case GF_PRINT: /* vname:CF:format -or- vname:format */
+ im->prt_c++;
+ case GF_GPRINT: /* vname:CF:format -or- vname:format */
+ if (rrd_parse_print(argv[i],&eaten,gdp,im)) return;
+ break;
+ case GF_COMMENT: /* text */
+ if (rrd_parse_legend(argv[i],&eaten,gdp)) return;
+ break;
+#ifdef WITH_PIECHART
+ case GF_PART: /* value[#color[:legend]] */
+#endif
+ case GF_VRULE: /* value#color[:legend] */
+ case GF_HRULE: /* value#color[:legend] */
+ case GF_LINE: /* vname-or-value[#color[:legend]][:STACK] */
+ case GF_AREA: /* vname-or-value[#color[:legend]][:STACK] */
+ case GF_TICK: /* vname#color[:num[:legend]] */
+ if (rrd_parse_PVHLAST(argv[i],&eaten,gdp,im))return;
+ last_gf = gdp->gf;
+ last_linewidth = gdp->linewidth;
+ break;
+ case GF_STACK: /* vname-or-value[#color[:legend]] */
+ if (rrd_parse_PVHLAST(argv[i],&eaten,gdp,im))return;
+ if (last_gf == GF_LINE || last_gf == GF_AREA){
+ gdp->gf = last_gf;
+ gdp->linewidth = last_linewidth;
+ } else {
+ rrd_set_error("STACK must follow LINE or AREA! command:\n%s",
+ &argv[i][eaten],argv[i]);
+ return;
+ }
+ break;
+ /* data acquisition */
+ case GF_DEF: /* vname=x:DS:CF:[:step=#][:start=#][:end=#] */
+ if (rrd_parse_def(argv[i],&eaten,gdp,im)) return;
+ break;
+ case GF_CDEF: /* vname=rpn-expression */
+ if (rrd_parse_cdef(argv[i],&eaten,gdp,im)) return;
+ break;
+ case GF_VDEF: /* vname=rpn-expression */
+ if (rrd_parse_vdef(argv[i],&eaten,gdp,im)) return;
+ break;
+ }
+ if (gdp->debug) {
+ dprintf("used %i out of %i chars\n",eaten,strlen(argv[i]));
+ dprintf("parsed line: '%s'\n",argv[i]);
+ dprintf("remaining: '%s'\n",&argv[i][eaten]);
+ if (eaten >= strlen(argv[i]))
+ dprintf("Command finished successfully\n");
+ }
+ if (eaten < strlen(argv[i])) {
+ rrd_set_error("Garbage '%s' after command:\n%s",
+ &argv[i][eaten],argv[i]);
+ return;
+ }
+ }
+}
diff --git a/program/src/rrd_hw.c b/program/src/rrd_hw.c
--- /dev/null
+++ b/program/src/rrd_hw.c
@@ -0,0 +1,843 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_hw.c : Support for Holt-Winters Smoothing/ Aberrant Behavior Detection
+ *****************************************************************************
+ * Initial version by Jake Brutlag, WebTV Networks, 5/1/00
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_hw.h"
+
+/* #define DEBUG */
+
+/* private functions */
+unsigned long MyMod(signed long val, unsigned long mod);
+int update_hwpredict(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx,
+ unsigned long ds_idx, unsigned short CDP_scratch_idx);
+int update_seasonal(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx,
+ unsigned long ds_idx, unsigned short CDP_scratch_idx,
+ rrd_value_t *seasonal_coef);
+int update_devpredict(rrd_t *rrd, unsigned long cdp_idx,
+ unsigned long rra_idx, unsigned long ds_idx, unsigned short CDP_scratch_idx);
+int update_devseasonal(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx,
+ unsigned long ds_idx, unsigned short CDP_scratch_idx,
+ rrd_value_t *seasonal_dev);
+int update_failures(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx,
+ unsigned long ds_idx, unsigned short CDP_scratch_idx);
+
+int
+update_hwpredict(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx,
+ unsigned long ds_idx, unsigned short CDP_scratch_idx)
+{
+ rrd_value_t prediction, seasonal_coef;
+ unsigned long dependent_rra_idx, seasonal_cdp_idx;
+ unival *coefs = rrd -> cdp_prep[cdp_idx].scratch;
+ rra_def_t *current_rra = &(rrd -> rra_def[rra_idx]);
+
+ /* save coefficients from current prediction */
+ coefs[CDP_hw_last_intercept].u_val = coefs[CDP_hw_intercept].u_val;
+ coefs[CDP_hw_last_slope].u_val = coefs[CDP_hw_slope].u_val;
+ coefs[CDP_last_null_count].u_cnt = coefs[CDP_null_count].u_cnt;
+
+ /* retrieve the current seasonal coef */
+ dependent_rra_idx = current_rra -> par[RRA_dependent_rra_idx].u_cnt;
+ seasonal_cdp_idx = dependent_rra_idx*(rrd -> stat_head -> ds_cnt) + ds_idx;
+ if (dependent_rra_idx < rra_idx)
+ seasonal_coef = rrd -> cdp_prep[seasonal_cdp_idx].scratch[CDP_hw_last_seasonal].u_val;
+ else
+ seasonal_coef = rrd -> cdp_prep[seasonal_cdp_idx].scratch[CDP_hw_seasonal].u_val;
+
+ /* compute the prediction */
+ if (isnan(coefs[CDP_hw_intercept].u_val) || isnan(coefs[CDP_hw_slope].u_val)
+ || isnan(seasonal_coef))
+ {
+ prediction = DNAN;
+
+ /* bootstrap initialization of slope and intercept */
+ if (isnan(coefs[CDP_hw_intercept].u_val) &&
+ !isnan(coefs[CDP_scratch_idx].u_val))
+ {
+#ifdef DEBUG
+ fprintf(stderr,"Initialization of slope/intercept\n");
+#endif
+ coefs[CDP_hw_intercept].u_val = coefs[CDP_scratch_idx].u_val;
+ coefs[CDP_hw_last_intercept].u_val = coefs[CDP_scratch_idx].u_val;
+ /* initialize the slope to 0 */
+ coefs[CDP_hw_slope].u_val = 0.0;
+ coefs[CDP_hw_last_slope].u_val = 0.0;
+ /* initialize null count to 1 */
+ coefs[CDP_null_count].u_cnt = 1;
+ coefs[CDP_last_null_count].u_cnt = 1;
+ }
+ /* if seasonal coefficient is NA, then don't update intercept, slope */
+ } else {
+ prediction = coefs[CDP_hw_intercept].u_val +
+ (coefs[CDP_hw_slope].u_val)*(coefs[CDP_null_count].u_cnt)
+ + seasonal_coef;
+#ifdef DEBUG
+ fprintf(stderr,"computed prediction: %f\n",prediction);
+#endif
+ if (isnan(coefs[CDP_scratch_idx].u_val))
+ {
+ /* NA value, no updates of intercept, slope;
+ * increment the null count */
+ (coefs[CDP_null_count].u_cnt)++;
+ } else {
+#ifdef DEBUG
+ fprintf(stderr,"Updating intercept, slope\n");
+#endif
+ /* update the intercept */
+ coefs[CDP_hw_intercept].u_val = (current_rra -> par[RRA_hw_alpha].u_val)*
+ (coefs[CDP_scratch_idx].u_val - seasonal_coef) +
+ (1 - current_rra -> par[RRA_hw_alpha].u_val)*(coefs[CDP_hw_intercept].u_val
+ + (coefs[CDP_hw_slope].u_val)*(coefs[CDP_null_count].u_cnt));
+ /* update the slope */
+ coefs[CDP_hw_slope].u_val = (current_rra -> par[RRA_hw_beta].u_val)*
+ (coefs[CDP_hw_intercept].u_val - coefs[CDP_hw_last_intercept].u_val) +
+ (1 - current_rra -> par[RRA_hw_beta].u_val)*(coefs[CDP_hw_slope].u_val);
+ /* reset the null count */
+ coefs[CDP_null_count].u_cnt = 1;
+ }
+ }
+
+ /* store the prediction for writing */
+ coefs[CDP_scratch_idx].u_val = prediction;
+ return 0;
+}
+
+int
+lookup_seasonal(rrd_t *rrd, unsigned long rra_idx, unsigned long rra_start,
+ FILE *rrd_file, unsigned long offset, rrd_value_t **seasonal_coef)
+{
+ unsigned long pos_tmp;
+ /* rra_ptr[].cur_row points to the rra row to be written; this function
+ * reads cur_row + offset */
+ unsigned long row_idx = rrd -> rra_ptr[rra_idx].cur_row + offset;
+ /* handle wrap around */
+ if (row_idx >= rrd -> rra_def[rra_idx].row_cnt)
+ row_idx = row_idx % (rrd -> rra_def[rra_idx].row_cnt);
+
+ /* rra_start points to the appropriate rra block in the file */
+ /* compute the pointer to the appropriate location in the file */
+ pos_tmp = rra_start + (row_idx)*(rrd -> stat_head -> ds_cnt)*sizeof(rrd_value_t);
+
+ /* allocate memory if need be */
+ if (*seasonal_coef == NULL)
+ *seasonal_coef =
+ (rrd_value_t *) malloc((rrd -> stat_head -> ds_cnt)*sizeof(rrd_value_t));
+ if (*seasonal_coef == NULL) {
+ rrd_set_error("memory allocation failure: seasonal coef");
+ return -1;
+ }
+
+ if (!fseek(rrd_file,pos_tmp,SEEK_SET))
+ {
+ if (fread(*seasonal_coef,sizeof(rrd_value_t),rrd->stat_head->ds_cnt,rrd_file)
+ == rrd -> stat_head -> ds_cnt)
+ {
+ /* success! */
+ /* we can safely ignore the rule requiring a seek operation between read
+ * and write, because this read moves the file pointer to somewhere
+ * in the file other than the next write location.
+ * */
+ return 0;
+ } else {
+ rrd_set_error("read operation failed in lookup_seasonal(): %lu\n",pos_tmp);
+ }
+ } else {
+ rrd_set_error("seek operation failed in lookup_seasonal(): %lu\n",pos_tmp);
+ }
+
+ return -1;
+}
+
+int
+update_seasonal(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx,
+ unsigned long ds_idx, unsigned short CDP_scratch_idx, rrd_value_t *seasonal_coef)
+{
+/* TODO: extract common if subblocks in the wake of I/O optimization */
+ rrd_value_t intercept, seasonal;
+ rra_def_t *current_rra = &(rrd -> rra_def[rra_idx]);
+ rra_def_t *hw_rra = &(rrd -> rra_def[current_rra -> par[RRA_dependent_rra_idx].u_cnt]);
+ /* obtain cdp_prep index for HWPREDICT */
+ unsigned long hw_cdp_idx = (current_rra -> par[RRA_dependent_rra_idx].u_cnt)
+ * (rrd -> stat_head -> ds_cnt) + ds_idx;
+ unival *coefs = rrd -> cdp_prep[hw_cdp_idx].scratch;
+
+ /* update seasonal coefficient in cdp prep areas */
+ seasonal = rrd -> cdp_prep[cdp_idx].scratch[CDP_hw_seasonal].u_val;
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_hw_last_seasonal].u_val = seasonal;
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_hw_seasonal].u_val =
+ seasonal_coef[ds_idx];
+
+ /* update seasonal value for disk */
+ if (current_rra -> par[RRA_dependent_rra_idx].u_cnt < rra_idx)
+ /* associated HWPREDICT has already been updated */
+ /* check for possible NA values */
+ if (isnan(rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val))
+ {
+ /* no update, store the old value unchanged,
+ * doesn't matter if it is NA */
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = seasonal;
+ } else if (isnan(coefs[CDP_hw_last_intercept].u_val)
+ || isnan(coefs[CDP_hw_last_slope].u_val))
+ {
+ /* this should never happen, as HWPREDICT was already updated */
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val= DNAN;
+ } else if (isnan(seasonal))
+ {
+ /* initialization: intercept is not currently being updated */
+#ifdef DEBUG
+ fprintf(stderr,"Initialization of seasonal coef %lu\n",
+ rrd -> rra_ptr[rra_idx].cur_row);
+#endif
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val
+ -= coefs[CDP_hw_last_intercept].u_val;
+ } else {
+ intercept = coefs[CDP_hw_intercept].u_val;
+#ifdef DEBUG
+ fprintf(stderr,
+ "Updating seasonal, params: gamma %f, new intercept %f, old seasonal %f\n",
+ current_rra -> par[RRA_seasonal_gamma].u_val,
+ intercept, seasonal);
+#endif
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+ (current_rra -> par[RRA_seasonal_gamma].u_val)*
+ (rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val - intercept) +
+ (1 - current_rra -> par[RRA_seasonal_gamma].u_val)*seasonal;
+ }
+ else {
+ /* SEASONAL array is updated first, which means the new intercept
+ * hasn't be computed; so we compute it here. */
+
+ /* check for possible NA values */
+ if (isnan(rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val))
+ {
+ /* no update, simple store the old value unchanged,
+ * doesn't matter if it is NA */
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = seasonal;
+ } else if (isnan(coefs[CDP_hw_intercept].u_val)
+ || isnan(coefs[CDP_hw_slope].u_val))
+ {
+ /* Initialization of slope and intercept will occur.
+ * force seasonal coefficient to 0. */
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val= 0.0;
+ } else if (isnan(seasonal))
+ {
+ /* initialization: intercept will not be updated
+ * CDP_hw_intercept = CDP_hw_last_intercept; just need to
+ * subtract this baseline value. */
+#ifdef DEBUG
+ fprintf(stderr,"Initialization of seasonal coef %lu\n",
+ rrd -> rra_ptr[rra_idx].cur_row);
+#endif
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val -= coefs[CDP_hw_intercept].u_val;
+ } else {
+ /* Note that we must get CDP_scratch_idx from SEASONAL array, as CDP_scratch_idx
+ * for HWPREDICT array will be DNAN. */
+ intercept = (hw_rra -> par[RRA_hw_alpha].u_val)*
+ (rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val - seasonal)
+ + (1 - hw_rra -> par[RRA_hw_alpha].u_val)*(coefs[CDP_hw_intercept].u_val
+ + (coefs[CDP_hw_slope].u_val)*(coefs[CDP_null_count].u_cnt));
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+ (current_rra -> par[RRA_seasonal_gamma].u_val)*
+ (rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val - intercept) +
+ (1 - current_rra -> par[RRA_seasonal_gamma].u_val)*seasonal;
+ }
+ }
+#ifdef DEBUG
+ fprintf(stderr,"seasonal coefficient set= %f\n",
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val);
+#endif
+ return 0;
+}
+
+int
+update_devpredict(rrd_t *rrd, unsigned long cdp_idx,
+ unsigned long rra_idx, unsigned long ds_idx, unsigned short CDP_scratch_idx)
+{
+ /* there really isn't any "update" here; the only reason this information
+ * is stored separately from DEVSEASONAL is to preserve deviation predictions
+ * for a longer duration than one seasonal cycle. */
+ unsigned long seasonal_cdp_idx = (rrd -> rra_def[rra_idx].par[RRA_dependent_rra_idx].u_cnt)
+ * (rrd -> stat_head -> ds_cnt) + ds_idx;
+
+ if (rrd -> rra_def[rra_idx].par[RRA_dependent_rra_idx].u_cnt < rra_idx)
+ {
+ /* associated DEVSEASONAL array already updated */
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val
+ = rrd -> cdp_prep[seasonal_cdp_idx].scratch[CDP_last_seasonal_deviation].u_val;
+ } else {
+ /* associated DEVSEASONAL not yet updated */
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val
+ = rrd -> cdp_prep[seasonal_cdp_idx].scratch[CDP_seasonal_deviation].u_val;
+ }
+ return 0;
+}
+
+int
+update_devseasonal(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx,
+ unsigned long ds_idx, unsigned short CDP_scratch_idx,
+ rrd_value_t *seasonal_dev)
+{
+ rrd_value_t prediction = 0, seasonal_coef = DNAN;
+ rra_def_t *current_rra = &(rrd -> rra_def[rra_idx]);
+ /* obtain cdp_prep index for HWPREDICT */
+ unsigned long hw_rra_idx = current_rra -> par[RRA_dependent_rra_idx].u_cnt;
+ unsigned long hw_cdp_idx = hw_rra_idx * (rrd -> stat_head -> ds_cnt) + ds_idx;
+ unsigned long seasonal_cdp_idx;
+ unival *coefs = rrd -> cdp_prep[hw_cdp_idx].scratch;
+
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_last_seasonal_deviation].u_val =
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_seasonal_deviation].u_val;
+ /* retrieve the next seasonal deviation value, could be NA */
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_seasonal_deviation].u_val =
+ seasonal_dev[ds_idx];
+
+ /* retrieve the current seasonal_coef (not to be confused with the
+ * current seasonal deviation). Could make this more readable by introducing
+ * some wrapper functions. */
+ seasonal_cdp_idx = (rrd -> rra_def[hw_rra_idx].par[RRA_dependent_rra_idx].u_cnt)
+ *(rrd -> stat_head -> ds_cnt) + ds_idx;
+ if (rrd -> rra_def[hw_rra_idx].par[RRA_dependent_rra_idx].u_cnt < rra_idx)
+ /* SEASONAL array already updated */
+ seasonal_coef = rrd -> cdp_prep[seasonal_cdp_idx].scratch[CDP_hw_last_seasonal].u_val;
+ else
+ /* SEASONAL array not yet updated */
+ seasonal_coef = rrd -> cdp_prep[seasonal_cdp_idx].scratch[CDP_hw_seasonal].u_val;
+
+ /* compute the abs value of the difference between the prediction and
+ * observed value */
+ if (hw_rra_idx < rra_idx)
+ {
+ /* associated HWPREDICT has already been updated */
+ if (isnan(coefs[CDP_hw_last_intercept].u_val) ||
+ isnan(coefs[CDP_hw_last_slope].u_val) ||
+ isnan(seasonal_coef))
+ {
+ /* one of the prediction values is uinitialized */
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = DNAN;
+ return 0;
+ } else {
+ prediction = coefs[CDP_hw_last_intercept].u_val +
+ (coefs[CDP_hw_last_slope].u_val)*(coefs[CDP_last_null_count].u_cnt)
+ + seasonal_coef;
+ }
+ } else {
+ /* associated HWPREDICT has NOT been updated */
+ if (isnan(coefs[CDP_hw_intercept].u_val) ||
+ isnan(coefs[CDP_hw_slope].u_val) ||
+ isnan(seasonal_coef))
+ {
+ /* one of the prediction values is uinitialized */
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = DNAN;
+ return 0;
+ } else {
+ prediction = coefs[CDP_hw_intercept].u_val +
+ (coefs[CDP_hw_slope].u_val)*(coefs[CDP_null_count].u_cnt)
+ + seasonal_coef;
+ }
+ }
+
+ if (isnan(rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val))
+ {
+ /* no update, store existing value unchanged, doesn't
+ * matter if it is NA */
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_last_seasonal_deviation].u_val;
+ } else if (isnan(rrd -> cdp_prep[cdp_idx].scratch[CDP_last_seasonal_deviation].u_val))
+ {
+ /* initialization */
+#ifdef DEBUG
+ fprintf(stderr,"Initialization of seasonal deviation\n");
+#endif
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+ fabs(prediction - rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val);
+ } else {
+ /* exponential smoothing update */
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val =
+ (rrd -> rra_def[rra_idx].par[RRA_seasonal_gamma].u_val)*
+ fabs(prediction - rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val)
+ + (1 - rrd -> rra_def[rra_idx].par[RRA_seasonal_gamma].u_val)*
+ (rrd -> cdp_prep[cdp_idx].scratch[CDP_last_seasonal_deviation].u_val);
+ }
+ return 0;
+}
+
+/* Check for a failure based on a threshold # of violations within the specified
+ * window. */
+int
+update_failures(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx,
+ unsigned long ds_idx, unsigned short CDP_scratch_idx)
+{
+ /* detection of a violation depends on 3 RRAs:
+ * HWPREDICT, SEASONAL, and DEVSEASONAL */
+ rra_def_t *current_rra = &(rrd -> rra_def[rra_idx]);
+ unsigned long dev_rra_idx = current_rra -> par[RRA_dependent_rra_idx].u_cnt;
+ rra_def_t *dev_rra = &(rrd -> rra_def[dev_rra_idx]);
+ unsigned long hw_rra_idx = dev_rra -> par[RRA_dependent_rra_idx].u_cnt;
+ rra_def_t *hw_rra = &(rrd -> rra_def[hw_rra_idx]);
+ unsigned long seasonal_rra_idx = hw_rra -> par[RRA_dependent_rra_idx].u_cnt;
+ unsigned long temp_cdp_idx;
+ rrd_value_t deviation = DNAN;
+ rrd_value_t seasonal_coef = DNAN;
+ rrd_value_t prediction = DNAN;
+ char violation = 0;
+ unsigned short violation_cnt = 0, i;
+ char *violations_array;
+
+ /* usual checks to determine the order of the RRAs */
+ temp_cdp_idx = dev_rra_idx * (rrd -> stat_head -> ds_cnt) + ds_idx;
+ if (rra_idx < seasonal_rra_idx)
+ {
+ /* DEVSEASONAL not yet updated */
+ deviation = rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_seasonal_deviation].u_val;
+ } else {
+ /* DEVSEASONAL already updated */
+ deviation = rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_last_seasonal_deviation].u_val;
+ }
+ if (!isnan(deviation)) {
+
+ temp_cdp_idx = seasonal_rra_idx * (rrd -> stat_head -> ds_cnt) + ds_idx;
+ if (rra_idx < seasonal_rra_idx)
+ {
+ /* SEASONAL not yet updated */
+ seasonal_coef = rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_hw_seasonal].u_val;
+ } else {
+ /* SEASONAL already updated */
+ seasonal_coef = rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_hw_last_seasonal].u_val;
+ }
+ /* in this code block, we know seasonal coef is not DNAN, because deviation is not
+ * null */
+
+ temp_cdp_idx = hw_rra_idx * (rrd -> stat_head -> ds_cnt) + ds_idx;
+ if (rra_idx < hw_rra_idx)
+ {
+ /* HWPREDICT not yet updated */
+ prediction = rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_hw_intercept].u_val +
+ (rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_hw_slope].u_val)
+ *(rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_null_count].u_cnt)
+ + seasonal_coef;
+ } else {
+ /* HWPREDICT already updated */
+ prediction = rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_hw_last_intercept].u_val +
+ (rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_hw_last_slope].u_val)
+ *(rrd -> cdp_prep[temp_cdp_idx].scratch[CDP_last_null_count].u_cnt)
+ + seasonal_coef;
+ }
+
+ /* determine if the observed value is a violation */
+ if (!isnan(rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val))
+ {
+ if (rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val > prediction +
+ (current_rra -> par[RRA_delta_pos].u_val)*deviation
+ || rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val < prediction -
+ (current_rra -> par[RRA_delta_neg].u_val)*deviation)
+ violation = 1;
+ } else {
+ violation = 1; /* count DNAN values as violations */
+ }
+
+ }
+
+ /* determine if a failure has occurred and update the failure array */
+ violation_cnt = violation;
+ violations_array = (char *) ((void *) rrd -> cdp_prep[cdp_idx].scratch);
+ for (i = current_rra -> par[RRA_window_len].u_cnt; i > 1; i--)
+ {
+ /* shift */
+ violations_array[i-1] = violations_array[i-2];
+ violation_cnt += violations_array[i-1];
+ }
+ violations_array[0] = violation;
+
+ if (violation_cnt < current_rra -> par[RRA_failure_threshold].u_cnt)
+ /* not a failure */
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = 0.0;
+ else
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = 1.0;
+
+ return (rrd-> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val);
+}
+
+/* For the specified CDP prep area and the FAILURES RRA,
+ * erase all history of past violations.
+ */
+void
+erase_violations(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx)
+{
+ unsigned short i;
+ char *violations_array;
+ /* check that rra_idx is a CF_FAILURES array */
+ if (cf_conv(rrd -> rra_def[rra_idx].cf_nam) != CF_FAILURES)
+ {
+#ifdef DEBUG
+ fprintf(stderr,"erase_violations called for non-FAILURES RRA: %s\n",
+ rrd -> rra_def[rra_idx].cf_nam);
+#endif
+ return;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr,"scratch buffer before erase:\n");
+ for (i = 0; i < MAX_CDP_PAR_EN; i++)
+ {
+ fprintf(stderr,"%lu ", rrd -> cdp_prep[cdp_idx].scratch[i].u_cnt);
+ }
+ fprintf(stderr,"\n");
+#endif
+
+ /* WARNING: an array of longs on disk is treated as an array of chars
+ * in memory. */
+ violations_array = (char *) ((void *) rrd -> cdp_prep[cdp_idx].scratch);
+ /* erase everything in the part of the CDP scratch array that will be
+ * used to store violations for the current window */
+ for (i = rrd -> rra_def[rra_idx].par[RRA_window_len].u_cnt; i > 0; i--)
+ {
+ violations_array[i-1] = 0;
+ }
+#ifdef DEBUG
+ fprintf(stderr,"scratch buffer after erase:\n");
+ for (i = 0; i < MAX_CDP_PAR_EN; i++)
+ {
+ fprintf(stderr,"%lu ", rrd -> cdp_prep[cdp_idx].scratch[i].u_cnt);
+ }
+ fprintf(stderr,"\n");
+#endif
+}
+
+/* Smooth a periodic array with a moving average: equal weights and
+ * length = 5% of the period. */
+int
+apply_smoother(rrd_t *rrd, unsigned long rra_idx, unsigned long rra_start,
+ FILE *rrd_file)
+{
+ unsigned long i, j, k;
+ unsigned long totalbytes;
+ rrd_value_t *rrd_values;
+ unsigned long row_length = rrd -> stat_head -> ds_cnt;
+ unsigned long row_count = rrd -> rra_def[rra_idx].row_cnt;
+ unsigned long offset;
+ FIFOqueue **buffers;
+ rrd_value_t *working_average;
+ rrd_value_t *baseline;
+
+ offset = floor(0.025*row_count);
+ if (offset == 0) return 0; /* no smoothing */
+
+ /* allocate memory */
+ totalbytes = sizeof(rrd_value_t)*row_length*row_count;
+ rrd_values = (rrd_value_t *) malloc(totalbytes);
+ if (rrd_values == NULL)
+ {
+ rrd_set_error("apply smoother: memory allocation failure");
+ return -1;
+ }
+
+ /* rra_start is at the beginning of this rra */
+ if (fseek(rrd_file,rra_start,SEEK_SET))
+ {
+ rrd_set_error("seek to rra %d failed", rra_start);
+ free(rrd_values);
+ return -1;
+ }
+ fflush(rrd_file);
+ /* could read all data in a single block, but we need to
+ * check for NA values */
+ for (i = 0; i < row_count; ++i)
+ {
+ for (j = 0; j < row_length; ++j)
+ {
+ fread(&(rrd_values[i*row_length + j]),sizeof(rrd_value_t),1,rrd_file);
+ /* should check fread for errors... */
+ if (isnan(rrd_values[i*row_length + j])) {
+ /* can't apply smoothing, still uninitialized values */
+#ifdef DEBUG
+ fprintf(stderr,"apply_smoother: NA detected in seasonal array: %ld %ld\n",i,j);
+#endif
+ free(rrd_values);
+ return 0;
+ }
+ }
+ }
+
+ /* allocate queues, one for each data source */
+ buffers = (FIFOqueue **) malloc(sizeof(FIFOqueue *)*row_length);
+ for (i = 0; i < row_length; ++i)
+ {
+ queue_alloc(&(buffers[i]),2*offset + 1);
+ }
+ /* need working average initialized to 0 */
+ working_average = (rrd_value_t *) calloc(row_length,sizeof(rrd_value_t));
+ baseline = (rrd_value_t *) calloc(row_length,sizeof(rrd_value_t));
+
+ /* compute sums of the first 2*offset terms */
+ for (i = 0; i < 2*offset; ++i)
+ {
+ k = MyMod(i - offset,row_count);
+ for (j = 0; j < row_length; ++j)
+ {
+ queue_push(buffers[j],rrd_values[k*row_length + j]);
+ working_average[j] += rrd_values[k*row_length + j];
+ }
+ }
+
+ /* compute moving averages */
+ for (i = offset; i < row_count + offset; ++i)
+ {
+ for (j = 0; j < row_length; ++j)
+ {
+ k = MyMod(i,row_count);
+ /* add a term to the sum */
+ working_average[j] += rrd_values[k*row_length + j];
+ queue_push(buffers[j],rrd_values[k*row_length + j]);
+
+ /* reset k to be the center of the window */
+ k = MyMod(i - offset,row_count);
+ /* overwrite rdd_values entry, the old value is already
+ * saved in buffers */
+ rrd_values[k*row_length + j] = working_average[j]/(2*offset + 1);
+ baseline[j] += rrd_values[k*row_length + j];
+
+ /* remove a term from the sum */
+ working_average[j] -= queue_pop(buffers[j]);
+ }
+ }
+
+ for (i = 0; i < row_length; ++i)
+ {
+ queue_dealloc(buffers[i]);
+ baseline[i] /= row_count;
+ }
+ free(buffers);
+ free(working_average);
+
+ if (cf_conv(rrd->rra_def[rra_idx].cf_nam) == CF_SEASONAL) {
+ for (j = 0; j < row_length; ++j)
+ {
+ for (i = 0; i < row_count; ++i)
+ {
+ rrd_values[i*row_length + j] -= baseline[j];
+ }
+ /* update the baseline coefficient,
+ * first, compute the cdp_index. */
+ offset = (rrd->rra_def[rra_idx].par[RRA_dependent_rra_idx].u_cnt)
+ * row_length + j;
+ (rrd->cdp_prep[offset]).scratch[CDP_hw_intercept].u_val += baseline[j];
+ }
+ /* flush cdp to disk */
+ fflush(rrd_file);
+ if (fseek(rrd_file,sizeof(stat_head_t) +
+ rrd->stat_head->ds_cnt * sizeof(ds_def_t) +
+ rrd->stat_head->rra_cnt * sizeof(rra_def_t) +
+ sizeof(live_head_t) +
+ rrd->stat_head->ds_cnt * sizeof(pdp_prep_t),SEEK_SET))
+ {
+ rrd_set_error("apply_smoother: seek to cdp_prep failed");
+ free(rrd_values);
+ return -1;
+ }
+ if (fwrite( rrd -> cdp_prep,
+ sizeof(cdp_prep_t),
+ (rrd->stat_head->rra_cnt) * rrd->stat_head->ds_cnt, rrd_file)
+ != (rrd->stat_head->rra_cnt) * (rrd->stat_head->ds_cnt) )
+ {
+ rrd_set_error("apply_smoother: cdp_prep write failed");
+ free(rrd_values);
+ return -1;
+ }
+ } /* endif CF_SEASONAL */
+
+ /* flush updated values to disk */
+ fflush(rrd_file);
+ if (fseek(rrd_file,rra_start,SEEK_SET))
+ {
+ rrd_set_error("apply_smoother: seek to pos %d failed", rra_start);
+ free(rrd_values);
+ return -1;
+ }
+ /* write as a single block */
+ if (fwrite(rrd_values,sizeof(rrd_value_t),row_length*row_count,rrd_file)
+ != row_length*row_count)
+ {
+ rrd_set_error("apply_smoother: write failed to %lu",rra_start);
+ free(rrd_values);
+ return -1;
+ }
+
+ fflush(rrd_file);
+ free(rrd_values);
+ free(baseline);
+ return 0;
+}
+
+/* Reset aberrant behavior model coefficients, including intercept, slope,
+ * seasonal, and seasonal deviation for the specified data source. */
+void
+reset_aberrant_coefficients(rrd_t *rrd, FILE *rrd_file, unsigned long ds_idx)
+{
+ unsigned long cdp_idx, rra_idx, i;
+ unsigned long cdp_start, rra_start;
+ rrd_value_t nan_buffer = DNAN;
+
+ /* compute the offset for the cdp area */
+ cdp_start = sizeof(stat_head_t) +
+ rrd->stat_head->ds_cnt * sizeof(ds_def_t) +
+ rrd->stat_head->rra_cnt * sizeof(rra_def_t) +
+ sizeof(live_head_t) +
+ rrd->stat_head->ds_cnt * sizeof(pdp_prep_t);
+ /* compute the offset for the first rra */
+ rra_start = cdp_start +
+ (rrd->stat_head->ds_cnt) * (rrd->stat_head->rra_cnt) * sizeof(cdp_prep_t) +
+ rrd->stat_head->rra_cnt * sizeof(rra_ptr_t);
+
+ /* loop over the RRAs */
+ for (rra_idx = 0; rra_idx < rrd -> stat_head -> rra_cnt; rra_idx++)
+ {
+ cdp_idx = rra_idx * (rrd-> stat_head-> ds_cnt) + ds_idx;
+ switch (cf_conv(rrd -> rra_def[rra_idx].cf_nam))
+ {
+ case CF_HWPREDICT:
+ init_hwpredict_cdp(&(rrd -> cdp_prep[cdp_idx]));
+ break;
+ case CF_SEASONAL:
+ case CF_DEVSEASONAL:
+ /* don't use init_seasonal because it will reset burn-in, which
+ * means different data sources will be calling for the smoother
+ * at different times. */
+ rrd->cdp_prep[cdp_idx].scratch[CDP_hw_seasonal].u_val = DNAN;
+ rrd->cdp_prep[cdp_idx].scratch[CDP_hw_last_seasonal].u_val = DNAN;
+ /* move to first entry of data source for this rra */
+ fseek(rrd_file,rra_start + ds_idx * sizeof(rrd_value_t),SEEK_SET);
+ /* entries for the same data source are not contiguous,
+ * temporal entries are contiguous */
+ for (i = 0; i < rrd->rra_def[rra_idx].row_cnt; ++i)
+ {
+ if (fwrite(&nan_buffer,sizeof(rrd_value_t),1,rrd_file) != 1)
+ {
+ rrd_set_error(
+ "reset_aberrant_coefficients: write failed data source %lu rra %s",
+ ds_idx,rrd->rra_def[rra_idx].cf_nam);
+ return;
+ }
+ fseek(rrd_file,(rrd->stat_head->ds_cnt - 1) *
+ sizeof(rrd_value_t),SEEK_CUR);
+ }
+ break;
+ case CF_FAILURES:
+ erase_violations(rrd,cdp_idx,rra_idx);
+ break;
+ default:
+ break;
+ }
+ /* move offset to the next rra */
+ rra_start += rrd->rra_def[rra_idx].row_cnt * rrd->stat_head->ds_cnt *
+ sizeof(rrd_value_t);
+ }
+ fseek(rrd_file,cdp_start,SEEK_SET);
+ if (fwrite( rrd -> cdp_prep,
+ sizeof(cdp_prep_t),
+ (rrd->stat_head->rra_cnt) * rrd->stat_head->ds_cnt, rrd_file)
+ != (rrd->stat_head->rra_cnt) * (rrd->stat_head->ds_cnt) )
+ {
+ rrd_set_error("reset_aberrant_coefficients: cdp_prep write failed");
+ return;
+ }
+}
+
+void init_hwpredict_cdp(cdp_prep_t *cdp)
+{
+ cdp->scratch[CDP_hw_intercept].u_val = DNAN;
+ cdp->scratch[CDP_hw_last_intercept].u_val = DNAN;
+ cdp->scratch[CDP_hw_slope].u_val = DNAN;
+ cdp->scratch[CDP_hw_last_slope].u_val = DNAN;
+ cdp->scratch[CDP_null_count].u_cnt = 1;
+ cdp->scratch[CDP_last_null_count].u_cnt = 1;
+}
+
+void init_seasonal_cdp(cdp_prep_t *cdp)
+{
+ cdp->scratch[CDP_hw_seasonal].u_val = DNAN;
+ cdp->scratch[CDP_hw_last_seasonal].u_val = DNAN;
+ cdp->scratch[CDP_init_seasonal].u_cnt = 1;
+}
+
+int
+update_aberrant_CF(rrd_t *rrd, rrd_value_t pdp_val, enum cf_en current_cf,
+ unsigned long cdp_idx, unsigned long rra_idx, unsigned long ds_idx,
+ unsigned short CDP_scratch_idx, rrd_value_t *seasonal_coef)
+{
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val = pdp_val;
+ switch (current_cf) {
+ case CF_AVERAGE:
+ default:
+ return 0;
+ case CF_HWPREDICT:
+ return update_hwpredict(rrd,cdp_idx,rra_idx,ds_idx,CDP_scratch_idx);
+ case CF_DEVPREDICT:
+ return update_devpredict(rrd,cdp_idx,rra_idx,ds_idx,CDP_scratch_idx);
+ case CF_SEASONAL:
+ return update_seasonal(rrd,cdp_idx,rra_idx,ds_idx,CDP_scratch_idx,seasonal_coef);
+ case CF_DEVSEASONAL:
+ return update_devseasonal(rrd,cdp_idx,rra_idx,ds_idx,CDP_scratch_idx,seasonal_coef);
+ case CF_FAILURES:
+ return update_failures(rrd,cdp_idx,rra_idx,ds_idx,CDP_scratch_idx);
+ }
+ return -1;
+}
+
+unsigned long MyMod(signed long val, unsigned long mod)
+{
+ unsigned long new_val;
+ if (val < 0)
+ new_val = ((unsigned long) abs(val)) % mod;
+ else
+ new_val = (val % mod);
+
+ if (val < 0)
+ return (mod - new_val);
+ else
+ return (new_val);
+}
+
+/* a standard fixed-capacity FIF0 queue implementation
+ * No overflow checking is performed. */
+int queue_alloc(FIFOqueue **q,int capacity)
+{
+ *q = (FIFOqueue *) malloc(sizeof(FIFOqueue));
+ if (*q == NULL) return -1;
+ (*q) -> queue = (rrd_value_t *) malloc(sizeof(rrd_value_t)*capacity);
+ if ((*q) -> queue == NULL)
+ {
+ free(*q);
+ return -1;
+ }
+ (*q) -> capacity = capacity;
+ (*q) -> head = capacity;
+ (*q) -> tail = 0;
+ return 0;
+}
+
+int queue_isempty(FIFOqueue *q)
+{
+ return (q -> head % q -> capacity == q -> tail);
+}
+
+void queue_push(FIFOqueue *q, rrd_value_t value)
+{
+ q -> queue[(q -> tail)++] = value;
+ q -> tail = q -> tail % q -> capacity;
+}
+
+rrd_value_t queue_pop(FIFOqueue *q)
+{
+ q -> head = q -> head % q -> capacity;
+ return q -> queue[(q -> head)++];
+}
+
+void queue_dealloc(FIFOqueue *q)
+{
+ free(q -> queue);
+ free(q);
+}
diff --git a/program/src/rrd_hw.h b/program/src/rrd_hw.h
--- /dev/null
+++ b/program/src/rrd_hw.h
@@ -0,0 +1,33 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_hw.h : Support for Holt-Winters Smoothing/ Aberrant Behavior Detection
+ *****************************************************************************/
+
+/* functions implemented in rrd_hw.c */
+int update_aberrant_CF(rrd_t *rrd, rrd_value_t pdp_val, enum cf_en current_cf,
+ unsigned long cdp_idx, unsigned long rra_idx, unsigned long ds_idx,
+ unsigned short CDP_scratch_idx, rrd_value_t *seasonal_coef);
+int create_hw_contingent_rras(rrd_t *rrd, unsigned short period,
+ unsigned long hashed_name);
+int lookup_seasonal(rrd_t *rrd, unsigned long rra_idx, unsigned long rra_start,
+ FILE *rrd_file, unsigned long offset, rrd_value_t **seasonal_coef);
+void erase_violations(rrd_t *rrd, unsigned long cdp_idx, unsigned long rra_idx);
+int apply_smoother(rrd_t *rrd, unsigned long rra_idx, unsigned long rra_start,
+ FILE *rrd_file);
+void reset_aberrant_coefficients(rrd_t *rrd, FILE *rrd_file, unsigned long ds_idx);
+void init_hwpredict_cdp(cdp_prep_t *);
+void init_seasonal_cdp(cdp_prep_t *);
+#define BURNIN_CYCLES 3
+
+/* a standard fixed-capacity FIFO queue implementation */
+typedef struct FIFOqueue {
+ rrd_value_t *queue;
+ int capacity, head, tail;
+} FIFOqueue;
+
+int queue_alloc(FIFOqueue **q,int capacity);
+void queue_dealloc(FIFOqueue *q);
+void queue_push(FIFOqueue *q, rrd_value_t value);
+int queue_isempty(FIFOqueue *q);
+rrd_value_t queue_pop(FIFOqueue *q);
diff --git a/program/src/rrd_info.c b/program/src/rrd_info.c
--- /dev/null
+++ b/program/src/rrd_info.c
@@ -0,0 +1,238 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_info Get Information about the configuration of an RRD
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include <stdarg.h>
+
+/* proto */
+info_t *rrd_info(int, char **);
+info_t *rrd_info_r(char *filename);
+
+/* allocate memory for string */
+char *
+sprintf_alloc(char *fmt, ...) {
+#ifdef HAVE_VSNPRINTF
+ int maxlen = 50;
+#else
+ int maxlen = 1000;
+#endif
+ char *str = NULL;
+ va_list argp;
+ str = malloc(sizeof(char)*(strlen(fmt)+maxlen));
+ if (str != NULL) {
+ va_start(argp, fmt);
+#ifdef HAVE_VSNPRINTF
+ vsnprintf(str, maxlen-1, fmt, argp);
+#else
+ vsprintf(str, fmt, argp);
+#endif
+ }
+ va_end(argp);
+ return str;
+}
+/* the function formerly known as push was renamed info_push because
+ * it is now used outside the scope of this file */
+info_t
+*info_push(info_t *info, char *key, enum info_type type, infoval value){
+ info_t *next;
+ next = malloc(sizeof(*next));
+ next->next = (info_t *) 0;
+ if( info )
+ info->next = next;
+ next->type = type;
+ next->key = key;
+ switch (type) {
+ case RD_I_VAL:
+ next->value.u_val = value.u_val;
+ break;
+ case RD_I_CNT:
+ next->value.u_cnt = value.u_cnt;
+ break;
+ case RD_I_INT:
+ next->value.u_int = value.u_int;
+ break;
+ case RD_I_STR:
+ next->value.u_str = malloc(sizeof(char)*(strlen(value.u_str)+1));
+ strcpy(next->value.u_str,value.u_str);
+ break;
+ }
+ return(next);
+}
+
+
+info_t *
+rrd_info(int argc, char **argv) {
+ info_t *info;
+
+ if(argc < 2){
+ rrd_set_error("please specify an rrd");
+ return NULL;
+ }
+
+ info = rrd_info_r(argv[1]);
+
+ return(info);
+}
+
+
+
+info_t *
+rrd_info_r(char *filename) {
+ unsigned int i,ii=0;
+ FILE *in_file;
+ rrd_t rrd;
+ info_t *data,*cd;
+ infoval info;
+ enum cf_en current_cf;
+ enum dst_en current_ds;
+
+ if(rrd_open(filename,&in_file,&rrd, RRD_READONLY)==-1){
+ return(NULL);
+ }
+ fclose(in_file);
+
+ info.u_str=filename;
+ cd=info_push(NULL,sprintf_alloc("filename"), RD_I_STR, info);
+ data=cd;
+
+ info.u_str=rrd.stat_head->version;
+ cd=info_push(cd,sprintf_alloc("rrd_version"), RD_I_STR, info);
+
+ info.u_cnt=rrd.stat_head->pdp_step;
+ cd=info_push(cd,sprintf_alloc("step"), RD_I_CNT, info);
+
+ info.u_cnt=rrd.live_head->last_up;
+ cd=info_push(cd,sprintf_alloc("last_update"), RD_I_CNT, info);
+
+ for(i=0;i<rrd.stat_head->ds_cnt;i++){
+
+ info.u_str=rrd.ds_def[i].dst;
+ cd=info_push(cd,sprintf_alloc("ds[%s].type", rrd.ds_def[i].ds_nam), RD_I_STR, info);
+
+ current_ds = dst_conv(rrd.ds_def[i].dst);
+ switch (current_ds) {
+ case DST_CDEF:
+ {
+ char *buffer = NULL;
+ rpn_compact2str((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef]),
+ rrd.ds_def, &buffer);
+ info.u_str = buffer;
+ cd=info_push(cd,sprintf_alloc("ds[%s].cdef",rrd.ds_def[i].ds_nam),RD_I_STR,info);
+ free(buffer);
+ }
+ break;
+ default:
+ info.u_cnt=rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt;
+ cd=info_push(cd,sprintf_alloc("ds[%s].minimal_heartbeat",rrd.ds_def[i].ds_nam), RD_I_CNT, info);
+
+ info.u_val=rrd.ds_def[i].par[DS_min_val].u_val;
+ cd=info_push(cd,sprintf_alloc("ds[%s].min",rrd.ds_def[i].ds_nam), RD_I_VAL, info);
+
+ info.u_val=rrd.ds_def[i].par[DS_max_val].u_val;
+ cd=info_push(cd,sprintf_alloc("ds[%s].max",rrd.ds_def[i].ds_nam), RD_I_VAL, info);
+ break;
+ }
+
+ info.u_str=rrd.pdp_prep[i].last_ds;
+ cd=info_push(cd,sprintf_alloc("ds[%s].last_ds", rrd.ds_def[i].ds_nam), RD_I_STR, info);
+
+ info.u_val=rrd.pdp_prep[i].scratch[PDP_val].u_val;
+ cd=info_push(cd,sprintf_alloc("ds[%s].value", rrd.ds_def[i].ds_nam), RD_I_VAL, info);
+
+ info.u_cnt=rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt;
+ cd=info_push(cd,sprintf_alloc("ds[%s].unknown_sec", rrd.ds_def[i].ds_nam), RD_I_CNT, info);
+ }
+
+ for(i=0;i<rrd.stat_head->rra_cnt;i++){
+ info.u_str=rrd.rra_def[i].cf_nam;
+ cd=info_push(cd,sprintf_alloc("rra[%d].cf", i), RD_I_STR, info);
+ current_cf = cf_conv(rrd.rra_def[i].cf_nam);
+
+ info.u_cnt=rrd.rra_def[i].row_cnt;
+ cd=info_push(cd,sprintf_alloc("rra[%d].rows",i), RD_I_CNT, info);
+
+ info.u_cnt=rrd.rra_def[i].pdp_cnt;
+ cd=info_push(cd,sprintf_alloc("rra[%d].pdp_per_row",i), RD_I_CNT, info);
+
+ switch(current_cf)
+ {
+ case CF_HWPREDICT:
+ info.u_val=rrd.rra_def[i].par[RRA_hw_alpha].u_val;
+ cd=info_push(cd,sprintf_alloc("rra[%d].alpha",i),RD_I_VAL,info);
+ info.u_val=rrd.rra_def[i].par[RRA_hw_beta].u_val;
+ cd=info_push(cd,sprintf_alloc("rra[%d].beta",i),RD_I_VAL,info);
+ break;
+ case CF_SEASONAL:
+ case CF_DEVSEASONAL:
+ info.u_val=rrd.rra_def[i].par[RRA_seasonal_gamma].u_val;
+ cd=info_push(cd,sprintf_alloc("rra[%d].gamma",i),RD_I_VAL,info);
+ break;
+ case CF_FAILURES:
+ info.u_val=rrd.rra_def[i].par[RRA_delta_pos].u_val;
+ cd=info_push(cd,sprintf_alloc("rra[%d].delta_pos",i),RD_I_VAL,info);
+ info.u_val=rrd.rra_def[i].par[RRA_delta_neg].u_val;
+ cd=info_push(cd,sprintf_alloc("rra[%d].delta_neg",i),RD_I_VAL,info);
+ info.u_cnt=rrd.rra_def[i].par[RRA_failure_threshold].u_cnt;
+ cd=info_push(cd,sprintf_alloc("rra[%d].failure_threshold",i),RD_I_CNT,info);
+ info.u_cnt=rrd.rra_def[i].par[RRA_window_len].u_cnt;
+ cd=info_push(cd,sprintf_alloc("rra[%d].window_length",i),RD_I_CNT,info);
+ break;
+ case CF_DEVPREDICT:
+ break;
+ default:
+ info.u_val=rrd.rra_def[i].par[RRA_cdp_xff_val].u_val;
+ cd=info_push(cd,sprintf_alloc("rra[%d].xff",i),RD_I_VAL,info);
+ break;
+ }
+
+ for(ii=0;ii<rrd.stat_head->ds_cnt;ii++){
+ switch(current_cf)
+ {
+ case CF_HWPREDICT:
+ info.u_val=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_intercept].u_val;
+ cd=info_push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].intercept",i,ii), RD_I_VAL, info);
+ info.u_val=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_slope].u_val;
+ cd=info_push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].slope",i,ii), RD_I_VAL, info);
+ info.u_cnt=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_null_count].u_cnt;
+ cd=info_push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].NaN_count",i,ii), RD_I_CNT, info);
+ break;
+ case CF_SEASONAL:
+ info.u_val=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_hw_seasonal].u_val;
+ cd=info_push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].seasonal",i,ii), RD_I_VAL, info);
+ break;
+ case CF_DEVSEASONAL:
+ info.u_val=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_seasonal_deviation].u_val;
+ cd=info_push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].deviation",i,ii), RD_I_VAL, info);
+ break;
+ case CF_DEVPREDICT:
+ break;
+ case CF_FAILURES:
+ {
+ unsigned short j;
+ char *violations_array;
+ char history[MAX_FAILURES_WINDOW_LEN+1];
+ violations_array = (char*) rrd.cdp_prep[i*rrd.stat_head->ds_cnt +ii].scratch;
+ for (j = 0; j < rrd.rra_def[i].par[RRA_window_len].u_cnt; ++j)
+ history[j] = (violations_array[j] == 1) ? '1' : '0';
+ history[j] = '\0';
+ info.u_str = history;
+ cd=info_push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].history",i,ii), RD_I_STR, info);
+ }
+ break;
+ default:
+ info.u_val=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_val].u_val;
+ cd=info_push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].value",i,ii), RD_I_VAL, info);
+ info.u_cnt=rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_unkn_pdp_cnt].u_cnt;
+ cd=info_push(cd,sprintf_alloc("rra[%d].cdp_prep[%d].unknown_datapoints",i,ii), RD_I_CNT, info);
+ break;
+ }
+ }
+ }
+ rrd_free(&rrd);
+ return(data);
+
+}
diff --git a/program/src/rrd_is_thread_safe.h b/program/src/rrd_is_thread_safe.h
--- /dev/null
@@ -0,0 +1,29 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ * This file: Copyright 2003 Peter Stamfest <peter@stamfest.at>
+ * & Tobias Oetiker
+ * Distributed under the GPL
+ *****************************************************************************
+ * rrd_is_thread_safe.c Poisons some nasty function calls using GNU cpp
+ *****************************************************************************
+ * $Id$
+ *************************************************************************** */
+
+#ifndef _RRD_IS_THREAD_SAFE_H
+#define _RRD_IS_THREAD_SAFE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#undef strerror
+
+#if( 2 < __GNUC__ )
+#pragma GCC poison strtok asctime ctime gmtime localtime tmpnam strerror
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*_RRD_IS_THREAD_SAFE_H */
diff --git a/program/src/rrd_last.c b/program/src/rrd_last.c
--- /dev/null
+++ b/program/src/rrd_last.c
@@ -0,0 +1,40 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_last.c
+ *****************************************************************************
+ * Initial version by Russ Wright, @Home Network, 9/28/98
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+
+time_t
+rrd_last(int argc, char **argv)
+{
+ if(argc < 2){
+ rrd_set_error("please specify an rrd");
+ return(-1);
+ }
+
+ return( rrd_last_r(argv[1]) );
+}
+
+
+time_t
+rrd_last_r(const char *filename)
+{
+ FILE *in_file;
+ time_t lastup;
+
+ rrd_t rrd;
+
+ if(rrd_open(filename, &in_file, &rrd, RRD_READONLY)==-1){
+ return(-1);
+ }
+ lastup = rrd.live_head->last_up;
+ rrd_free(&rrd);
+ fclose(in_file);
+ return(lastup);
+}
+
+
diff --git a/program/src/rrd_lastupdate.c b/program/src/rrd_lastupdate.c
--- /dev/null
@@ -0,0 +1,54 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_lastupdate Get the last datum entered for each DS
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include <stdarg.h>
+
+int
+rrd_lastupdate(int argc, char **argv, time_t *last_update,
+ unsigned long *ds_cnt, char ***ds_namv, char ***last_ds) {
+ unsigned long i=0;
+ char *filename;
+ FILE *in_file;
+ rrd_t rrd;
+
+ if(argc < 2){
+ rrd_set_error("please specify an rrd");
+ return -1;
+ }
+ filename = argv[1];
+
+ if(rrd_open(filename,&in_file,&rrd, RRD_READONLY)==-1){
+ return(-1);
+ }
+ fclose(in_file);
+
+ *last_update=rrd.live_head->last_up;
+ *ds_cnt = rrd.stat_head->ds_cnt;
+ if (((*ds_namv) =
+ (char **) malloc(rrd.stat_head->ds_cnt * sizeof(char*)))==NULL){
+ rrd_set_error("malloc fetch ds_namv array");
+ rrd_free(&rrd);
+ return(-1);
+ }
+
+ if (((*last_ds) =
+ (char **) malloc(rrd.stat_head->ds_cnt * sizeof(char*)))==NULL){
+ rrd_set_error("malloc fetch last_ds array");
+ rrd_free(&rrd);
+ free(*ds_namv);
+ return(-1);
+ }
+
+ for(i=0;i<rrd.stat_head->ds_cnt;i++){
+ (*ds_namv)[i] = sprintf_alloc("%s", rrd.ds_def[i].ds_nam);
+ (*last_ds)[i] = sprintf_alloc("%s", rrd.pdp_prep[i].last_ds);
+ }
+
+ rrd_free(&rrd);
+ return(0);
+}
diff --git a/program/src/rrd_nan_inf.c b/program/src/rrd_nan_inf.c
--- /dev/null
@@ -0,0 +1,32 @@
+#include "rrd_nan_inf.h"
+
+int done_nan = 0;
+int done_inf = 0;
+
+double dnan;
+double dinf;
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+#include <math.h>
+
+#define NAN_FUNC (double)fmod(0.0,0.0)
+#define INF_FUNC (double)fabs((double)log(0.0))
+
+#else
+
+#define NAN_FUNC (double)(0.0/0.0)
+#define INF_FUNC (double)(1.0/0.0)
+
+#endif
+
+double set_to_DNAN(void)
+{
+ if ( !done_nan ) { dnan = NAN_FUNC; done_nan = 1; }
+ return dnan;
+}
+
+double set_to_DINF(void)
+{
+ if ( !done_inf ) { dinf = INF_FUNC; done_inf = 1; }
+ return dinf;
+}
diff --git a/program/src/rrd_nan_inf.h b/program/src/rrd_nan_inf.h
--- /dev/null
@@ -0,0 +1,5 @@
+#define DNAN set_to_DNAN()
+#define DINF set_to_DINF()
+
+double set_to_DNAN(void);
+double set_to_DINF(void);
diff --git a/program/src/rrd_not_thread_safe.c b/program/src/rrd_not_thread_safe.c
--- /dev/null
@@ -0,0 +1,47 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ * This file: Copyright 2003 Peter Stamfest <peter@stamfest.at>
+ * & Tobias Oetiker
+ * Distributed under the GPL
+ *****************************************************************************
+ * rrd_not_thread_safe.c Contains routines used when thread safety is not
+ * an issue
+ *****************************************************************************
+ * $Id$
+ *************************************************************************** */
+#include "rrd.h"
+#include "rrd_tool.h"
+#define MAXLEN 4096
+#define ERRBUFLEN 256
+
+static char rrd_error[MAXLEN+10];
+static char rrd_liberror[ERRBUFLEN+10];
+static int rrd_context_init = 0;
+/* The global context is very useful in the transition period to even
+ more thread-safe stuff, it can be used whereever we need a context
+ and do not need to worry about concurrency. */
+static struct rrd_context global_ctx = {
+ MAXLEN,
+ ERRBUFLEN,
+ rrd_error,
+ rrd_liberror
+};
+/* #include <stdarg.h> */
+
+struct rrd_context *rrd_get_context(void) {
+ if (! rrd_context_init ){
+ rrd_context_init = 1;
+ global_ctx.rrd_error[0]='\0';
+ global_ctx.lib_errstr[0]='\0';
+ }
+ return &global_ctx;
+}
+
+/* how ugly that is!!! - make sure strerror is what it should be. It
+ might be redefined to help in keeping other modules thread safe by
+ silently turning misplaced strerror into rrd_strerror, but here
+ this turns recursive! */
+#undef strerror
+const char *rrd_strerror(int err) {
+ return strerror(err);
+}
diff --git a/program/src/rrd_open.c b/program/src/rrd_open.c
--- /dev/null
+++ b/program/src/rrd_open.c
@@ -0,0 +1,254 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_open.c Open an RRD File
+ *****************************************************************************
+ * $Id$
+ * $Log$
+ * Revision 1.10 2004/05/26 22:11:12 oetiker
+ * reduce compiler warnings. Many small fixes. -- Mike Slifcak <slif@bellsouth.net>
+ *
+ * Revision 1.9 2003/04/29 21:56:49 oetiker
+ * readline in rrd_open.c reads the file in 8 KB blocks, and calls realloc for
+ * each block. realloc is very slow in Mac OS X for huge blocks, e.g. when
+ * restoring databases from huge xml files. This patch finds the size of the
+ * file, and starts out with malloc'ing the full size.
+ * -- Peter Speck <speck@ruc.dk>
+ *
+ * Revision 1.8 2003/04/11 19:43:44 oetiker
+ * New special value COUNT which allows calculations based on the position of a
+ * value within a data set. Bug fix in rrd_rpncalc.c. PREV returned erroneus
+ * value for the second value. Bug fix in rrd_restore.c. Bug causing seek error
+ * when accesing an RRD restored from an xml that holds an RRD version <3.
+ * -- Ruben Justo <ruben@ainek.com>
+ *
+ * Revision 1.7 2003/03/31 21:22:12 oetiker
+ * enables RRDtool updates with microsecond or in case of windows millisecond
+ * precision. This is needed to reduce time measurement error when archive step
+ * is small. (<30s) -- Sasha Mikheev <sasha@avalon-net.co.il>
+ *
+ * Revision 1.6 2003/02/13 07:05:27 oetiker
+ * Find attached the patch I promised to send to you. Please note that there
+ * are three new source files (src/rrd_is_thread_safe.h, src/rrd_thread_safe.c
+ * and src/rrd_not_thread_safe.c) and the introduction of librrd_th. This
+ * library is identical to librrd, but it contains support code for per-thread
+ * global variables currently used for error information only. This is similar
+ * to how errno per-thread variables are implemented. librrd_th must be linked
+ * alongside of libpthred
+ *
+ * There is also a new file "THREADS", holding some documentation.
+ *
+ * -- Peter Stamfest <peter@stamfest.at>
+ *
+ * Revision 1.5 2002/06/20 00:21:03 jake
+ * More Win32 build changes; thanks to Kerry Calvert.
+ *
+ * Revision 1.4 2002/02/01 20:34:49 oetiker
+ * fixed version number and date/time
+ *
+ * Revision 1.3 2001/03/04 13:01:55 oetiker
+ * Aberrant Behavior Detection support. A brief overview added to rrdtool.pod.
+ * Major updates to rrd_update.c, rrd_create.c. Minor update to other core files.
+ * This is backwards compatible! But new files using the Aberrant stuff are not readable
+ * by old rrdtool versions. See http://cricket.sourceforge.net/aberrant/rrd_hw.htm
+ * -- Jake Brutlag <jakeb@corp.webtv.net>
+ *
+ * Revision 1.2 2001/03/04 10:29:20 oetiker
+ * fixed filedescriptor leak
+ * -- Mike Franusich <mike@franusich.com>
+ *
+ * Revision 1.1.1.1 2001/02/25 22:25:05 oetiker
+ * checkin
+ *
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#define MEMBLK 8192
+
+/* open a database file, return its header and a open filehandle */
+/* positioned to the first cdp in the first rra */
+
+int
+rrd_open(const char *file_name, FILE **in_file, rrd_t *rrd, int rdwr)
+{
+
+
+ char *mode = NULL;
+ int version;
+
+ rrd_init(rrd);
+ if (rdwr == RRD_READONLY) {
+ mode = "rb";
+ } else {
+ mode = "rb+";
+ }
+
+ if (((*in_file) = fopen(file_name,mode)) == NULL ){
+ rrd_set_error("opening '%s': %s",file_name, rrd_strerror(errno));
+ return (-1);
+ }
+
+#ifdef HAVE_POSIX_FADVISE
+ /* In general we need no read-ahead when dealing with rrd_files.
+ When we stop reading, it is highly unlikely that we start up again.
+ In this manner we actually save time and diskaccess (and buffer cache).
+ Thanks to Dave Plonka for the Idea of using POSIX_FADV_RANDOM here. */
+ if (0 != posix_fadvise(fileno(*in_file), 0, 0, POSIX_FADV_RANDOM)) {
+ rrd_set_error("setting POSIX_FADV_RANDOM on '%s': %s",file_name, rrd_strerror(errno));
+ fclose(*in_file);
+ return(-1);
+ }
+#endif
+
+/*
+ if (rdwr == RRD_READWRITE)
+ {
+ if (setvbuf((*in_file),NULL,_IONBF,2)) {
+ rrd_set_error("failed to disable the stream buffer\n");
+ return (-1);
+ }
+ }
+*/
+
+#define MYFREAD(MYVAR,MYVART,MYCNT) \
+ if ((MYVAR = malloc(sizeof(MYVART) * MYCNT)) == NULL) {\
+ rrd_set_error("" #MYVAR " malloc"); \
+ fclose(*in_file); \
+ return (-1); } \
+ fread(MYVAR,sizeof(MYVART),MYCNT, *in_file);
+
+
+ MYFREAD(rrd->stat_head, stat_head_t, 1)
+ /* lets see if the first read worked */
+ if (ferror( *in_file ) || feof(*in_file)) {
+ rrd_set_error("reading the cookie off %s faild",file_name);
+ fclose(*in_file);
+ return(-1);
+ }
+
+ /* lets do some test if we are on track ... */
+ if (strncmp(rrd->stat_head->cookie,RRD_COOKIE,4) != 0){
+ rrd_set_error("'%s' is not an RRD file",file_name);
+ free(rrd->stat_head);
+ rrd->stat_head = NULL;
+ fclose(*in_file);
+ return(-1);}
+
+ if (rrd->stat_head->float_cookie != FLOAT_COOKIE){
+ rrd_set_error("This RRD was created on other architecture");
+ free(rrd->stat_head);
+ rrd->stat_head = NULL;
+ fclose(*in_file);
+ return(-1);}
+
+ version = atoi(rrd->stat_head->version);
+
+ if (version > atoi(RRD_VERSION)){
+ rrd_set_error("can't handle RRD file version %s",
+ rrd->stat_head->version);
+ free(rrd->stat_head);
+ rrd->stat_head = NULL;
+ fclose(*in_file);
+ return(-1);}
+
+
+ MYFREAD(rrd->ds_def, ds_def_t, rrd->stat_head->ds_cnt)
+ MYFREAD(rrd->rra_def, rra_def_t, rrd->stat_head->rra_cnt)
+ /* handle different format for the live_head */
+ if(version < 3) {
+ rrd->live_head = (live_head_t *)malloc(sizeof(live_head_t));
+ if(rrd->live_head == NULL) {
+ rrd_set_error("live_head_t malloc");
+ fclose(*in_file);
+ return (-1);
+ }
+ fread(&rrd->live_head->last_up, sizeof(long), 1, *in_file);
+ rrd->live_head->last_up_usec = 0;
+ }
+ else {
+ MYFREAD(rrd->live_head, live_head_t, 1)
+ }
+ MYFREAD(rrd->pdp_prep, pdp_prep_t, rrd->stat_head->ds_cnt)
+ MYFREAD(rrd->cdp_prep, cdp_prep_t, (rrd->stat_head->rra_cnt
+ * rrd->stat_head->ds_cnt))
+ MYFREAD(rrd->rra_ptr, rra_ptr_t, rrd->stat_head->rra_cnt)
+#undef MYFREAD
+
+ return(0);
+}
+
+void rrd_init(rrd_t *rrd)
+{
+ rrd->stat_head = NULL;
+ rrd->ds_def = NULL;
+ rrd->rra_def = NULL;
+ rrd->live_head = NULL;
+ rrd->rra_ptr = NULL;
+ rrd->pdp_prep = NULL;
+ rrd->cdp_prep = NULL;
+ rrd->rrd_value = NULL;
+}
+
+void rrd_free(rrd_t *rrd)
+{
+ if (rrd->stat_head) free(rrd->stat_head);
+ if (rrd->ds_def) free(rrd->ds_def);
+ if (rrd->rra_def) free(rrd->rra_def);
+ if (rrd->live_head) free(rrd->live_head);
+ if (rrd->rra_ptr) free(rrd->rra_ptr);
+ if (rrd->pdp_prep) free(rrd->pdp_prep);
+ if (rrd->cdp_prep) free(rrd->cdp_prep);
+ if (rrd->rrd_value) free(rrd->rrd_value);
+}
+
+/* routine used by external libraries to free memory allocated by
+ * rrd library */
+void rrd_freemem(void *mem)
+{
+
+ if (mem) free(mem);
+}
+
+int readfile(const char *file_name, char **buffer, int skipfirst){
+ long writecnt=0,totalcnt = MEMBLK;
+ long offset = 0;
+ FILE *input=NULL;
+ char c ;
+ if ((strcmp("-",file_name) == 0)) { input = stdin; }
+ else {
+ if ((input = fopen(file_name,"rb")) == NULL ){
+ rrd_set_error("opening '%s': %s",file_name,rrd_strerror(errno));
+ return (-1);
+ }
+ }
+ if (skipfirst){
+ do { c = getc(input); offset++; } while (c != '\n' && ! feof(input));
+ }
+ if (strcmp("-",file_name)) {
+ fseek(input, 0, SEEK_END);
+ /* have extra space for detecting EOF without realloc */
+ totalcnt = (ftell(input) + 1) / sizeof(char) - offset;
+ if (totalcnt < MEMBLK)
+ totalcnt = MEMBLK; /* sanitize */
+ fseek(input, offset * sizeof(char), SEEK_SET);
+ }
+ if (((*buffer) = (char *) malloc((totalcnt+4) * sizeof(char))) == NULL) {
+ perror("Allocate Buffer:");
+ exit(1);
+ };
+ do{
+ writecnt += fread((*buffer)+writecnt, 1, (totalcnt - writecnt) * sizeof(char),input);
+ if (writecnt >= totalcnt){
+ totalcnt += MEMBLK;
+ if (((*buffer)=rrd_realloc((*buffer), (totalcnt+4) * sizeof(char)))==NULL){
+ perror("Realloc Buffer:");
+ exit(1);
+ };
+ }
+ } while (! feof(input));
+ (*buffer)[writecnt] = '\0';
+ if (strcmp("-",file_name) != 0) {fclose(input);};
+ return writecnt;
+}
+
+
diff --git a/program/src/rrd_resize.c b/program/src/rrd_resize.c
--- /dev/null
+++ b/program/src/rrd_resize.c
@@ -0,0 +1,205 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_resize.c Alters size of an RRA
+ *****************************************************************************
+ * Initial version by Alex van den Bogaerdt
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+
+int
+rrd_resize(int argc, char **argv)
+{
+ char *infilename,outfilename[11]="resize.rrd";
+ FILE *infile,*outfile;
+ rrd_t rrdold,rrdnew;
+ rrd_value_t buffer;
+ int version;
+ unsigned long l,rra;
+ long modify;
+ unsigned long target_rra;
+ int grow=0,shrink=0;
+ char *endptr;
+
+ infilename=argv[1];
+ if (!strcmp(infilename,"resize.rrd")) {
+ rrd_set_error("resize.rrd is a reserved name");
+ return(-1);
+ }
+ if (argc!=5) {
+ rrd_set_error("wrong number of parameters");
+ return(-1);
+ }
+
+ target_rra=strtol(argv[2],&endptr,0);
+
+ if (!strcmp(argv[3],"GROW")) grow=1;
+ else if (!strcmp(argv[3],"SHRINK")) shrink=1;
+ else {
+ rrd_set_error("I can only GROW or SHRINK");
+ return(-1);
+ }
+
+ modify=strtol(argv[4],&endptr,0);
+
+ if ((modify<1)) {
+ rrd_set_error("Please grow or shrink with at least 1 row");
+ return(-1);
+ }
+
+ if (shrink) modify = -modify;
+
+
+ if (rrd_open(infilename, &infile, &rrdold, RRD_READWRITE)==-1) {
+ rrd_set_error("could not open RRD");
+ return(-1);
+ }
+ if (LockRRD(infile) != 0) {
+ rrd_set_error("could not lock original RRD");
+ rrd_free(&rrdold);
+ fclose(infile);
+ return(-1);
+ }
+
+ if (target_rra >= rrdold.stat_head->rra_cnt) {
+ rrd_set_error("no such RRA in this RRD");
+ rrd_free(&rrdold);
+ fclose(infile);
+ return(-1);
+ }
+
+ if (modify < 0)
+ if ((long)rrdold.rra_def[target_rra].row_cnt <= -modify) {
+ rrd_set_error("This RRA is not that big");
+ rrd_free(&rrdold);
+ fclose(infile);
+ return(-1);
+ }
+
+ rrdnew.stat_head = rrdold.stat_head;
+ rrdnew.ds_def = rrdold.ds_def;
+ rrdnew.rra_def = rrdold.rra_def;
+ rrdnew.live_head = rrdold.live_head;
+ rrdnew.pdp_prep = rrdold.pdp_prep;
+ rrdnew.cdp_prep = rrdold.cdp_prep;
+ rrdnew.rra_ptr = rrdold.rra_ptr;
+
+ version = atoi(rrdold.stat_head->version);
+ switch (version) {
+ case 3: break;
+ case 1: rrdold.stat_head->version[3]='3';
+ break;
+ default: {
+ rrd_set_error("Do not know how to handle RRD version %s",rrdold.stat_head->version);
+ rrd_free(&rrdold);
+ fclose(infile);
+ return(-1);
+ }
+ }
+
+ if ((outfile=fopen(outfilename,"wb"))==NULL) {
+ rrd_set_error("Can't create '%s'",outfilename);
+ return(-1);
+ }
+ if (LockRRD(outfile) != 0) {
+ rrd_set_error("could not lock new RRD");
+ rrd_free(&rrdold);
+ fclose(infile);
+ fclose(outfile);
+ return(-1);
+ }
+ fwrite(rrdnew.stat_head, sizeof(stat_head_t),1,outfile);
+ fwrite(rrdnew.ds_def,sizeof(ds_def_t),rrdnew.stat_head->ds_cnt,outfile);
+ fwrite(rrdnew.rra_def,sizeof(rra_def_t),rrdnew.stat_head->rra_cnt,outfile);
+ fwrite(rrdnew.live_head,sizeof(live_head_t),1,outfile);
+ fwrite(rrdnew.pdp_prep,sizeof(pdp_prep_t),rrdnew.stat_head->ds_cnt,outfile);
+ fwrite(rrdnew.cdp_prep,sizeof(cdp_prep_t),rrdnew.stat_head->ds_cnt*rrdnew.stat_head->rra_cnt,outfile);
+ fwrite(rrdnew.rra_ptr,sizeof(rra_ptr_t),rrdnew.stat_head->rra_cnt,outfile);
+
+ /* Move the CDPs from the old to the new database.
+ ** This can be made (much) faster but isn't worth the effort. Clarity
+ ** is much more important.
+ */
+
+ /* Move data in unmodified RRAs
+ */
+ l=0;
+ for (rra=0;rra<target_rra;rra++) {
+ l+=rrdnew.stat_head->ds_cnt * rrdnew.rra_def[rra].row_cnt;
+ }
+ while (l>0) {
+ fread(&buffer,sizeof(rrd_value_t),1,infile);
+ fwrite(&buffer,sizeof(rrd_value_t),1,outfile);
+ l--;
+ }
+ /* Move data in this RRA, either removing or adding some rows
+ */
+ if (modify>0) {
+ /* Adding extra rows; insert unknown values just after the
+ ** current row number.
+ */
+ l = rrdnew.stat_head->ds_cnt * (rrdnew.rra_ptr[target_rra].cur_row+1);
+ while (l>0) {
+ fread(&buffer,sizeof(rrd_value_t),1,infile);
+ fwrite(&buffer,sizeof(rrd_value_t),1,outfile);
+ l--;
+ }
+ buffer=DNAN;
+ l=rrdnew.stat_head->ds_cnt * modify;
+ while (l>0) {
+ fwrite(&buffer,sizeof(rrd_value_t),1,outfile);
+ l--;
+ }
+ } else {
+ /* Removing rows. Normally this would be just after the cursor
+ ** however this may also mean that we wrap to the beginning of
+ ** the array.
+ */
+ signed long int remove_end=0;
+
+ remove_end=(rrdnew.rra_ptr[target_rra].cur_row-modify)%rrdnew.rra_def[target_rra].row_cnt;
+ if (remove_end <= (signed long int)rrdnew.rra_ptr[target_rra].cur_row) {
+ while (remove_end >= 0) {
+ fseek(infile,sizeof(rrd_value_t)*rrdnew.stat_head->ds_cnt,SEEK_CUR);
+ rrdnew.rra_ptr[target_rra].cur_row--;
+ rrdnew.rra_def[target_rra].row_cnt--;
+ remove_end--;
+ modify++;
+ }
+ remove_end=rrdnew.rra_def[target_rra].row_cnt-1;
+ }
+ for (l=0;l<=rrdnew.rra_ptr[target_rra].cur_row;l++) {
+ unsigned int tmp;
+ for (tmp=0;tmp<rrdnew.stat_head->ds_cnt;tmp++) {
+ fread(&buffer,sizeof(rrd_value_t),1,infile);
+ fwrite(&buffer,sizeof(rrd_value_t),1,outfile);
+ }
+ }
+ while (modify<0) {
+ fseek(infile,sizeof(rrd_value_t)*rrdnew.stat_head->ds_cnt,SEEK_CUR);
+ rrdnew.rra_def[target_rra].row_cnt--;
+ modify++;
+ }
+ }
+ /* Move the rest of the CDPs
+ */
+ while (1) {
+ fread(&buffer,sizeof(rrd_value_t),1,infile);
+ if (feof(infile))
+ break;
+ fwrite(&buffer,sizeof(rrd_value_t),1,outfile);
+ }
+ rrdnew.rra_def[target_rra].row_cnt += modify;
+ fseek(outfile,sizeof(stat_head_t)+sizeof(ds_def_t)*rrdnew.stat_head->ds_cnt,SEEK_SET);
+ fwrite(rrdnew.rra_def,sizeof(rra_def_t),rrdnew.stat_head->rra_cnt, outfile);
+ fseek(outfile,sizeof(live_head_t),SEEK_CUR);
+ fseek(outfile,sizeof(pdp_prep_t)*rrdnew.stat_head->ds_cnt,SEEK_CUR);
+ fseek(outfile,sizeof(cdp_prep_t)*rrdnew.stat_head->ds_cnt*rrdnew.stat_head->rra_cnt,SEEK_CUR);
+ fwrite(rrdnew.rra_ptr,sizeof(rra_ptr_t),rrdnew.stat_head->rra_cnt, outfile);
+
+ fclose(outfile);
+ rrd_free(&rrdold);
+ fclose(infile);
+ return(0);
+}
diff --git a/program/src/rrd_restore.c b/program/src/rrd_restore.c
--- /dev/null
@@ -0,0 +1,652 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_restore.c creates new rrd from data dumped by rrd_dump.c
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include <fcntl.h>
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+#include <io.h>
+#define open _open
+#define close _close
+#endif
+
+/* Prototypes */
+
+void xml_lc(char*);
+int skip(char **);
+int skipxml(char **);
+int eat_tag(char **, char *);
+int read_tag(char **, char *, char *, void *);
+int xml2rrd(char*, rrd_t*, char);
+int rrd_write(char *, rrd_t *, char);
+void parse_patch1028_RRA_params(char **buf, rrd_t *rrd, int rra_index);
+void parse_patch1028_CDP_params(char **buf, rrd_t *rrd, int rra_index, int ds_index);
+void parse_FAILURES_history(char **buf, rrd_t *rrd, int rra_index, int ds_index);
+
+/* convert all occurrences of <BlaBlaBla> to <blablabla> */
+
+void xml_lc(char* buf){
+ int intag=0;
+ while((*buf)){
+ if (intag ==0 && (*buf) == '<') {
+ intag = 1;
+ }
+ else if (intag ==1 && (*buf) == '>') {
+ intag = 0;
+ continue;
+ } else if (intag ==1) {
+ *buf = tolower(*buf);
+ }
+ buf++;
+ }
+}
+
+int skipxml(char **buf){
+ char *ptr;
+ ptr=(*buf);
+ do {
+ (*buf)=ptr;
+ while((*(ptr+1)) && ((*ptr)==' ' || (*ptr)=='\r' || (*ptr)=='\n' || (*ptr)=='\t')) ptr++;
+ if (strncmp(ptr,"<?xml",4) == 0) {
+ ptr= strstr(ptr,"?>");
+ if (ptr) ptr+=2; else {
+ rrd_set_error("Dangling XML header");
+ (*buf) = NULL;
+ return -1;
+ }
+ }
+ } while ((*buf)!=ptr);
+ return 1;
+}
+
+int skip(char **buf){
+ char *ptr;
+ if ((buf == NULL) || (*buf == NULL))
+ return -1;
+ ptr=(*buf);
+ do {
+ (*buf)=ptr;
+ while((*(ptr+1)) && ((*ptr)==' ' || (*ptr)=='\r' || (*ptr)=='\n' || (*ptr)=='\t')) ptr++;
+ if (strncmp(ptr,"<!--",4) == 0) {
+ ptr= strstr(ptr,"-->");
+ if (ptr) ptr+=3; else {
+ rrd_set_error("Dangling Comment");
+ (*buf) = NULL;
+ return -1;
+ }
+ }
+ } while ((*buf)!=ptr);
+ return 1;
+}
+
+int eat_tag(char **buf, char *tag){
+ if ((*buf)==NULL) return -1; /* fall though clause */
+
+ rrd_clear_error();
+ skip(buf);
+ if ((**buf)=='<'
+ && strncmp((*buf)+1,tag,strlen(tag)) == 0
+ && *((*buf)+strlen(tag)+1)=='>') {
+ (*buf) += strlen(tag)+2;
+ }
+ else {
+ rrd_set_error("No <%s> tag found",tag);
+ (*buf) = NULL;
+ return -1;
+ }
+ skip(buf);
+ return 1;
+}
+
+int read_tag(char **buf, char *tag, char *format, void *value){
+ char *end_tag;
+ int matches;
+ if ((*buf)==NULL) return -1; /* fall though clause */
+ rrd_clear_error();
+ if (eat_tag(buf,tag)==1){
+ char *temp;
+ temp = (*buf);
+ while(*((*buf)+1) && (*(*buf) != '<')) (*buf)++; /*find start of endtag*/
+ *(*buf) = '\0';
+ matches =sscanf(temp,format,value);
+ *(*buf) = '<';
+ end_tag = malloc((strlen(tag)+2)*sizeof(char));
+ sprintf(end_tag,"/%s",tag);
+ eat_tag(buf,end_tag);
+ free(end_tag);
+ if (matches == 0 && strcmp(format,"%lf") == 0)
+ (*((double* )(value))) = DNAN;
+ if (matches != 1) return 0;
+ return 1;
+ }
+ return -1;
+}
+
+
+/* parse the data stored in buf and return a filled rrd structure */
+int xml2rrd(char* buf, rrd_t* rrd, char rc){
+ /* pass 1 identify number of RRAs */
+ char *ptr,*ptr2,*ptr3; /* walks thought the buffer */
+ long rows=0,mempool=0,i=0;
+ int rra_index;
+ int input_version;
+ xml_lc(buf); /* lets lowercase all active parts of the xml */
+ ptr=buf;
+ ptr2=buf;
+ ptr3=buf;
+ /* start with an RRD tag */
+
+ skipxml(&ptr);
+
+ eat_tag(&ptr,"rrd");
+ /* allocate static header */
+ if((rrd->stat_head = calloc(1,sizeof(stat_head_t)))==NULL){
+ rrd_set_error("allocating rrd.stat_head");
+ return -1;
+ };
+
+ strcpy(rrd->stat_head->cookie,RRD_COOKIE);
+ read_tag(&ptr,"version","%4[0-9]",rrd->stat_head->version);
+ input_version = atoi(rrd->stat_head->version);
+ /* added primitive version checking */
+ if (input_version > atoi(RRD_VERSION) || input_version < 1)
+ {
+ rrd_set_error("Incompatible file version, detected version %s. This is not supported by the version %s restore tool.\n",
+ rrd -> stat_head -> version, RRD_VERSION );
+ free(rrd -> stat_head);
+ rrd->stat_head = NULL;
+ return -1;
+ }
+ /* make sure we output the right version */
+ strcpy(rrd->stat_head->version,RRD_VERSION);
+
+ /* if (atoi(rrd -> stat_head -> version) < 2)
+ {
+ rrd_set_error("Can only restore version >= 2 (Not %s). Dump your old rrd using a current rrdtool dump.", rrd -> stat_head -> version );
+ return -1;
+ } */
+
+ rrd->stat_head->float_cookie = FLOAT_COOKIE;
+ rrd->stat_head->ds_cnt = 0;
+ rrd->stat_head->rra_cnt = 0;
+ read_tag(&ptr,"step","%lu",&(rrd->stat_head->pdp_step));
+
+ /* allocate live head */
+ if((rrd->live_head = calloc(1,sizeof(live_head_t)))==NULL){
+ rrd_set_error("allocating rrd.live_head");
+ return -1;
+ }
+ read_tag(&ptr,"lastupdate","%lu",&(rrd->live_head->last_up));
+
+ /* Data Source Definition Part */
+ ptr2 = ptr;
+ while (eat_tag(&ptr2,"ds") == 1){
+ rrd->stat_head->ds_cnt++;
+ if((rrd->ds_def = rrd_realloc(rrd->ds_def,rrd->stat_head->ds_cnt*sizeof(ds_def_t)))==NULL){
+ rrd_set_error("allocating rrd.ds_def");
+ return -1;
+ };
+ /* clean out memory to make sure no data gets stored from previous tasks */
+ memset(&(rrd->ds_def[rrd->stat_head->ds_cnt-1]), 0, sizeof(ds_def_t));
+ if((rrd->pdp_prep = rrd_realloc(rrd->pdp_prep,rrd->stat_head->ds_cnt
+ *sizeof(pdp_prep_t)))==NULL){
+ rrd_set_error("allocating pdp_prep");
+ return(-1);
+ }
+ /* clean out memory to make sure no data gets stored from previous tasks */
+ memset(&(rrd->pdp_prep[rrd->stat_head->ds_cnt-1]), 0, sizeof(pdp_prep_t));
+
+ read_tag(&ptr2,"name",DS_NAM_FMT,rrd->ds_def[rrd->stat_head->ds_cnt-1].ds_nam);
+
+ read_tag(&ptr2,"type",DST_FMT,rrd->ds_def[rrd->stat_head->ds_cnt-1].dst);
+ /* test for valid type */
+ if( (int)dst_conv(rrd->ds_def[rrd->stat_head->ds_cnt-1].dst) == -1) return -1;
+
+ if (dst_conv(rrd->ds_def[rrd->stat_head->ds_cnt-1].dst) != DST_CDEF)
+ {
+ read_tag(&ptr2,"minimal_heartbeat","%lu",
+ &(rrd->ds_def[rrd->stat_head->ds_cnt-1].par[DS_mrhb_cnt].u_cnt));
+ read_tag(&ptr2,"min","%lf",&(rrd->ds_def[rrd->stat_head->ds_cnt-1].par[DS_min_val].u_val));
+ read_tag(&ptr2,"max","%lf",&(rrd->ds_def[rrd->stat_head->ds_cnt-1].par[DS_max_val].u_val));
+ } else { /* DST_CDEF */
+ char buffer[1024];
+ read_tag(&ptr2,"cdef","%1000s",buffer);
+ parseCDEF_DS(buffer,rrd,rrd -> stat_head -> ds_cnt - 1);
+ if (rrd_test_error()) return -1;
+ }
+
+ read_tag(&ptr2,"last_ds","%30s",rrd->pdp_prep[rrd->stat_head->ds_cnt-1].last_ds);
+ read_tag(&ptr2,"value","%lf",&(rrd->pdp_prep[rrd->stat_head->ds_cnt-1].scratch[PDP_val].u_val));
+ read_tag(&ptr2,"unknown_sec","%lu",&(rrd->pdp_prep[rrd->stat_head->ds_cnt-1].scratch[PDP_unkn_sec_cnt].u_cnt));
+ eat_tag(&ptr2,"/ds");
+ ptr=ptr2;
+ }
+
+ ptr2 = ptr;
+ while (eat_tag(&ptr2,"rra") == 1){
+ rrd->stat_head->rra_cnt++;
+
+ /* allocate and reset rra definition areas */
+ if((rrd->rra_def = rrd_realloc(rrd->rra_def,rrd->stat_head->rra_cnt*sizeof(rra_def_t)))==NULL){
+ rrd_set_error("allocating rra_def"); return -1; }
+ memset(&(rrd->rra_def[rrd->stat_head->rra_cnt-1]), 0, sizeof(rra_def_t));
+
+ /* allocate and reset consolidation point areas */
+ if((rrd->cdp_prep = rrd_realloc(rrd->cdp_prep,
+ rrd->stat_head->rra_cnt
+ *rrd->stat_head->ds_cnt*sizeof(cdp_prep_t)))==NULL){
+ rrd_set_error("allocating cdp_prep"); return -1; }
+
+ memset(&(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rrd->stat_head->rra_cnt-1)]),
+ 0, rrd->stat_head->ds_cnt*sizeof(cdp_prep_t));
+
+
+ read_tag(&ptr2,"cf",CF_NAM_FMT,rrd->rra_def[rrd->stat_head->rra_cnt-1].cf_nam);
+ /* test for valid type */
+ if( (int)cf_conv(rrd->rra_def[rrd->stat_head->rra_cnt-1].cf_nam) == -1) return -1;
+
+ read_tag(&ptr2,"pdp_per_row","%lu",&(rrd->rra_def[rrd->stat_head->rra_cnt-1].pdp_cnt));
+ /* support to read RRA parameters */
+ rra_index = rrd->stat_head->rra_cnt - 1;
+ if ( input_version < 2 ){
+ read_tag(&ptr2, "xff","%lf",
+ &(rrd->rra_def[rra_index].par[RRA_cdp_xff_val].u_val));
+ } else {
+ if (eat_tag(&ptr2, "params") != 1) {
+ rrd_set_error("could not find params tag to eat and skip");
+ return -1;
+ }
+ skip(&ptr2);
+ /* backwards compatibility w/ old patch */
+ if (strncmp(ptr2, "<value>",7) == 0) {
+ parse_patch1028_RRA_params(&ptr2,rrd,rra_index);
+ } else {
+ switch(cf_conv(rrd -> rra_def[rra_index].cf_nam)) {
+ case CF_HWPREDICT:
+ read_tag(&ptr2, "hw_alpha", "%lf",
+ &(rrd->rra_def[rra_index].par[RRA_hw_alpha].u_val));
+ read_tag(&ptr2, "hw_beta", "%lf",
+ &(rrd->rra_def[rra_index].par[RRA_hw_beta].u_val));
+ read_tag(&ptr2, "dependent_rra_idx", "%lu",
+ &(rrd->rra_def[rra_index].par[RRA_dependent_rra_idx].u_cnt));
+ break;
+ case CF_SEASONAL:
+ case CF_DEVSEASONAL:
+ read_tag(&ptr2, "seasonal_gamma", "%lf",
+ &(rrd->rra_def[rra_index].par[RRA_seasonal_gamma].u_val));
+ read_tag(&ptr2, "seasonal_smooth_idx", "%lu",
+ &(rrd->rra_def[rra_index].par[RRA_seasonal_smooth_idx].u_cnt));
+ read_tag(&ptr2, "dependent_rra_idx", "%lu",
+ &(rrd->rra_def[rra_index].par[RRA_dependent_rra_idx].u_cnt));
+ break;
+ case CF_FAILURES:
+ read_tag(&ptr2, "delta_pos", "%lf",
+ &(rrd->rra_def[rra_index].par[RRA_delta_pos].u_val));
+ read_tag(&ptr2, "delta_neg", "%lf",
+ &(rrd->rra_def[rra_index].par[RRA_delta_neg].u_val));
+ read_tag(&ptr2, "window_len", "%lu",
+ &(rrd->rra_def[rra_index].par[RRA_window_len].u_cnt));
+ read_tag(&ptr2, "failure_threshold", "%lu",
+ &(rrd->rra_def[rra_index].par[RRA_failure_threshold].u_cnt));
+ /* fall thru */
+ case CF_DEVPREDICT:
+ read_tag(&ptr2, "dependent_rra_idx", "%lu",
+ &(rrd->rra_def[rra_index].par[RRA_dependent_rra_idx].u_cnt));
+ break;
+ case CF_AVERAGE:
+ case CF_MAXIMUM:
+ case CF_MINIMUM:
+ case CF_LAST:
+ default:
+ read_tag(&ptr2, "xff","%lf",
+ &(rrd->rra_def[rra_index].par[RRA_cdp_xff_val].u_val));
+ }
+ }
+ eat_tag(&ptr2, "/params");
+ }
+
+
+ eat_tag(&ptr2,"cdp_prep");
+ for(i=0;i< (int)rrd->stat_head->ds_cnt;i++)
+ {
+ eat_tag(&ptr2,"ds");
+ /* support to read CDP parameters */
+ rra_index = rrd->stat_head->rra_cnt-1;
+ skip(&ptr2);
+ if ( input_version < 2 ){
+ rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)+i].scratch[CDP_primary_val].u_val = 0.0;
+ rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)+i].scratch[CDP_secondary_val].u_val = 0.0;
+ read_tag(&ptr2,"value","%lf",&(rrd->cdp_prep[rrd->stat_head->ds_cnt
+ *(rra_index) +i].scratch[CDP_val].u_val));
+ read_tag(&ptr2,"unknown_datapoints","%lu",&(rrd->cdp_prep[rrd->stat_head->ds_cnt
+ *(rra_index) +i].scratch[CDP_unkn_pdp_cnt].u_cnt));
+ } else {
+
+ if (strncmp(ptr2, "<value>",7) == 0) {
+ parse_patch1028_CDP_params(&ptr2,rrd,rra_index,i);
+ } else {
+ read_tag(&ptr2, "primary_value","%lf",
+ &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+ +i].scratch[CDP_primary_val].u_val));
+ read_tag(&ptr2, "secondary_value","%lf",
+ &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+ +i].scratch[CDP_secondary_val].u_val));
+ switch(cf_conv(rrd->rra_def[rra_index].cf_nam)) {
+ case CF_HWPREDICT:
+ read_tag(&ptr2,"intercept","%lf",
+ &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+ +i].scratch[CDP_hw_intercept].u_val));
+ read_tag(&ptr2,"last_intercept","%lf",
+ &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+ +i].scratch[CDP_hw_last_intercept].u_val));
+ read_tag(&ptr2,"slope","%lf",
+ &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+ +i].scratch[CDP_hw_slope].u_val));
+ read_tag(&ptr2,"last_slope","%lf",
+ &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+ +i].scratch[CDP_hw_last_slope].u_val));
+ read_tag(&ptr2,"nan_count","%lu",
+ &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+ +i].scratch[CDP_null_count].u_cnt));
+ read_tag(&ptr2,"last_nan_count","%lu",
+ &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+ +i].scratch[CDP_last_null_count].u_cnt));
+ break;
+ case CF_SEASONAL:
+ case CF_DEVSEASONAL:
+ read_tag(&ptr2,"seasonal","%lf",
+ &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+ +i].scratch[CDP_hw_seasonal].u_val));
+ read_tag(&ptr2,"last_seasonal","%lf",
+ &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+ +i].scratch[CDP_hw_last_seasonal].u_val));
+ read_tag(&ptr2,"init_flag","%lu",
+ &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+ +i].scratch[CDP_init_seasonal].u_cnt));
+ break;
+ case CF_DEVPREDICT:
+ break;
+ case CF_FAILURES:
+ parse_FAILURES_history(&ptr2,rrd,rra_index,i);
+ break;
+ case CF_AVERAGE:
+ case CF_MAXIMUM:
+ case CF_MINIMUM:
+ case CF_LAST:
+ default:
+ read_tag(&ptr2,"value","%lf",&(rrd->cdp_prep[rrd->stat_head->ds_cnt
+ *(rra_index) +i].scratch[CDP_val].u_val));
+ read_tag(&ptr2,"unknown_datapoints","%lu",&(rrd->cdp_prep[rrd->stat_head->ds_cnt
+ *(rra_index) +i].scratch[CDP_unkn_pdp_cnt].u_cnt));
+ break;
+ }
+ }
+ }
+ eat_tag(&ptr2,"/ds");
+ }
+ eat_tag(&ptr2,"/cdp_prep");
+ rrd->rra_def[rrd->stat_head->rra_cnt-1].row_cnt=0;
+ eat_tag(&ptr2,"database");
+ ptr3 = ptr2;
+ while (eat_tag(&ptr3,"row") == 1){
+
+ if(mempool==0){
+ mempool = 1000;
+ if((rrd->rrd_value = rrd_realloc(rrd->rrd_value,
+ (rows+mempool)*(rrd->stat_head->ds_cnt)
+ *sizeof(rrd_value_t)))==NULL) {
+ rrd_set_error("allocating rrd_values"); return -1; }
+ }
+ rows++;
+ mempool--;
+ rrd->rra_def[rrd->stat_head->rra_cnt-1].row_cnt++;
+ for(i=0;i< (int)rrd->stat_head->ds_cnt;i++){
+
+ rrd_value_t * value = &(rrd->rrd_value[(rows-1)*rrd->stat_head->ds_cnt+i]);
+
+ read_tag(&ptr3,"v","%lf", value);
+
+ if (
+ (rc == 1) /* do we have to check for the ranges */
+ &&
+ (!isnan(*value)) /* not a NAN value */
+ &&
+ (dst_conv(rrd->ds_def[i].dst) != DST_CDEF)
+ &&
+ ( /* min defined and in the range ? */
+ (!isnan(rrd->ds_def[i].par[DS_min_val].u_val)
+ && (*value < rrd->ds_def[i].par[DS_min_val].u_val))
+ || /* max defined and in the range ? */
+ (!isnan(rrd->ds_def[i].par[DS_max_val].u_val)
+ && (*value > rrd->ds_def[i].par[DS_max_val].u_val))
+ )
+ ) {
+ fprintf (stderr, "out of range found [ds: %lu], [value : %0.10e]\n", i, *value);
+ *value = DNAN;
+ }
+ }
+ eat_tag(&ptr3,"/row");
+ ptr2=ptr3;
+ }
+ eat_tag(&ptr2,"/database");
+ eat_tag(&ptr2,"/rra");
+ ptr=ptr2;
+ }
+ eat_tag(&ptr,"/rrd");
+
+ if((rrd->rra_ptr = calloc(1,sizeof(rra_ptr_t)*rrd->stat_head->rra_cnt)) == NULL) {
+ rrd_set_error("allocating rra_ptr");
+ return(-1);
+ }
+
+ for(i=0; i < (int)rrd->stat_head->rra_cnt; i++) {
+ /* last row in the xml file is the most recent; as
+ * rrd_update increments the current row pointer, set cur_row
+ * here to the last row. */
+ rrd->rra_ptr[i].cur_row = rrd->rra_def[i].row_cnt-1;
+ }
+ if (ptr==NULL)
+ return -1;
+ return 1;
+}
+
+
+
+
+
+/* create and empty rrd file according to the specs given */
+
+int
+rrd_write(char *file_name, rrd_t *rrd, char force_overwrite)
+{
+ unsigned long i,ii,val_cnt;
+ FILE *rrd_file=NULL;
+ int fdflags;
+ int fd;
+
+ if (strcmp("-",file_name)==0){
+ rrd_file= stdout;
+ } else {
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+ fdflags = O_RDWR|O_BINARY|O_CREAT;
+#else
+ fdflags = O_WRONLY|O_CREAT;
+#endif
+ if (force_overwrite == 0) {
+ fdflags |= O_EXCL;
+ }
+ fd = open(file_name,fdflags,0666);
+ if (fd == -1 || (rrd_file = fdopen(fd,"wb")) == NULL) {
+ rrd_set_error("creating '%s': %s",file_name,rrd_strerror(errno));
+ if (fd != -1)
+ close(fd);
+ return(-1);
+ }
+ }
+ fwrite(rrd->stat_head,
+ sizeof(stat_head_t), 1, rrd_file);
+
+ fwrite(rrd->ds_def,
+ sizeof(ds_def_t), rrd->stat_head->ds_cnt, rrd_file);
+
+ fwrite(rrd->rra_def,
+ sizeof(rra_def_t), rrd->stat_head->rra_cnt, rrd_file);
+
+ fwrite(rrd->live_head, sizeof(live_head_t),1, rrd_file);
+
+ fwrite( rrd->pdp_prep, sizeof(pdp_prep_t),rrd->stat_head->ds_cnt,rrd_file);
+
+ fwrite( rrd->cdp_prep, sizeof(cdp_prep_t),rrd->stat_head->rra_cnt*
+ rrd->stat_head->ds_cnt,rrd_file);
+ fwrite( rrd->rra_ptr, sizeof(rra_ptr_t), rrd->stat_head->rra_cnt,rrd_file);
+
+
+
+ /* calculate the number of rrd_values to dump */
+ val_cnt=0;
+ for(i=0; i < rrd->stat_head->rra_cnt; i++)
+ for(ii=0; ii < rrd->rra_def[i].row_cnt * rrd->stat_head->ds_cnt;ii++)
+ val_cnt++;
+ fwrite( rrd->rrd_value, sizeof(rrd_value_t),val_cnt,rrd_file);
+
+ /* lets see if we had an error */
+ if(ferror(rrd_file)){
+ rrd_set_error("a file error occurred while creating '%s'",file_name);
+ fclose(rrd_file);
+ return(-1);
+ }
+
+ fclose(rrd_file);
+ return 0;
+}
+
+
+int
+rrd_restore(int argc, char **argv)
+{
+ rrd_t rrd;
+ char *buf;
+ char rc = 0;
+ char force_overwrite = 0;
+
+ /* init rrd clean */
+ optind = 0; opterr = 0; /* initialize getopt */
+ while (1) {
+ static struct option long_options[] =
+ {
+ {"range-check", no_argument, 0, 'r'},
+ {"force-overwrite", no_argument, 0, 'f'},
+ {0,0,0,0}
+ };
+ int option_index = 0;
+ int opt;
+
+
+ opt = getopt_long(argc, argv, "rf", long_options, &option_index);
+
+ if (opt == EOF)
+ break;
+
+ switch(opt) {
+ case 'r':
+ rc=1;
+ break;
+ case 'f':
+ force_overwrite=1;
+ break;
+ default:
+ rrd_set_error("usage rrdtool %s [--range-check|-r] [--force-overwrite/-f] file.xml file.rrd",argv[0]);
+ return -1;
+ break;
+ }
+ }
+
+ if (argc-optind != 2) {
+ rrd_set_error("usage rrdtool %s [--range-check/-r] [--force-overwrite/-f] file.xml file.rrd",argv[0]);
+ return -1;
+ }
+
+ if (readfile(argv[optind],&buf,0)==-1){
+ return -1;
+ }
+
+ rrd_init(&rrd);
+
+ if (xml2rrd(buf,&rrd,rc)==-1) {
+ rrd_free(&rrd);
+ free(buf);
+ return -1;
+ }
+
+ free(buf);
+
+ if(rrd_write(argv[optind+1],&rrd,force_overwrite)==-1){
+ rrd_free(&rrd);
+ return -1;
+ };
+ rrd_free(&rrd);
+ return 0;
+}
+
+/* a backwards compatibility routine that will parse the RRA params section
+ * generated by the aberrant patch to 1.0.28. */
+
+void
+parse_patch1028_RRA_params(char **buf, rrd_t *rrd, int rra_index)
+{
+ int i;
+ for (i = 0; i < MAX_RRA_PAR_EN; i++)
+ {
+ if (i == RRA_dependent_rra_idx ||
+ i == RRA_seasonal_smooth_idx ||
+ i == RRA_failure_threshold)
+ read_tag(buf, "value","%lu",
+ &(rrd->rra_def[rra_index].par[i].u_cnt));
+ else
+ read_tag(buf, "value","%lf",
+ &(rrd->rra_def[rra_index].par[i].u_val));
+ }
+}
+
+/* a backwards compatibility routine that will parse the CDP params section
+ * generated by the aberrant patch to 1.0.28. */
+void
+parse_patch1028_CDP_params(char **buf, rrd_t *rrd, int rra_index, int ds_index)
+{
+ int ii;
+ for (ii = 0; ii < MAX_CDP_PAR_EN; ii++)
+ {
+ if (cf_conv(rrd->rra_def[rra_index].cf_nam) == CF_FAILURES ||
+ ii == CDP_unkn_pdp_cnt ||
+ ii == CDP_null_count ||
+ ii == CDP_last_null_count)
+ {
+ read_tag(buf,"value","%lu",
+ &(rrd->cdp_prep[rrd->stat_head->ds_cnt*(rra_index) + ds_index].scratch[ii].u_cnt));
+ } else {
+ read_tag(buf,"value","%lf",&(rrd->cdp_prep[rrd->stat_head->ds_cnt*
+ (rra_index) + ds_index].scratch[ii].u_val));
+ }
+ }
+}
+
+void
+parse_FAILURES_history(char **buf, rrd_t *rrd, int rra_index, int ds_index)
+{
+ char history[MAX_FAILURES_WINDOW_LEN + 1];
+ char *violations_array;
+ unsigned short i;
+
+ /* 28 = MAX_FAILURES_WINDOW_LEN */
+ read_tag(buf, "history", "%28[0-1]", history);
+ violations_array = (char*) rrd -> cdp_prep[rrd->stat_head->ds_cnt*(rra_index)
+ + ds_index].scratch;
+
+ for (i = 0; i < rrd -> rra_def[rra_index].par[RRA_window_len].u_cnt; ++i)
+ violations_array[i] = (history[i] == '1') ? 1 : 0;
+
+}
diff --git a/program/src/rrd_rpncalc.c b/program/src/rrd_rpncalc.c
--- /dev/null
@@ -0,0 +1,830 @@
+/****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd_rpncalc.c RPN calculator functions
+ ****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include "rrd_graph.h"
+#include <limits.h>
+
+short addop2str(enum op_en op, enum op_en op_type, char *op_str,
+ char **result_str, unsigned short *offset);
+int tzoffset(time_t); /* used to implement LTIME */
+
+short rpn_compact(rpnp_t *rpnp, rpn_cdefds_t **rpnc, short *count)
+{
+ short i;
+ *count = 0;
+ /* count the number of rpn nodes */
+ while(rpnp[*count].op != OP_END) (*count)++;
+ if (++(*count) > DS_CDEF_MAX_RPN_NODES) {
+ rrd_set_error("Maximum %d RPN nodes permitted",
+ DS_CDEF_MAX_RPN_NODES);
+ return -1;
+ }
+
+ /* allocate memory */
+ *rpnc = (rpn_cdefds_t *) calloc(*count,sizeof(rpn_cdefds_t));
+ for (i = 0; rpnp[i].op != OP_END; i++)
+ {
+ (*rpnc)[i].op = (char) rpnp[i].op;
+ if (rpnp[i].op == OP_NUMBER) {
+ /* rpnp.val is a double, rpnc.val is a short */
+ double temp = floor(rpnp[i].val);
+ if (temp < SHRT_MIN || temp > SHRT_MAX) {
+ rrd_set_error(
+ "constants must be integers in the interval (%d, %d)",
+ SHRT_MIN, SHRT_MAX);
+ free(*rpnc);
+ return -1;
+ }
+ (*rpnc)[i].val = (short) temp;
+ } else if (rpnp[i].op == OP_VARIABLE ||
+ rpnp[i].op == OP_PREV_OTHER) {
+ (*rpnc)[i].val = (short) rpnp[i].ptr;
+ }
+ }
+ /* terminate the sequence */
+ (*rpnc)[(*count) - 1].op = OP_END;
+ return 0;
+}
+
+rpnp_t * rpn_expand(rpn_cdefds_t *rpnc)
+{
+ short i;
+ rpnp_t *rpnp;
+
+ /* DS_CDEF_MAX_RPN_NODES is small, so at the expense of some wasted
+ * memory we avoid any reallocs */
+ rpnp = (rpnp_t *) calloc(DS_CDEF_MAX_RPN_NODES,sizeof(rpnp_t));
+ if (rpnp == NULL) return NULL;
+ for (i = 0; rpnc[i].op != OP_END; ++i)
+ {
+ rpnp[i].op = (long) rpnc[i].op;
+ if (rpnp[i].op == OP_NUMBER) {
+ rpnp[i].val = (double) rpnc[i].val;
+ } else if (rpnp[i].op == OP_VARIABLE ||
+ rpnp[i].op == OP_PREV_OTHER) {
+ rpnp[i].ptr = (long) rpnc[i].val;
+ }
+ }
+ /* terminate the sequence */
+ rpnp[i].op = OP_END;
+ return rpnp;
+}
+
+/* rpn_compact2str: convert a compact sequence of RPN operator nodes back
+ * into a CDEF string. This function is used by rrd_dump.
+ * arguments:
+ * rpnc: an array of compact RPN operator nodes
+ * ds_def: a pointer to the data source definition section of an RRD header
+ * for lookup of data source names by index
+ * str: out string, memory is allocated by the function, must be freed by the
+ * the caller */
+void rpn_compact2str(rpn_cdefds_t *rpnc,ds_def_t *ds_def,char **str)
+{
+ unsigned short i,offset = 0;
+ char buffer[7]; /* short as a string */
+
+ for (i = 0; rpnc[i].op != OP_END; i++)
+ {
+ if (i > 0) (*str)[offset++] = ',';
+
+#define add_op(VV,VVV) \
+ if (addop2str(rpnc[i].op, VV, VVV, str, &offset) == 1) continue;
+
+ if (rpnc[i].op == OP_NUMBER) {
+ /* convert a short into a string */
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+ _itoa(rpnc[i].val,buffer,10);
+#else
+ sprintf(buffer,"%d",rpnc[i].val);
+#endif
+ add_op(OP_NUMBER,buffer)
+ }
+
+ if (rpnc[i].op == OP_VARIABLE) {
+ char *ds_name = ds_def[rpnc[i].val].ds_nam;
+ add_op(OP_VARIABLE, ds_name)
+ }
+
+ if (rpnc[i].op == OP_PREV_OTHER) {
+ char *ds_name = ds_def[rpnc[i].val].ds_nam;
+ add_op(OP_VARIABLE, ds_name)
+ }
+
+#undef add_op
+
+#define add_op(VV,VVV) \
+ if (addop2str(rpnc[i].op, VV, #VVV, str, &offset) == 1) continue;
+
+ add_op(OP_ADD,+)
+ add_op(OP_SUB,-)
+ add_op(OP_MUL,*)
+ add_op(OP_DIV,/)
+ add_op(OP_MOD,%)
+ add_op(OP_SIN,SIN)
+ add_op(OP_COS,COS)
+ add_op(OP_LOG,LOG)
+ add_op(OP_FLOOR,FLOOR)
+ add_op(OP_CEIL,CEIL)
+ add_op(OP_EXP,EXP)
+ add_op(OP_DUP,DUP)
+ add_op(OP_EXC,EXC)
+ add_op(OP_POP,POP)
+ add_op(OP_LT,LT)
+ add_op(OP_LE,LE)
+ add_op(OP_GT,GT)
+ add_op(OP_GE,GE)
+ add_op(OP_EQ,EQ)
+ add_op(OP_IF,IF)
+ add_op(OP_MIN,MIN)
+ add_op(OP_MAX,MAX)
+ add_op(OP_LIMIT,LIMIT)
+ add_op(OP_UNKN,UNKN)
+ add_op(OP_UN,UN)
+ add_op(OP_NEGINF,NEGINF)
+ add_op(OP_NE,NE)
+ add_op(OP_PREV,PREV)
+ add_op(OP_INF,INF)
+ add_op(OP_ISINF,ISINF)
+ add_op(OP_NOW,NOW)
+ add_op(OP_LTIME,LTIME)
+ add_op(OP_TIME,TIME)
+ add_op(OP_ATAN2,ATAN2)
+ add_op(OP_ATAN,ATAN)
+ add_op(OP_SQRT,SQRT)
+ add_op(OP_SORT,SORT)
+ add_op(OP_REV,REV)
+ add_op(OP_TREND,TREND)
+ add_op(OP_RAD2DEG,RAD2DEG)
+ add_op(OP_DEG2RAD,DEG2RAD)
+ add_op(OP_AVG,AVG)
+ add_op(OP_ABS,ABS)
+#undef add_op
+ }
+ (*str)[offset] = '\0';
+
+}
+
+short addop2str(enum op_en op, enum op_en op_type, char *op_str,
+ char **result_str, unsigned short *offset)
+{
+ if (op == op_type) {
+ short op_len;
+ op_len = strlen(op_str);
+ *result_str = (char *) rrd_realloc(*result_str,
+ (op_len + 1 + *offset)*sizeof(char));
+ if (*result_str == NULL) {
+ rrd_set_error("failed to alloc memory in addop2str");
+ return -1;
+ }
+ strncpy(&((*result_str)[*offset]),op_str,op_len);
+ *offset += op_len;
+ return 1;
+ }
+ return 0;
+}
+
+void parseCDEF_DS(const char *def,rrd_t *rrd, int ds_idx)
+{
+ rpnp_t *rpnp = NULL;
+ rpn_cdefds_t *rpnc = NULL;
+ short count, i;
+
+ rpnp = rpn_parse((void*) rrd, def, &lookup_DS);
+ if (rpnp == NULL) {
+ rrd_set_error("failed to parse computed data source");
+ return;
+ }
+ /* Check for OP nodes not permitted in COMPUTE DS.
+ * Moved this check from within rpn_compact() because it really is
+ * COMPUTE DS specific. This is less efficient, but creation doesn't
+ * occur too often. */
+ for (i = 0; rpnp[i].op != OP_END; i++) {
+ if (rpnp[i].op == OP_TIME || rpnp[i].op == OP_LTIME ||
+ rpnp[i].op == OP_PREV || rpnp[i].op == OP_COUNT)
+ {
+ rrd_set_error(
+ "operators time, ltime, prev and count not supported with DS COMPUTE");
+ free(rpnp);
+ return;
+ }
+ }
+ if (rpn_compact(rpnp,&rpnc,&count) == -1) {
+ free(rpnp);
+ return;
+ }
+ /* copy the compact rpn representation over the ds_def par array */
+ memcpy((void*) &(rrd -> ds_def[ds_idx].par[DS_cdef]),
+ (void*) rpnc, count*sizeof(rpn_cdefds_t));
+ free(rpnp);
+ free(rpnc);
+}
+
+/* lookup a data source name in the rrd struct and return the index,
+ * should use ds_match() here except:
+ * (1) need a void * pointer to the rrd
+ * (2) error handling is left to the caller
+ */
+long lookup_DS(void *rrd_vptr,char *ds_name)
+{
+ unsigned int i;
+ rrd_t *rrd;
+
+ rrd = (rrd_t *) rrd_vptr;
+
+ for (i = 0; i < rrd -> stat_head -> ds_cnt; ++i)
+ {
+ if(strcmp(ds_name,rrd -> ds_def[i].ds_nam) == 0)
+ return i;
+ }
+ /* the caller handles a bad data source name in the rpn string */
+ return -1;
+}
+
+/* rpn_parse : parse a string and generate a rpnp array; modified
+ * str2rpn() originally included in rrd_graph.c
+ * arguments:
+ * key_hash: a transparent argument passed to lookup(); conceptually this
+ * is a hash object for lookup of a numeric key given a variable name
+ * expr: the string RPN expression, including variable names
+ * lookup(): a function that retrieves a numeric key given a variable name
+ */
+rpnp_t *
+rpn_parse(void *key_hash,const char *const expr_const,long (*lookup)(void *,char*)){
+ int pos=0;
+ char *expr;
+ long steps=-1;
+ rpnp_t *rpnp;
+ char vname[MAX_VNAME_LEN+10];
+
+ rpnp=NULL;
+ expr=(char *)expr_const;
+
+ while(*expr){
+ if ((rpnp = (rpnp_t *) rrd_realloc(rpnp, (++steps + 2)*
+ sizeof(rpnp_t)))==NULL){
+ return NULL;
+ }
+
+ else if((sscanf(expr,"%lf%n",&rpnp[steps].val,&pos) == 1) && (expr[pos] == ',')){
+ rpnp[steps].op = OP_NUMBER;
+ expr+=pos;
+ }
+
+#define match_op(VV,VVV) \
+ else if (strncmp(expr, #VVV, strlen(#VVV))==0 && ( expr[strlen(#VVV)] == ',' || expr[strlen(#VVV)] == '\0' )){ \
+ rpnp[steps].op = VV; \
+ expr+=strlen(#VVV); \
+ }
+
+
+#define match_op_param(VV,VVV) \
+ else if (sscanf(expr, #VVV "(" DEF_NAM_FMT ")",vname) == 1) { \
+ int length = 0; \
+ if ((length = strlen(#VVV)+strlen(vname)+2, \
+ expr[length] == ',' || expr[length] == '\0') ) { \
+ rpnp[steps].op = VV; \
+ rpnp[steps].ptr = (*lookup)(key_hash,vname); \
+ if (rpnp[steps].ptr < 0) { \
+ free(rpnp); \
+ return NULL; \
+ } else expr+=length; \
+ } \
+ }
+
+ match_op(OP_ADD,+)
+ match_op(OP_SUB,-)
+ match_op(OP_MUL,*)
+ match_op(OP_DIV,/)
+ match_op(OP_MOD,%)
+ match_op(OP_SIN,SIN)
+ match_op(OP_COS,COS)
+ match_op(OP_LOG,LOG)
+ match_op(OP_FLOOR,FLOOR)
+ match_op(OP_CEIL,CEIL)
+ match_op(OP_EXP,EXP)
+ match_op(OP_DUP,DUP)
+ match_op(OP_EXC,EXC)
+ match_op(OP_POP,POP)
+ match_op(OP_LTIME,LTIME)
+ match_op(OP_LT,LT)
+ match_op(OP_LE,LE)
+ match_op(OP_GT,GT)
+ match_op(OP_GE,GE)
+ match_op(OP_EQ,EQ)
+ match_op(OP_IF,IF)
+ match_op(OP_MIN,MIN)
+ match_op(OP_MAX,MAX)
+ match_op(OP_LIMIT,LIMIT)
+ /* order is important here ! .. match longest first */
+ match_op(OP_UNKN,UNKN)
+ match_op(OP_UN,UN)
+ match_op(OP_NEGINF,NEGINF)
+ match_op(OP_NE,NE)
+ match_op(OP_COUNT,COUNT)
+ match_op_param(OP_PREV_OTHER,PREV)
+ match_op(OP_PREV,PREV)
+ match_op(OP_INF,INF)
+ match_op(OP_ISINF,ISINF)
+ match_op(OP_NOW,NOW)
+ match_op(OP_TIME,TIME)
+ match_op(OP_ATAN2,ATAN2)
+ match_op(OP_ATAN,ATAN)
+ match_op(OP_SQRT,SQRT)
+ match_op(OP_SORT,SORT)
+ match_op(OP_REV,REV)
+ match_op(OP_TREND,TREND)
+ match_op(OP_RAD2DEG,RAD2DEG)
+ match_op(OP_DEG2RAD,DEG2RAD)
+ match_op(OP_AVG,AVG)
+ match_op(OP_ABS,ABS)
+#undef match_op
+
+
+ else if ((sscanf(expr, DEF_NAM_FMT "%n",
+ vname,&pos) == 1)
+ && ((rpnp[steps].ptr = (*lookup)(key_hash,vname)) != -1)){
+ rpnp[steps].op = OP_VARIABLE;
+ expr+=pos;
+ }
+
+ else {
+ free(rpnp);
+ return NULL;
+ }
+ if (*expr == 0)
+ break;
+ if (*expr == ',')
+ expr++;
+ else {
+ free(rpnp);
+ return NULL;
+ }
+ }
+ rpnp[steps+1].op = OP_END;
+ return rpnp;
+}
+
+void
+rpnstack_init(rpnstack_t *rpnstack)
+{
+ rpnstack -> s = NULL;
+ rpnstack -> dc_stacksize = 0;
+ rpnstack -> dc_stackblock = 100;
+}
+
+void
+rpnstack_free(rpnstack_t *rpnstack)
+{
+ if (rpnstack -> s != NULL)
+ free(rpnstack -> s);
+ rpnstack -> dc_stacksize = 0;
+}
+
+static int
+rpn_compare_double(const void *x, const void *y)
+{
+ double diff = *((const double *)x) - *((const double *)y);
+
+ return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
+}
+
+/* rpn_calc: run the RPN calculator; also performs variable substitution;
+ * moved and modified from data_calc() originally included in rrd_graph.c
+ * arguments:
+ * rpnp : an array of RPN operators (including variable references)
+ * rpnstack : the initialized stack
+ * data_idx : when data_idx is a multiple of rpnp.step, the rpnp.data pointer
+ * is advanced by rpnp.ds_cnt; used only for variable substitution
+ * output : an array of output values; OP_PREV assumes this array contains
+ * the "previous" value at index position output_idx-1; the definition of
+ * "previous" depends on the calling environment
+ * output_idx : an index into the output array in which to store the output
+ * of the RPN calculator
+ * returns: -1 if the computation failed (also calls rrd_set_error)
+ * 0 on success
+ */
+short
+rpn_calc(rpnp_t *rpnp, rpnstack_t *rpnstack, long data_idx,
+ rrd_value_t *output, int output_idx)
+{
+ int rpi;
+ long stptr = -1;
+
+ /* process each op from the rpn in turn */
+ for (rpi=0; rpnp[rpi].op != OP_END; rpi++){
+ /* allocate or grow the stack */
+ if (stptr + 5 > rpnstack -> dc_stacksize){
+ /* could move this to a separate function */
+ rpnstack -> dc_stacksize += rpnstack -> dc_stackblock;
+ rpnstack -> s = rrd_realloc(rpnstack -> s,
+ (rpnstack -> dc_stacksize)*sizeof(*(rpnstack -> s)));
+ if (rpnstack -> s == NULL){
+ rrd_set_error("RPN stack overflow");
+ return -1;
+ }
+ }
+
+#define stackunderflow(MINSIZE) \
+ if(stptr<MINSIZE){ \
+ rrd_set_error("RPN stack underflow"); \
+ return -1; \
+ }
+
+ switch (rpnp[rpi].op){
+ case OP_NUMBER:
+ rpnstack -> s[++stptr] = rpnp[rpi].val;
+ break;
+ case OP_VARIABLE:
+ case OP_PREV_OTHER:
+ /* Sanity check: VDEFs shouldn't make it here */
+ if (rpnp[rpi].ds_cnt == 0) {
+ rrd_set_error("VDEF made it into rpn_calc... aborting");
+ return -1;
+ } else {
+ /* make sure we pull the correct value from
+ * the *.data array. Adjust the pointer into
+ * the array acordingly. Advance the ptr one
+ * row in the rra (skip over non-relevant
+ * data sources)
+ */
+ if (rpnp[rpi].op == OP_VARIABLE) {
+ rpnstack -> s[++stptr] = *(rpnp[rpi].data);
+ } else {
+ if ((output_idx) <= 0) {
+ rpnstack -> s[++stptr] = DNAN;
+ } else {
+ rpnstack -> s[++stptr] = *(rpnp[rpi].data-rpnp[rpi].ds_cnt);
+ }
+
+ }
+ if (data_idx % rpnp[rpi].step == 0){
+ rpnp[rpi].data += rpnp[rpi].ds_cnt;
+ }
+ }
+ break;
+ case OP_COUNT:
+ rpnstack -> s[++stptr] = (output_idx+1); /* Note: Counter starts at 1 */
+ break;
+ case OP_PREV:
+ if ((output_idx) <= 0) {
+ rpnstack -> s[++stptr] = DNAN;
+ } else {
+ rpnstack -> s[++stptr] = output[output_idx-1];
+ }
+ break;
+ case OP_UNKN:
+ rpnstack -> s[++stptr] = DNAN;
+ break;
+ case OP_INF:
+ rpnstack -> s[++stptr] = DINF;
+ break;
+ case OP_NEGINF:
+ rpnstack -> s[++stptr] = -DINF;
+ break;
+ case OP_NOW:
+ rpnstack -> s[++stptr] = (double)time(NULL);
+ break;
+ case OP_TIME:
+ /* HACK: this relies on the data_idx being the time,
+ ** which the within-function scope is unaware of */
+ rpnstack -> s[++stptr] = (double) data_idx;
+ break;
+ case OP_LTIME:
+ rpnstack -> s[++stptr] =
+ (double) tzoffset(data_idx) + (double)data_idx;
+ break;
+ case OP_ADD:
+ stackunderflow(1);
+ rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1]
+ + rpnstack -> s[stptr];
+ stptr--;
+ break;
+ case OP_SUB:
+ stackunderflow(1);
+ rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1]
+ - rpnstack -> s[stptr];
+ stptr--;
+ break;
+ case OP_MUL:
+ stackunderflow(1);
+ rpnstack -> s[stptr-1] = (rpnstack -> s[stptr-1])
+ * (rpnstack -> s[stptr]);
+ stptr--;
+ break;
+ case OP_DIV:
+ stackunderflow(1);
+ rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1]
+ / rpnstack -> s[stptr];
+ stptr--;
+ break;
+ case OP_MOD:
+ stackunderflow(1);
+ rpnstack -> s[stptr-1]= fmod( rpnstack -> s[stptr-1]
+ ,rpnstack -> s[stptr]);
+ stptr--;
+ break;
+ case OP_SIN:
+ stackunderflow(0);
+ rpnstack -> s[stptr] = sin(rpnstack -> s[stptr]);
+ break;
+ case OP_ATAN:
+ stackunderflow(0);
+ rpnstack -> s[stptr] = atan(rpnstack -> s[stptr]);
+ break;
+ case OP_RAD2DEG:
+ stackunderflow(0);
+ rpnstack -> s[stptr] = 57.29577951 * rpnstack -> s[stptr];
+ break;
+ case OP_DEG2RAD:
+ stackunderflow(0);
+ rpnstack -> s[stptr] = 0.0174532952 * rpnstack -> s[stptr];
+ break;
+ case OP_ATAN2:
+ stackunderflow(1);
+ rpnstack -> s[stptr-1]= atan2(
+ rpnstack -> s[stptr-1],
+ rpnstack -> s[stptr]);
+ stptr--;
+ break;
+ case OP_COS:
+ stackunderflow(0);
+ rpnstack -> s[stptr] = cos(rpnstack -> s[stptr]);
+ break;
+ case OP_CEIL:
+ stackunderflow(0);
+ rpnstack -> s[stptr] = ceil(rpnstack -> s[stptr]);
+ break;
+ case OP_FLOOR:
+ stackunderflow(0);
+ rpnstack -> s[stptr] = floor(rpnstack -> s[stptr]);
+ break;
+ case OP_LOG:
+ stackunderflow(0);
+ rpnstack -> s[stptr] = log(rpnstack -> s[stptr]);
+ break;
+ case OP_DUP:
+ stackunderflow(0);
+ rpnstack -> s[stptr+1] = rpnstack -> s[stptr];
+ stptr++;
+ break;
+ case OP_POP:
+ stackunderflow(0);
+ stptr--;
+ break;
+ case OP_EXC:
+ stackunderflow(1);
+ {
+ double dummy;
+ dummy = rpnstack -> s[stptr] ;
+ rpnstack -> s[stptr] = rpnstack -> s[stptr-1];
+ rpnstack -> s[stptr-1] = dummy;
+ }
+ break;
+ case OP_EXP:
+ stackunderflow(0);
+ rpnstack -> s[stptr] = exp(rpnstack -> s[stptr]);
+ break;
+ case OP_LT:
+ stackunderflow(1);
+ if (isnan(rpnstack -> s[stptr-1]))
+ ;
+ else if (isnan(rpnstack -> s[stptr]))
+ rpnstack -> s[stptr-1] = rpnstack -> s[stptr];
+ else
+ rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] <
+ rpnstack -> s[stptr] ? 1.0 : 0.0;
+ stptr--;
+ break;
+ case OP_LE:
+ stackunderflow(1);
+ if (isnan(rpnstack -> s[stptr-1]))
+ ;
+ else if (isnan(rpnstack -> s[stptr]))
+ rpnstack -> s[stptr-1] = rpnstack -> s[stptr];
+ else
+ rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] <=
+ rpnstack -> s[stptr] ? 1.0 : 0.0;
+ stptr--;
+ break;
+ case OP_GT:
+ stackunderflow(1);
+ if (isnan(rpnstack -> s[stptr-1]))
+ ;
+ else if (isnan(rpnstack -> s[stptr]))
+ rpnstack -> s[stptr-1] = rpnstack -> s[stptr];
+ else
+ rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] >
+ rpnstack -> s[stptr] ? 1.0 : 0.0;
+ stptr--;
+ break;
+ case OP_GE:
+ stackunderflow(1);
+ if (isnan(rpnstack -> s[stptr-1]))
+ ;
+ else if (isnan(rpnstack -> s[stptr]))
+ rpnstack -> s[stptr-1] = rpnstack -> s[stptr];
+ else
+ rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] >=
+ rpnstack -> s[stptr] ? 1.0 : 0.0;
+ stptr--;
+ break;
+ case OP_NE:
+ stackunderflow(1);
+ if (isnan(rpnstack -> s[stptr-1]))
+ ;
+ else if (isnan(rpnstack -> s[stptr]))
+ rpnstack -> s[stptr-1] = rpnstack -> s[stptr];
+ else
+ rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] ==
+ rpnstack -> s[stptr] ? 0.0 : 1.0;
+ stptr--;
+ break;
+ case OP_EQ:
+ stackunderflow(1);
+ if (isnan(rpnstack -> s[stptr-1]))
+ ;
+ else if (isnan(rpnstack -> s[stptr]))
+ rpnstack -> s[stptr-1] = rpnstack -> s[stptr];
+ else
+ rpnstack -> s[stptr-1] = rpnstack -> s[stptr-1] ==
+ rpnstack -> s[stptr] ? 1.0 : 0.0;
+ stptr--;
+ break;
+ case OP_IF:
+ stackunderflow(2);
+ rpnstack->s[stptr-2] = rpnstack->s[stptr-2] != 0.0 ?
+ rpnstack->s[stptr-1] : rpnstack->s[stptr];
+ stptr--;
+ stptr--;
+ break;
+ case OP_MIN:
+ stackunderflow(1);
+ if (isnan(rpnstack->s[stptr-1]))
+ ;
+ else if (isnan(rpnstack->s[stptr]))
+ rpnstack->s[stptr-1] = rpnstack->s[stptr];
+ else if (rpnstack->s[stptr-1] > rpnstack->s[stptr])
+ rpnstack->s[stptr-1] = rpnstack->s[stptr];
+ stptr--;
+ break;
+ case OP_MAX:
+ stackunderflow(1);
+ if (isnan(rpnstack->s[stptr-1]))
+ ;
+ else if (isnan(rpnstack->s[stptr]))
+ rpnstack->s[stptr-1] = rpnstack->s[stptr];
+ else if (rpnstack->s[stptr-1] < rpnstack->s[stptr])
+ rpnstack->s[stptr-1] = rpnstack->s[stptr];
+ stptr--;
+ break;
+ case OP_LIMIT:
+ stackunderflow(2);
+ if (isnan(rpnstack->s[stptr-2]))
+ ;
+ else if (isnan(rpnstack->s[stptr-1]))
+ rpnstack->s[stptr-2] = rpnstack->s[stptr-1];
+ else if (isnan(rpnstack->s[stptr]))
+ rpnstack->s[stptr-2] = rpnstack->s[stptr];
+ else if (rpnstack->s[stptr-2] < rpnstack->s[stptr-1])
+ rpnstack->s[stptr-2] = DNAN;
+ else if (rpnstack->s[stptr-2] > rpnstack->s[stptr])
+ rpnstack->s[stptr-2] = DNAN;
+ stptr-=2;
+ break;
+ case OP_UN:
+ stackunderflow(0);
+ rpnstack->s[stptr] = isnan(rpnstack->s[stptr]) ? 1.0 : 0.0;
+ break;
+ case OP_ISINF:
+ stackunderflow(0);
+ rpnstack->s[stptr] = isinf(rpnstack->s[stptr]) ? 1.0 : 0.0;
+ break;
+ case OP_SQRT:
+ stackunderflow(0);
+ rpnstack -> s[stptr] = sqrt(rpnstack -> s[stptr]);
+ break;
+ case OP_SORT:
+ stackunderflow(0);
+ {
+ int spn = (int)rpnstack -> s[stptr--];
+
+ stackunderflow(spn-1);
+ qsort(rpnstack -> s + stptr-spn+1, spn, sizeof(double),
+ rpn_compare_double);
+ }
+ break;
+ case OP_REV:
+ stackunderflow(0);
+ {
+ int spn = (int)rpnstack -> s[stptr--];
+ double *p, *q;
+
+ stackunderflow(spn-1);
+
+ p = rpnstack -> s + stptr-spn+1;
+ q = rpnstack -> s + stptr;
+ while (p < q) {
+ double x = *q;
+
+ *q-- = *p;
+ *p++ = x;
+ }
+ }
+ break;
+ case OP_TREND:
+ stackunderflow(1);
+ if ((rpi < 2) || (rpnp[rpi-2].op != OP_VARIABLE)) {
+ rrd_set_error("malformed trend arguments");
+ return -1;
+ } else {
+ time_t dur = (time_t)rpnstack -> s[stptr];
+ time_t step = (time_t)rpnp[rpi-2].step;
+
+ if (output_idx > (int)ceil((float)dur / (float)step)) {
+ double accum = 0.0;
+ int i = 0;
+
+ do {
+ accum += rpnp[rpi-2].data[rpnp[rpi-2].ds_cnt * i--];
+ dur -= step;
+ } while (dur > 0);
+
+ rpnstack -> s[--stptr] = (accum / -i);
+ } else
+ rpnstack -> s[--stptr] = DNAN;
+ }
+ break;
+ case OP_AVG:
+ stackunderflow(0);
+ {
+ int i=(int)rpnstack -> s[stptr--];
+ double sum=0;
+ int count=0;
+ stackunderflow(i-1);
+ while(i>0) {
+ double val=rpnstack -> s[stptr--];
+ i--;
+ if (isnan(val)) { continue; }
+ count++;
+ sum+=val;
+ }
+ /* now push the result back on stack */
+ if (count>0) {
+ rpnstack -> s[++stptr]=sum/count;
+ } else {
+ rpnstack -> s[++stptr]=DNAN;
+ }
+ }
+ break;
+ case OP_ABS:
+ stackunderflow(0);
+ rpnstack -> s[stptr] = fabs(rpnstack -> s[stptr]);
+ break;
+ case OP_END:
+ break;
+ }
+#undef stackunderflow
+ }
+ if(stptr!=0){
+ rrd_set_error("RPN final stack size != 1");
+ return -1;
+ }
+
+ output[output_idx] = rpnstack->s[0];
+ return 0;
+}
+
+/* figure out what the local timezone offset for any point in
+ time was. Return it in seconds */
+int
+tzoffset( time_t now ){
+ int gm_sec, gm_min, gm_hour, gm_yday, gm_year,
+ l_sec, l_min, l_hour, l_yday, l_year;
+ struct tm t;
+ int off;
+ gmtime_r(&now, &t);
+ gm_sec = t.tm_sec;
+ gm_min = t.tm_min;
+ gm_hour = t.tm_hour;
+ gm_yday = t.tm_yday;
+ gm_year = t.tm_year;
+ localtime_r(&now, &t);
+ l_sec = t.tm_sec;
+ l_min = t.tm_min;
+ l_hour = t.tm_hour;
+ l_yday = t.tm_yday;
+ l_year = t.tm_year;
+ off = (l_sec-gm_sec)+(l_min-gm_min)*60+(l_hour-gm_hour)*3600;
+ if ( l_yday > gm_yday || l_year > gm_year){
+ off += 24*3600;
+ } else if ( l_yday < gm_yday || l_year < gm_year){
+ off -= 24*3600;
+ }
+ return off;
+}
+
diff --git a/program/src/rrd_rpncalc.h b/program/src/rrd_rpncalc.h
--- /dev/null
@@ -0,0 +1,59 @@
+/****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd_rpncalc.h RPN calculator functions
+ ****************************************************************************/
+#ifndef _RRD_RPNCALC_H
+#define _RRD_RPNCALC_H
+
+/* WARNING: if new operators are added, they MUST be added at the very end of the list.
+ * This is because COMPUTE (CDEF) DS store OP nodes by number (name is not
+ * an option due to limited par array size). OP nodes must have the same
+ * numeric values, otherwise the stored numbers will mean something different. */
+enum op_en {OP_NUMBER=0,OP_VARIABLE,OP_INF,OP_PREV,OP_NEGINF,
+ OP_UNKN,OP_NOW,OP_TIME,OP_ADD,OP_MOD,OP_SUB,OP_MUL,
+ OP_DIV,OP_SIN, OP_DUP, OP_EXC, OP_POP,
+ OP_COS,OP_LOG,OP_EXP,OP_LT,OP_LE,OP_GT,OP_GE,OP_EQ,OP_IF,
+ OP_MIN,OP_MAX,OP_LIMIT, OP_FLOOR, OP_CEIL,
+ OP_UN,OP_END,OP_LTIME,OP_NE,OP_ISINF,OP_PREV_OTHER,OP_COUNT,
+ OP_ATAN,OP_SQRT,OP_SORT,OP_REV,OP_TREND,
+ OP_ATAN2,OP_RAD2DEG,OP_DEG2RAD,
+ OP_AVG,OP_ABS};
+
+typedef struct rpnp_t {
+ enum op_en op;
+ double val; /* value for a OP_NUMBER */
+ long ptr; /* pointer into the gdes array for OP_VAR */
+ double *data; /* pointer to the current value from OP_VAR DAS*/
+ long ds_cnt; /* data source count for data pointer */
+ long step; /* time step for OP_VAR das */
+} rpnp_t;
+
+/* a compact representation of rpnp_t for computed data sources */
+typedef struct rpn_cdefds_t {
+ char op; /* rpn operator type */
+ short val; /* used by OP_NUMBER and OP_VARIABLE */
+} rpn_cdefds_t;
+
+/* limit imposed by sizeof(rpn_cdefs_t) and rrd.ds_def.par */
+#define DS_CDEF_MAX_RPN_NODES 26
+
+typedef struct rpnstack_t {
+ double *s;
+ long dc_stacksize;
+ long dc_stackblock;
+} rpnstack_t;
+
+void rpnstack_init(rpnstack_t *rpnstack);
+void rpnstack_free(rpnstack_t *rpnstack);
+
+void parseCDEF_DS(const char *def, rrd_t *rrd, int ds_idx);
+long lookup_DS(void *rrd_vptr, char *ds_name);
+
+short rpn_compact(rpnp_t *rpnp,rpn_cdefds_t **rpnc,short *count);
+rpnp_t * rpn_expand(rpn_cdefds_t *rpnc);
+void rpn_compact2str(rpn_cdefds_t *rpnc,ds_def_t *ds_def,char **str);
+rpnp_t * rpn_parse(void *key_hash,const char *const expr, long (*lookup)(void *,char *));
+short rpn_calc(rpnp_t *rpnp, rpnstack_t *rpnstack, long data_idx, rrd_value_t *output, int output_idx);
+
+#endif
diff --git a/program/src/rrd_stat.c b/program/src/rrd_stat.c
--- /dev/null
+++ b/program/src/rrd_stat.c
@@ -0,0 +1,145 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_stat Retreive the header part of an RRD
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+
+extern char *tzname[2];
+
+stat_node
+rrd_stat(int argc, char **argv)
+{
+ int i,ii,ix,iii=0;
+ time_t now;
+ char somestring[255];
+ rrd_value_t my_cdp;
+ long rra_base, rra_start, rra_next;
+ FILE *in_file;
+ rrd_t rrd;
+
+
+ if(rrd_open(argv[1],&in_file,&rrd, RRD_READONLY)==-1){
+ return(-1);
+ }
+ puts("<!-- Round Robin Database Dump -->");
+ puts("<rrd>");
+ printf("\t<version> %s </version>\n",rrd.stat_head->version);
+ printf("\t<step> %lu </step> <!-- Seconds -->\n",rrd.stat_head->pdp_step);
+#if HAVE_STRFTIME
+ strftime(somestring,200,"%Y-%m-%d %H:%M:%S %Z",
+ localtime_r(&rrd.live_head->last_up, &tm));
+#else
+# error "Need strftime"
+#endif
+ printf("\t<lastupdate> %ld </lastupdate> <!-- %s -->\n\n",
+ rrd.live_head->last_up,somestring);
+ for(i=0;i<rrd.stat_head->ds_cnt;i++){
+ printf("\t<ds>\n");
+ printf("\t\t<name> %s </name>\n",rrd.ds_def[i].ds_nam);
+ printf("\t\t<type> %s </type>\n",rrd.ds_def[i].dst);
+ printf("\t\t<minimal_heartbeat> %lu </minimal_heartbeat>\n",
+ rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt);
+ if (isnan(rrd.ds_def[i].par[DS_min_val].u_val)){
+ printf("\t\t<min> NaN </min>\n");
+ } else {
+ printf("\t\t<min> %0.10e </min>\n",rrd.ds_def[i].par[DS_min_val].u_val);
+ }
+ if (isnan(rrd.ds_def[i].par[DS_max_val].u_val)){
+ printf("\t\t<max> NaN </max>\n");
+ } else {
+ printf("\t\t<max> %0.10e </max>\n",rrd.ds_def[i].par[DS_max_val].u_val);
+ }
+ printf("\n\t\t<!-- PDP Status -->\n");
+ printf("\t\t<last_ds> %s </last_ds>\n",rrd.pdp_prep[i].last_ds);
+ if (isnan(rrd.pdp_prep[i].scratch[PDP_val].u_val)){
+ printf("\t\t<value> NaN </value>\n");
+ } else {
+ printf("\t\t<value> %0.10e </value>\n",rrd.pdp_prep[i].scratch[PDP_val].u_val);
+ }
+ printf("\t\t<unknown_sec> %lu </unknown_sec>\n",
+ rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
+
+ printf("\t</ds>\n\n");
+ }
+
+ puts("<!-- Round Robin Archives -->");
+
+ rra_base=ftell(in_file);
+ rra_next = rra_base;
+
+ for(i=0;i<rrd.stat_head->rra_cnt;i++){
+
+ long timer=0;
+ rra_start= rra_next;
+ rra_next += ( rrd.stat_head->ds_cnt
+ * rrd.rra_def[i].row_cnt
+ * sizeof(rrd_value_t));
+ printf("\t<rra>\n");
+ printf("\t\t<cf> %s </cf>\n",rrd.rra_def[i].cf_nam);
+ printf("\t\t<pdp_per_row> %lu </pdp_per_row> <!-- %lu seconds -->\n\n",
+ rrd.rra_def[i].pdp_cnt, rrd.rra_def[i].pdp_cnt
+ *rrd.stat_head->pdp_step);
+ printf("\t\t<cdp_prep>\n");
+ for(ii=0;ii<rrd.stat_head->ds_cnt;ii++){
+ double value = rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_val].u_val;
+ printf("\t\t\t<ds>");
+ if (isnan(value)){
+ printf("<value> NaN </value>");
+ } else {
+ printf("<value> %0.10e </value>", value);
+ }
+ printf(" <unknown_datapoints> %lu </unknown_datapoints>",
+ rrd.cdp_prep[i*rrd.stat_head->ds_cnt+ii].scratch[CDP_unkn_pdp_cnt].u_cnt);
+ printf("</ds>\n");
+ }
+ printf("\t\t</cdp_prep>\n");
+
+ printf("\t\t<database>\n");
+ fseek(in_file,(rra_start
+ +(rrd.rra_ptr[i].cur_row+1)
+ * rrd.stat_head->ds_cnt
+ * sizeof(rrd_value_t)),SEEK_SET);
+ timer = - (rrd.rra_def[i].row_cnt-1);
+ ii=rrd.rra_ptr[i].cur_row;
+ for(ix=0;ix<rrd.rra_def[i].row_cnt;ix++){
+ ii++;
+ if (ii>=rrd.rra_def[i].row_cnt) {
+ fseek(in_file,rra_start,SEEK_SET);
+ ii=0; /* wrap if max row cnt is reached */
+ }
+ now = (rrd.live_head->last_up
+ - rrd.live_head->last_up
+ % (rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step))
+ + (timer*rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step);
+
+ timer++;
+#if HAVE_STRFTIME
+ strftime(somestring,200,"%Y-%m-%d %H:%M:%S %Z", localtime_r(&now, &tm));
+#else
+# error "Need strftime"
+#endif
+ printf("\t\t\t<!-- %s --> <row>",somestring);
+ for(iii=0;iii<rrd.stat_head->ds_cnt;iii++){
+ fread(&my_cdp,sizeof(rrd_value_t),1,in_file);
+ if (isnan(my_cdp)){
+ printf("<v> NaN </v>");
+ } else {
+ printf("<v> %0.10e </v>",my_cdp);
+ };
+ }
+ printf("</row>\n");
+ }
+ printf("\t\t</database>\n\t</rra>\n");
+
+ }
+ printf("</rrd>\n");
+ rrd_free(&rrd);
+ fclose(in_file);
+ return(0);
+}
+
+
+
+
diff --git a/program/src/rrd_thread_safe.c b/program/src/rrd_thread_safe.c
--- /dev/null
@@ -0,0 +1,72 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ * This file: Copyright 2003 Peter Stamfest <peter@stamfest.at>
+ * & Tobias Oetiker
+ * Distributed under the GPL
+ *****************************************************************************
+ * rrd_thread_safe.c Contains routines used when thread safety is required
+ *****************************************************************************
+ * $Id$
+ *************************************************************************** */
+
+#include <pthread.h>
+#include <string.h>
+/* #include <error.h> */
+#include "rrd.h"
+#include "rrd_tool.h"
+
+/* Key for the thread-specific rrd_context */
+static pthread_key_t context_key;
+
+/* Once-only initialisation of the key */
+static pthread_once_t context_key_once = PTHREAD_ONCE_INIT;
+
+/* Free the thread-specific rrd_context - we might actually use
+ rrd_free_context instead...
+ */
+static void context_destroy_context(void *ctx_)
+{
+ struct rrd_context *ctx = ctx_;
+ if (ctx) rrd_free_context(ctx);
+}
+
+/* Allocate the key */
+static void context_get_key()
+{
+ pthread_key_create(&context_key, context_destroy_context);
+}
+
+struct rrd_context *rrd_get_context(void) {
+ struct rrd_context *ctx;
+
+ pthread_once(&context_key_once, context_get_key);
+ ctx = pthread_getspecific(context_key);
+ if (!ctx) {
+ ctx = rrd_new_context();
+ pthread_setspecific(context_key, ctx);
+ }
+ return ctx;
+}
+
+#ifdef HAVE_STRERROR_R
+const char *rrd_strerror(int err) {
+ struct rrd_context *ctx = rrd_get_context();
+ if (strerror_r(err, ctx->lib_errstr, ctx->errlen))
+ return "strerror_r faild. sorry!";
+ else
+ return ctx->lib_errstr;
+}
+#else
+#undef strerror
+const char *rrd_strerror(int err) {
+ static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
+ struct rrd_context *ctx;
+ ctx = rrd_get_context();
+ pthread_mutex_lock(&mtx);
+ strncpy(ctx->lib_errstr, strerror(err), ctx->errlen);
+ ctx->lib_errstr[ctx->errlen]='\0';
+ pthread_mutex_unlock(&mtx);
+ return ctx->lib_errstr;
+}
+#endif
+
diff --git a/program/src/rrd_thread_safe_nt.c b/program/src/rrd_thread_safe_nt.c
--- /dev/null
@@ -0,0 +1,142 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ * This file: Copyright 2003 Peter Stamfest <peter@stamfest.at>
+ * & Tobias Oetiker
+ * Distributed under the GPL
+ *****************************************************************************
+ * rrd_thread_safe.c Contains routines used when thread safety is required
+ * for win32
+ *****************************************************************************
+ * $Id$
+ *************************************************************************** */
+
+#include <windows.h>
+#include <string.h>
+/* #include <error.h> */
+#include "rrd.h"
+#include "rrd_tool.h"
+
+/* Key for the thread-specific rrd_context */
+static DWORD context_key;
+static CRITICAL_SECTION CriticalSection;
+
+
+/* Once-only initialisation of the key */
+static DWORD context_key_once = 0;
+
+
+/* Free the thread-specific rrd_context - we might actually use
+ rrd_free_context instead...
+ */
+static void context_destroy_context(void)
+{
+ DeleteCriticalSection(&CriticalSection);
+ TlsFree(context_key);
+ context_key_once=0;
+}
+static void context_init_context(void)
+{
+ if (!InterlockedExchange(&context_key_once, 1)) {
+ context_key = TlsAlloc();
+ InitializeCriticalSection(&CriticalSection);
+ atexit(context_destroy_context);
+ }
+}
+struct rrd_context *rrd_get_context(void) {
+ struct rrd_context *ctx;
+
+ context_init_context();
+
+ ctx = TlsGetValue(context_key);
+ if (!ctx) {
+ ctx = rrd_new_context();
+ TlsSetValue(context_key, ctx);
+ }
+ return ctx;
+}
+#undef strerror
+const char *rrd_strerror(int err) {
+ struct rrd_context *ctx;
+ context_init_context();
+
+ ctx = rrd_get_context();
+
+ EnterCriticalSection(&CriticalSection);
+ strncpy(ctx->lib_errstr, strerror(err), ctx->errlen);
+ ctx->lib_errstr[ctx->errlen] = '\0';
+ LeaveCriticalSection(&CriticalSection);
+
+ return ctx->lib_errstr;
+}
+/*
+ * there much be a re-entrant version of these somewhere in win32 land
+ */
+struct tm* localtime_r(const time_t *timep, struct tm* result)
+{
+ struct tm *local;
+ context_init_context();
+
+ EnterCriticalSection(&CriticalSection);
+ local = localtime(timep);
+ memcpy(result,local,sizeof(struct tm));
+ LeaveCriticalSection(&CriticalSection);
+ return result;
+}
+char* ctime_r(const time_t *timep, char* result)
+{
+ char *local;
+ context_init_context();
+
+ EnterCriticalSection(&CriticalSection);
+ local = ctime(timep);
+ strcpy(result,local);
+ LeaveCriticalSection(&CriticalSection);
+ return result;
+}
+
+struct tm* gmtime_r(const time_t *timep, struct tm* result)
+{
+ struct tm *local;
+ context_init_context();
+
+ EnterCriticalSection(&CriticalSection);
+ local = gmtime(timep);
+ memcpy(result,local,sizeof(struct tm));
+ LeaveCriticalSection(&CriticalSection);
+ return result;
+}
+
+/* implementation from Apache's APR library */
+char *strtok_r(char *str, const char *sep, char **last)
+{
+ char *token;
+ context_init_context();
+
+
+ if (!str) /* subsequent call */
+ str = *last; /* start where we left off */
+
+ /* skip characters in sep (will terminate at '\0') */
+ while (*str && strchr(sep, *str))
+ ++str;
+
+ if (!*str) /* no more tokens */
+ return NULL;
+
+ token = str;
+
+ /* skip valid token characters to terminate token and
+ * prepare for the next call (will terminate at '\0)
+ */
+ *last = token + 1;
+ while (**last && !strchr(sep, **last))
+ ++*last;
+
+ if (**last) {
+ **last = '\0';
+ ++*last;
+ }
+
+ return token;
+}
+
diff --git a/program/src/rrd_tool.c b/program/src/rrd_tool.c
--- /dev/null
+++ b/program/src/rrd_tool.c
@@ -0,0 +1,874 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_tool.c Startup wrapper
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_xport.h"
+
+void PrintUsage(char *cmd);
+int CountArgs(char *aLine);
+int CreateArgs(char *, char *, int, char **);
+int HandleInputLine(int, char **, FILE*);
+int RemoteMode=0;
+int ChangeRoot=0;
+#define TRUE 1
+#define FALSE 0
+#define MAX_LENGTH 10000
+
+
+void PrintUsage(char *cmd)
+{
+
+ char help_main[] =
+ "RRDtool " PACKAGE_VERSION " Copyright 1997-2007 by Tobias Oetiker <tobi@oetiker.ch>\n"
+ " Compiled " __DATE__ " " __TIME__ "\n\n"
+ "Usage: rrdtool [options] command command_options\n\n";
+
+ char help_list[] =
+ "Valid commands: create, update, updatev, graph, dump, restore,\n"
+ "\t\tlast, lastupdate, first, info, fetch, tune,\n"
+ "\t\tresize, xport\n\n";
+
+ char help_listremote[] =
+ "Valid remote commands: quit, ls, cd, mkdir, pwd\n\n";
+
+
+ char help_create[] =
+ "* create - create a new RRD\n\n"
+ "\trrdtool create filename [--start|-b start time]\n"
+ "\t\t[--step|-s step]\n"
+ "\t\t[DS:ds-name:DST:dst arguments]\n"
+ "\t\t[RRA:CF:cf arguments]\n\n";
+
+ char help_dump[] =
+ "* dump - dump an RRD to XML\n\n"
+ "\trrdtool dump filename.rrd >filename.xml\n\n";
+
+ char help_info[] =
+ "* info - returns the configuration and status of the RRD\n\n"
+ "\trrdtool info filename.rrd\n\n";
+
+ char help_restore[] =
+ "* restore - restore an RRD file from its XML form\n\n"
+ "\trrdtool restore [--range-check|-r] [--force-overwrite|-f] filename.xml filename.rrd\n\n";
+
+ char help_last[] =
+ "* last - show last update time for RRD\n\n"
+ "\trrdtool last filename.rrd\n\n";
+
+ char help_lastupdate[] =
+ "* lastupdate - returns the most recent datum stored for\n"
+ " each DS in an RRD\n\n"
+ "\trrdtool lastupdate filename.rrd\n\n";
+
+ char help_first[] =
+ "* first - show first update time for RRA within an RRD\n\n"
+ "\trrdtool first filename.rrd [--rraindex number]\n\n";
+
+ char help_update[] =
+ "* update - update an RRD\n\n"
+ "\trrdtool update filename\n"
+ "\t\t--template|-t ds-name:ds-name:...\n"
+ "\t\ttime|N:value[:value...]\n\n"
+ "\t\tat-time@value[:value...]\n\n"
+ "\t\t[ time:value[:value...] ..]\n\n";
+
+ char help_updatev[] =
+ "* updatev - a verbose version of update\n"
+ "\treturns information about values, RRAs, and datasources updated\n\n"
+ "\trrdtool updatev filename\n"
+ "\t\t--template|-t ds-name:ds-name:...\n"
+ "\t\ttime|N:value[:value...]\n\n"
+ "\t\tat-time@value[:value...]\n\n"
+ "\t\t[ time:value[:value...] ..]\n\n";
+
+ char help_fetch[] =
+ "* fetch - fetch data out of an RRD\n\n"
+ "\trrdtool fetch filename.rrd CF\n"
+ "\t\t[-r|--resolution resolution]\n"
+ "\t\t[-s|--start start] [-e|--end end]\n\n";
+
+/* break up very large strings (help_graph, help_tune) for ISO C89 compliance*/
+
+ char help_graph1[] =
+ "* graph - generate a graph from one or several RRD\n\n"
+ "\trrdtool graph filename [-s|--start seconds] [-e|--end seconds]\n"
+ "\t\t[-x|--x-grid x-axis grid and label]\n"
+ "\t\t[-Y|--alt-y-grid]\n"
+ "\t\t[-y|--y-grid y-axis grid and label]\n"
+ "\t\t[-v|--vertical-label string] [-w|--width pixels]\n"
+ "\t\t[-h|--height pixels] [-o|--logarithmic]\n"
+ "\t\t[-u|--upper-limit value] [-z|--lazy]\n"
+ "\t\t[-l|--lower-limit value] [-r|--rigid]\n"
+ "\t\t[-g|--no-legend]\n"
+ "\t\t[-F|--force-rules-legend]\n"
+ "\t\t[-j|--only-graph]\n";
+ char help_graph2[] =
+ "\t\t[-n|--font FONTTAG:size:font]\n"
+ "\t\t[-m|--zoom factor]\n"
+ "\t\t[-A|--alt-autoscale]\n"
+ "\t\t[-M|--alt-autoscale-max]\n"
+ "\t\t[-R|--font-render-mode {normal,light,mono}]\n"
+ "\t\t[-B|--font-smoothing-threshold size]\n"
+ "\t\t[-E|--slope-mode]\n"
+ "\t\t[-N|--no-gridfit]\n"
+ "\t\t[-X|--units-exponent value]\n"
+ "\t\t[-L|--units-length value]\n"
+ "\t\t[-S|--step seconds]\n"
+ "\t\t[-f|--imginfo printfstr]\n"
+ "\t\t[-a|--imgformat PNG]\n"
+ "\t\t[-c|--color COLORTAG#rrggbb[aa]] [-t|--title string]\n"
+ "\t\t[-W|--watermark string]\n"
+ "\t\t[DEF:vname=rrd:ds-name:CF]\n";
+ char help_graph3[] =
+ "\t\t[CDEF:vname=rpn-expression]\n"
+ "\t\t[VDEF:vdefname=rpn-expression]\n"
+ "\t\t[PRINT:vdefname:format]\n"
+ "\t\t[GPRINT:vdefname:format]\n"
+ "\t\t[COMMENT:text]\n"
+ "\t\t[SHIFT:vname:offset]\n"
+ "\t\t[TICK:vname#rrggbb[aa][:[fraction][:legend]]]\n"
+ "\t\t[HRULE:value#rrggbb[aa][:legend]]\n"
+ "\t\t[VRULE:value#rrggbb[aa][:legend]]\n"
+ "\t\t[LINE[width]:vname[#rrggbb[aa][:[legend][:STACK]]]]\n"
+ "\t\t[AREA:vname[#rrggbb[aa][:[legend][:STACK]]]]\n"
+ "\t\t[PRINT:vname:CF:format] (deprecated)\n"
+ "\t\t[GPRINT:vname:CF:format] (deprecated)\n"
+ "\t\t[STACK:vname[#rrggbb[aa][:legend]]] (deprecated)\n\n";
+
+ char help_tune1[] =
+ " * tune - Modify some basic properties of an RRD\n\n"
+ "\trrdtool tune filename\n"
+ "\t\t[--heartbeat|-h ds-name:heartbeat]\n"
+ "\t\t[--data-source-type|-d ds-name:DST]\n"
+ "\t\t[--data-source-rename|-r old-name:new-name]\n"
+ "\t\t[--minimum|-i ds-name:min] [--maximum|-a ds-name:max]\n"
+ "\t\t[--deltapos scale-value] [--deltaneg scale-value]\n"
+ "\t\t[--failure-threshold integer]\n"
+ "\t\t[--window-length integer]\n"
+ "\t\t[--alpha adaptation-parameter]\n";
+ char help_tune2[] =
+ " * tune - Modify some basic properties of an RRD\n\n"
+ "\t\t[--beta adaptation-parameter]\n"
+ "\t\t[--gamma adaptation-parameter]\n"
+ "\t\t[--gamma-deviation adaptation-parameter]\n"
+ "\t\t[--aberrant-reset ds-name]\n\n";
+
+ char help_resize[] =
+ " * resize - alter the length of one of the RRAs in an RRD\n\n"
+ "\trrdtool resize filename rranum GROW|SHRINK rows\n\n";
+
+ char help_xport[] =
+ "* xport - generate XML dump from one or several RRD\n\n"
+ "\trrdtool xport [-s|--start seconds] [-e|--end seconds]\n"
+ "\t\t[-m|--maxrows rows]\n"
+ "\t\t[--step seconds]\n"
+ "\t\t[--enumds]\n"
+ "\t\t[DEF:vname=rrd:ds-name:CF]\n"
+ "\t\t[CDEF:vname=rpn-expression]\n"
+ "\t\t[XPORT:vname:legend]\n\n";
+
+ char help_quit[] =
+ " * quit - closing a session in remote mode\n\n"
+ "\trrdtool quit\n\n";
+
+ char help_ls[] =
+ " * ls - lists all *.rrd files in current directory\n\n"
+ "\trrdtool ls\n\n";
+
+ char help_cd[] =
+ " * cd - changes the current directory\n\n"
+ "\trrdtool cd new directory\n\n";
+
+ char help_mkdir[] =
+ " * mkdir - creates a new directory\n\n"
+ "\trrdtool mkdir newdirectoryname\n\n";
+
+ char help_pwd[] =
+ " * pwd - returns the current working directory\n\n"
+ "\trrdtool pwd\n\n";
+
+ char help_lic[] =
+ "RRDtool is distributed under the Terms of the GNU General\n"
+ "Public License Version 2. (www.gnu.org/copyleft/gpl.html)\n\n"
+
+ "For more information read the RRD manpages\n\n";
+
+ enum { C_NONE, C_CREATE, C_DUMP, C_INFO, C_RESTORE, C_LAST,
+ C_LASTUPDATE, C_FIRST, C_UPDATE, C_FETCH, C_GRAPH, C_TUNE,
+ C_RESIZE, C_XPORT, C_QUIT, C_LS, C_CD, C_MKDIR, C_PWD,
+ C_UPDATEV };
+
+ int help_cmd = C_NONE;
+
+ if (cmd)
+ {
+ if (!strcmp(cmd,"create"))
+ help_cmd = C_CREATE;
+ else if (!strcmp(cmd,"dump"))
+ help_cmd = C_DUMP;
+ else if (!strcmp(cmd,"info"))
+ help_cmd = C_INFO;
+ else if (!strcmp(cmd,"restore"))
+ help_cmd = C_RESTORE;
+ else if (!strcmp(cmd,"last"))
+ help_cmd = C_LAST;
+ else if (!strcmp(cmd,"lastupdate"))
+ help_cmd = C_LASTUPDATE;
+ else if (!strcmp(cmd,"first"))
+ help_cmd = C_FIRST;
+ else if (!strcmp(cmd,"update"))
+ help_cmd = C_UPDATE;
+ else if (!strcmp(cmd,"updatev"))
+ help_cmd = C_UPDATEV;
+ else if (!strcmp(cmd,"fetch"))
+ help_cmd = C_FETCH;
+ else if (!strcmp(cmd,"graph"))
+ help_cmd = C_GRAPH;
+ else if (!strcmp(cmd,"tune"))
+ help_cmd = C_TUNE;
+ else if (!strcmp(cmd,"resize"))
+ help_cmd = C_RESIZE;
+ else if (!strcmp(cmd,"xport"))
+ help_cmd = C_XPORT;
+ else if (!strcmp(cmd,"quit"))
+ help_cmd = C_QUIT;
+ else if (!strcmp(cmd,"ls"))
+ help_cmd = C_LS;
+ else if (!strcmp(cmd,"cd"))
+ help_cmd = C_CD;
+ else if (!strcmp(cmd,"mkdir"))
+ help_cmd = C_MKDIR;
+ else if (!strcmp(cmd,"pwd"))
+ help_cmd = C_PWD;
+ }
+ fputs(help_main, stdout);
+ switch (help_cmd)
+ {
+ case C_NONE:
+ fputs(help_list, stdout);
+ if (RemoteMode){
+ fputs(help_listremote, stdout);
+ }
+ break;
+ case C_CREATE:
+ fputs(help_create, stdout);
+ break;
+ case C_DUMP:
+ fputs(help_dump, stdout);
+ break;
+ case C_INFO:
+ fputs(help_info, stdout);
+ break;
+ case C_RESTORE:
+ fputs(help_restore, stdout);
+ break;
+ case C_LAST:
+ fputs(help_last, stdout);
+ break;
+ case C_LASTUPDATE:
+ fputs(help_lastupdate, stdout);
+ break;
+ case C_FIRST:
+ fputs(help_first, stdout);
+ break;
+ case C_UPDATE:
+ fputs(help_update, stdout);
+ break;
+ case C_UPDATEV:
+ fputs(help_updatev, stdout);
+ break;
+ case C_FETCH:
+ fputs(help_fetch, stdout);
+ break;
+ case C_GRAPH:
+ fputs(help_graph1, stdout);
+ fputs(help_graph2, stdout);
+ fputs(help_graph3, stdout);
+ break;
+ case C_TUNE:
+ fputs(help_tune1, stdout);
+ fputs(help_tune2, stdout);
+ break;
+ case C_RESIZE:
+ fputs(help_resize, stdout);
+ break;
+ case C_XPORT:
+ fputs(help_xport, stdout);
+ break;
+ case C_QUIT:
+ fputs(help_quit, stdout);
+ break;
+ case C_LS:
+ fputs(help_ls, stdout);
+ break;
+ case C_CD:
+ fputs(help_cd, stdout);
+ break;
+ case C_MKDIR:
+ fputs(help_mkdir, stdout);
+ break;
+ case C_PWD:
+ fputs(help_pwd, stdout);
+ break;
+ }
+ fputs(help_lic, stdout);
+}
+
+static char *fgetslong(char **aLinePtr, FILE *stream)
+{
+ char *linebuf;
+ size_t bufsize = MAX_LENGTH;
+ int eolpos = 0;
+
+ if (feof(stream)) return *aLinePtr = 0;
+ if (!(linebuf = malloc(bufsize))) {
+ perror("fgetslong: malloc");
+ exit(1);
+ }
+ linebuf[0] = '\0';
+ while (fgets(linebuf + eolpos, MAX_LENGTH, stream)) {
+ eolpos += strlen(linebuf + eolpos);
+ if (linebuf[eolpos - 1] == '\n') return *aLinePtr = linebuf;
+ bufsize += MAX_LENGTH;
+ if (!(linebuf = realloc(linebuf, bufsize))) {
+ perror("fgetslong: realloc");
+ exit(1);
+ }
+ }
+ return *aLinePtr = linebuf[0] ? linebuf : 0;
+}
+
+int main(int argc, char *argv[])
+{
+ char **myargv;
+ char *aLine;
+ char *firstdir="";
+#ifdef MUST_DISABLE_SIGFPE
+ signal(SIGFPE,SIG_IGN);
+#endif
+#ifdef MUST_DISABLE_FPMASK
+ fpsetmask(0);
+#endif
+ if (argc == 1)
+ {
+ PrintUsage("");
+ return 0;
+ }
+
+ if (((argc == 2)||(argc == 3)) && !strcmp("-",argv[1]))
+ {
+#if HAVE_GETRUSAGE
+ struct rusage myusage;
+ struct timeval starttime;
+ struct timeval currenttime;
+
+ gettimeofday(&starttime, NULL);
+#endif
+ RemoteMode=1;
+ if ((argc == 3) && strcmp("",argv[2])){
+
+ if (
+#ifdef HAVE_GETUID
+ getuid()
+#else
+ 1
+#endif
+ == 0 ){
+
+#ifdef HAVE_CHROOT
+ chroot(argv[2]);
+ if (errno!=0){
+ fprintf(stderr,"ERROR: can't change root to '%s' errno=%d\n",
+ argv[2],errno);
+ exit(errno);
+ }
+ ChangeRoot=1;
+ firstdir="/";
+#else
+ fprintf(stderr,"ERROR: change root is not supported by your OS "
+ "or at least by this copy of rrdtool\n");
+ exit(1);
+#endif
+ } else {
+ firstdir=argv[2];
+ }
+ }
+ if (strcmp(firstdir,"")){
+ chdir(firstdir);
+ if (errno!=0){
+ fprintf(stderr,"ERROR: %s\n",rrd_strerror(errno));
+ exit(errno);
+ }
+ }
+
+ while (fgetslong(&aLine, stdin)){
+ if ((argc = CountArgs(aLine)) == 0) {
+ printf("ERROR: not enough arguments\n");
+ }
+ if ((myargv = (char **) malloc((argc+1) *
+ sizeof(char *))) == NULL) {
+ perror("malloc");
+ exit(1);
+ }
+ if ((argc=CreateArgs(argv[0], aLine, argc, myargv)) < 0) {
+ printf("ERROR: creating arguments\n");
+ } else {
+ int ret = HandleInputLine(argc, myargv, stdout);
+ free(myargv);
+ if (ret == 0){
+
+
+#if HAVE_GETRUSAGE
+ getrusage(RUSAGE_SELF,&myusage);
+ gettimeofday(¤ttime,NULL);
+ printf("OK u:%1.2f s:%1.2f r:%1.2f\n",
+ (double)myusage.ru_utime.tv_sec+
+ (double)myusage.ru_utime.tv_usec/1000000.0,
+ (double)myusage.ru_stime.tv_sec+
+ (double)myusage.ru_stime.tv_usec/1000000.0,
+ (double)(currenttime.tv_sec-starttime.tv_sec)
+ +(double)(currenttime.tv_usec-starttime.tv_usec)
+ /1000000.0);
+#else
+ printf("OK\n");
+
+#endif
+ }
+ }
+ fflush(stdout); /* this is important for pipes to work */
+ free(aLine);
+ }
+ }
+ else if (argc == 2)
+ {
+ PrintUsage(argv[1]);
+ exit(0);
+ }
+ else if (argc == 3 && !strcmp(argv[1],"help"))
+ {
+ PrintUsage(argv[2]);
+ exit(0);
+ }
+ else {
+ exit(HandleInputLine(argc, argv, stderr));
+ }
+ return 0;
+}
+
+/* HandleInputLine is NOT thread safe - due to readdir issues,
+ resolving them portably is not really simple. */
+int HandleInputLine(int argc, char **argv, FILE* out)
+{
+#if defined(HAVE_OPENDIR) && defined (HAVE_READDIR)
+ DIR *curdir; /* to read current dir with ls */
+ struct dirent *dent;
+#endif
+#if defined(HAVE_SYS_STAT_H)
+ struct stat st;
+#endif
+ char* cwd; /* To hold current working dir on call to pwd */
+
+ /* Reset errno to 0 before we start.
+ */
+ errno = 0;
+
+ if (RemoteMode){
+ if (argc>1 && strcmp("quit", argv[1]) == 0){
+ if (argc>2){
+ printf("ERROR: invalid parameter count for quit\n");
+ return(1);
+ }
+ exit(0);
+ }
+#if defined(HAVE_OPENDIR) && defined(HAVE_READDIR) && defined(HAVE_CHDIR)
+ if (argc>1 && strcmp("cd", argv[1]) == 0){
+ if (argc>3){
+ printf("ERROR: invalid parameter count for cd\n");
+ return(1);
+ }
+#if ! defined(HAVE_CHROOT) || ! defined(HAVE_GETUID)
+ if (getuid()==0 && ! ChangeRoot){
+ printf("ERROR: chdir security problem - rrdtool is running as "
+ "root but not chroot!\n");
+ return(1);
+ }
+#endif
+ chdir(argv[2]);
+ if (errno!=0){
+ printf("ERROR: %s\n",rrd_strerror(errno));
+ return(1);
+ }
+ return(0);
+ }
+ if (argc>1 && strcmp("pwd", argv[1]) == 0){
+ if (argc>2){
+ printf("ERROR: invalid parameter count for pwd\n");
+ return(1);
+ }
+ cwd = getcwd(NULL, MAXPATH);
+ if(cwd == NULL) {
+ printf("ERROR: %s\n",rrd_strerror(errno));
+ return(1);
+ }
+ printf("%s\n", cwd);
+ free(cwd);
+ return(0);
+ }
+ if (argc>1 && strcmp("mkdir", argv[1]) == 0){
+ if (argc>3){
+ printf("ERROR: invalid parameter count for mkdir\n");
+ return(1);
+ }
+#if ! defined(HAVE_CHROOT) || ! defined(HAVE_GETUID)
+ if (getuid()==0 && ! ChangeRoot){
+ printf("ERROR: mkdir security problem - rrdtool is running as "
+ "root but not chroot!\n");
+ return(1);
+ }
+#endif
+ mkdir(argv[2],0777);
+ if (errno!=0){
+ printf("ERROR: %s\n",rrd_strerror(errno));
+ return(1);
+ }
+ return(0);
+ }
+ if (argc>1 && strcmp("ls", argv[1]) == 0){
+ if (argc>2){
+ printf("ERROR: invalid parameter count for ls\n");
+ return(1);
+ }
+ if ((curdir=opendir("."))!=NULL){
+ while((dent=readdir(curdir))!=NULL){
+ if (!stat(dent->d_name,&st)){
+ if (S_ISDIR(st.st_mode)){
+ printf("d %s\n",dent->d_name);
+ }
+ if (strlen(dent->d_name)>4 && S_ISREG(st.st_mode)){
+ if (!strcmp(dent->d_name+NAMLEN(dent)-4,".rrd") ||
+ !strcmp(dent->d_name+NAMLEN(dent)-4,".RRD")){
+ printf("- %s\n",dent->d_name);
+ }
+ }
+ }
+ }
+ closedir(curdir);
+ }
+ else{
+ printf("ERROR: %s\n",rrd_strerror(errno));
+ return(errno);
+ }
+ return(0);
+ }
+#endif /* opendir and readdir */
+
+ }
+ if (argc < 3
+ || strcmp("help", argv[1]) == 0
+ || strcmp("--help", argv[1]) == 0
+ || strcmp("-help", argv[1]) == 0
+ || strcmp("-?", argv[1]) == 0
+ || strcmp("-h", argv[1]) == 0 ) {
+ PrintUsage("");
+ return 0;
+ }
+
+ if (strcmp("create", argv[1]) == 0)
+ rrd_create(argc-1, &argv[1]);
+ else if (strcmp("dump", argv[1]) == 0)
+ rrd_dump(argc-1, &argv[1]);
+ else if (strcmp("info", argv[1]) == 0
+ || strcmp("updatev", argv[1]) == 0){
+ info_t *data,*save;
+ if (strcmp("info",argv[1]) == 0)
+ data=rrd_info(argc-1, &argv[1]);
+ else
+ data=rrd_update_v(argc-1, &argv[1]);
+ while (data) {
+ save=data;
+ printf ("%s = ", data->key);
+ free(data->key);
+
+ switch (data->type) {
+ case RD_I_VAL:
+ if (isnan (data->value.u_val))
+ printf("NaN");
+ else
+ printf ("%0.10e", data->value.u_val);
+ break;
+ case RD_I_CNT:
+ printf ("%lu", data->value.u_cnt);
+ break;
+ case RD_I_INT:
+ printf ("%d", data->value.u_int);
+ break;
+ case RD_I_STR:
+ printf ("\"%s\"", data->value.u_str);
+ free(data->value.u_str);
+ break;
+ }
+ data = data->next;
+ free(save);
+ printf ("\n");
+ }
+ free(data);
+ }
+
+ else if (strcmp("--version", argv[1]) == 0 ||
+ strcmp("version", argv[1]) == 0 ||
+ strcmp("v", argv[1]) == 0 ||
+ strcmp("-v", argv[1]) == 0 ||
+ strcmp("-version", argv[1]) == 0 )
+ printf("RRDtool " PACKAGE_VERSION " Copyright by Tobi Oetiker, 1997-2005 (%f)\n",
+ rrd_version());
+ else if (strcmp("restore", argv[1]) == 0)
+ rrd_restore(argc-1, &argv[1]);
+ else if (strcmp("resize", argv[1]) == 0)
+ rrd_resize(argc-1, &argv[1]);
+ else if (strcmp("last", argv[1]) == 0)
+ printf("%ld\n",rrd_last(argc-1, &argv[1]));
+ else if (strcmp("lastupdate", argv[1]) == 0) {
+ time_t last_update;
+ char **ds_namv;
+ char **last_ds;
+ unsigned long ds_cnt,
+ i;
+ if (rrd_lastupdate(argc-1, &argv[1], &last_update,
+ &ds_cnt, &ds_namv, &last_ds) == 0) {
+ for (i=0; i<ds_cnt; i++)
+ printf(" %s", ds_namv[i]);
+ printf("\n\n");
+ printf("%10lu:", last_update);
+ for (i=0; i<ds_cnt; i++) {
+ printf(" %s", last_ds[i]);
+ free(last_ds[i]);
+ free(ds_namv[i]);
+ }
+ printf("\n");
+ free(last_ds);
+ free(ds_namv);
+ }
+ } else if (strcmp("first", argv[1]) == 0)
+ printf("%ld\n",rrd_first(argc-1, &argv[1]));
+ else if (strcmp("update", argv[1]) == 0)
+ rrd_update(argc-1, &argv[1]);
+ else if (strcmp("fetch", argv[1]) == 0) {
+ time_t start,end, ti;
+ unsigned long step, ds_cnt,i,ii;
+ rrd_value_t *data,*datai;
+ char **ds_namv;
+ if (rrd_fetch(argc-1, &argv[1],&start,&end,&step,&ds_cnt,&ds_namv,&data) != -1) {
+ datai=data;
+ printf(" ");
+ for (i = 0; i<ds_cnt;i++)
+ printf("%20s",ds_namv[i]);
+ printf ("\n\n");
+ for (ti = start+step; ti <= end; ti += step){
+ printf("%10lu:", ti);
+ for (ii = 0; ii < ds_cnt; ii++)
+ printf(" %0.10e", *(datai++));
+ printf("\n");
+ }
+ for (i=0;i<ds_cnt;i++)
+ free(ds_namv[i]);
+ free(ds_namv);
+ free (data);
+ }
+ } else if (strcmp("xport", argv[1]) == 0) {
+ int xxsize;
+ unsigned long int j = 0;
+ time_t start,end, ti;
+ unsigned long step, col_cnt,row_cnt;
+ rrd_value_t *data,*ptr;
+ char **legend_v;
+ int enumds = 0;
+ int i;
+ size_t vtag_s = strlen(COL_DATA_TAG) + 10;
+ char *vtag = malloc(vtag_s);
+ for ( i = 2; i < argc; i++){
+ if (strcmp("--enumds", argv[i]) == 0)
+ enumds = 1;
+ }
+
+ if(rrd_xport(argc-1, &argv[1], &xxsize,&start,&end,&step,&col_cnt,&legend_v,&data) != -1) {
+ row_cnt = (end-start)/step;
+ ptr = data;
+ printf("<?xml version=\"1.0\" encoding=\"%s\"?>\n\n", XML_ENCODING);
+ printf("<%s>\n", ROOT_TAG);
+ printf(" <%s>\n", META_TAG);
+ printf(" <%s>%lu</%s>\n", META_START_TAG, start+step, META_START_TAG);
+ printf(" <%s>%lu</%s>\n", META_STEP_TAG, step, META_STEP_TAG);
+ printf(" <%s>%lu</%s>\n", META_END_TAG, end, META_END_TAG);
+ printf(" <%s>%lu</%s>\n", META_ROWS_TAG, row_cnt, META_ROWS_TAG);
+ printf(" <%s>%lu</%s>\n", META_COLS_TAG, col_cnt, META_COLS_TAG);
+ printf(" <%s>\n", LEGEND_TAG);
+ for (j = 0; j < col_cnt; j++) {
+ char *entry = NULL;
+ entry = legend_v[j];
+ printf(" <%s>%s</%s>\n", LEGEND_ENTRY_TAG, entry, LEGEND_ENTRY_TAG);
+ free(entry);
+ }
+ free(legend_v);
+ printf(" </%s>\n", LEGEND_TAG);
+ printf(" </%s>\n", META_TAG);
+ printf(" <%s>\n", DATA_TAG);
+ for (ti = start+step; ti <= end; ti += step) {
+ printf (" <%s>", DATA_ROW_TAG);
+ printf ("<%s>%lu</%s>", COL_TIME_TAG, ti, COL_TIME_TAG);
+ for (j = 0; j < col_cnt; j++) {
+ rrd_value_t newval = DNAN;
+ if (enumds == 1)
+ snprintf(vtag,vtag_s,"%s%lu", COL_DATA_TAG, j);
+ else
+ snprintf(vtag,vtag_s,"%s",COL_DATA_TAG);
+
+ newval = *ptr;
+ if(isnan(newval)){
+ printf("<%s>NaN</%s>", vtag,vtag);
+ } else {
+ printf("<%s>%0.10e</%s>", vtag, newval, vtag);
+ };
+ ptr++;
+ }
+ printf("</%s>\n", DATA_ROW_TAG);
+ }
+ free(data);
+ printf(" </%s>\n", DATA_TAG);
+ printf("</%s>\n", ROOT_TAG);
+ }
+ free(vtag);
+ }
+ else if (strcmp("graph", argv[1]) == 0) {
+ char **calcpr;
+#ifdef notused /*XXX*/
+ const char *imgfile = argv[2]; /* rrd_graph changes argv pointer */
+#endif
+ int xsize, ysize;
+ double ymin,ymax;
+ int i;
+ int tostdout = (strcmp(argv[2],"-") == 0);
+ int imginfo = 0;
+ for (i=2;i<argc;i++){
+ if (strcmp(argv[i],"--imginfo") == 0 || strcmp(argv[i],"-f") == 0){
+ imginfo = 1;
+ break;
+ }
+ }
+ if( rrd_graph(argc-1, &argv[1], &calcpr, &xsize, &ysize, NULL, &ymin, &ymax) != -1 ) {
+ if (!tostdout && !imginfo)
+ printf ("%dx%d\n",xsize,ysize);
+ if (calcpr) {
+ for(i=0;calcpr[i];i++){
+ if (!tostdout)
+ printf("%s\n",calcpr[i]);
+ free(calcpr[i]);
+ }
+ free(calcpr);
+ }
+ }
+
+ } else if (strcmp("tune", argv[1]) == 0)
+ rrd_tune(argc-1, &argv[1]);
+ else {
+ rrd_set_error("unknown function '%s'",argv[1]);
+ }
+ if (rrd_test_error()) {
+ fprintf(out, "ERROR: %s\n",rrd_get_error());
+ rrd_clear_error();
+ return 1;
+ }
+ return(0);
+}
+
+int CountArgs(char *aLine)
+{
+ int i=0;
+ int aCount = 0;
+ int inarg = 0;
+ while (aLine[i] == ' ') i++;
+ while (aLine[i] != 0){
+ if((aLine[i]== ' ') && inarg){
+ inarg = 0;
+ }
+ if((aLine[i]!= ' ') && ! inarg){
+ inarg = 1;
+ aCount++;
+ }
+ i++;
+ }
+ return aCount;
+}
+
+/*
+ * CreateArgs - take a string (aLine) and tokenize
+ */
+int CreateArgs(char *pName, char *aLine, int argc, char **argv)
+{
+ char *getP, *putP;
+ char **pargv = argv;
+ char Quote = 0;
+ int inArg = 0;
+ int len;
+
+ len = strlen(aLine);
+ /* remove trailing space and newlines */
+ while (len && aLine[len] <= ' ') {
+ aLine[len] = 0 ; len--;
+ }
+ /* sikp leading blanks */
+ while (*aLine && *aLine <= ' ') aLine++;
+
+ pargv[0] = pName;
+ argc = 1;
+ getP = aLine;
+ putP = aLine;
+ while (*getP){
+ switch (*getP) {
+ case ' ':
+ if (Quote){
+ *(putP++)=*getP;
+ } else
+ if(inArg) {
+ *(putP++) = 0;
+ inArg = 0;
+ }
+ break;
+ case '"':
+ case '\'':
+ if (Quote != 0) {
+ if (Quote == *getP)
+ Quote = 0;
+ else {
+ *(putP++)=*getP;
+ }
+ } else {
+ if(!inArg){
+ pargv[argc++] = putP;
+ inArg=1;
+ }
+ Quote = *getP;
+ }
+ break;
+ default:
+ if(!inArg){
+ pargv[argc++] = putP;
+ inArg=1;
+ }
+ *(putP++)=*getP;
+ break;
+ }
+ getP++;
+ }
+
+ *putP = '\0';
+
+ if (Quote)
+ return -1;
+ else
+ return argc;
+}
+
+
diff --git a/program/src/rrd_tool.h b/program/src/rrd_tool.h
--- /dev/null
+++ b/program/src/rrd_tool.h
@@ -0,0 +1,203 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_tool.h Common Header File
+ *****************************************************************************/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#ifndef _RRD_TOOL_H
+#define _RRD_TOOL_H
+
+#ifdef HAVE_CONFIG_H
+#include "../rrd_config.h"
+#elif defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+#include "../win32/config.h"
+#endif
+
+#ifdef MUST_DISABLE_SIGFPE
+#include <signal.h>
+#endif
+
+#ifdef MUST_DISABLE_FPMASK
+#include <floatingpoint.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <ctype.h>
+
+#if HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+
+#ifndef MAXPATH
+# define MAXPATH 1024
+#endif
+
+#if HAVE_MATH_H
+# include <math.h>
+#endif
+/* Sorry: don't know autoconf as well how to check the exist of
+ dirent.h ans sys/stat.h
+*/
+
+#if HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+# include <ndir.h>
+# endif
+#endif
+
+#if HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#if HAVE_SYS_TIMES_H
+# include <sys/times.h>
+#endif
+
+
+#if HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+#if (defined(__svr4__) && defined(__sun__))
+/* Solaris headers (pre 2.6) don't have a getrusage prototype.
+ Use this instead. */
+extern int getrusage(int, struct rusage *);
+#endif /* __svr4__ && __sun__ */
+#endif
+
+#include "rrd.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+
+/* Win32 only includes */
+
+#include <float.h> /* for _isnan */
+#include <io.h> /* for chdir */
+
+struct tm* localtime_r(const time_t *timep, struct tm* result);
+char* ctime_r(const time_t *timep, char* result);
+struct tm* gmtime_r(const time_t *timep, struct tm* result);
+char *strtok_r(char *str, const char *sep, char **last);
+
+#else
+
+/* unix-only includes */
+#if !defined isnan && !defined HAVE_ISNAN
+int isnan(double value);
+#endif
+
+#endif
+
+/* local include files -- need to be after the system ones */
+#include "rrd_getopt.h"
+#include "rrd_format.h"
+
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef min
+#define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define DIM(x) (sizeof(x)/sizeof(x[0]))
+
+/* rrd info interface */
+enum info_type { RD_I_VAL=0,
+ RD_I_CNT,
+ RD_I_STR,
+ RD_I_INT };
+
+typedef union infoval {
+ unsigned long u_cnt;
+ rrd_value_t u_val;
+ char *u_str;
+ int u_int;
+} infoval;
+
+typedef struct info_t {
+ char *key;
+ enum info_type type;
+ union infoval value;
+ struct info_t *next;
+} info_t;
+
+info_t *rrd_info(int, char **);
+int rrd_lastupdate(int argc, char **argv, time_t *last_update,
+ unsigned long *ds_cnt, char ***ds_namv, char ***last_ds);
+info_t *rrd_update_v(int, char **);
+char * sprintf_alloc(char *, ...);
+info_t *info_push(info_t *, char *, enum info_type, infoval);
+
+/* HELPER FUNCTIONS */
+
+int PngSize(FILE *, long *, long *);
+
+int rrd_create_fn(const char *file_name, rrd_t *rrd);
+int rrd_fetch_fn(const char *filename, enum cf_en cf_idx,
+ time_t *start,time_t *end,
+ unsigned long *step,
+ unsigned long *ds_cnt,
+ char ***ds_namv,
+ rrd_value_t **data);
+
+void rrd_free(rrd_t *rrd);
+void rrd_freemem(void *mem);
+void rrd_init(rrd_t *rrd);
+
+int rrd_open(const char *file_name, FILE **in_file, rrd_t *rrd, int rdwr);
+int readfile(const char *file, char **buffer, int skipfirst);
+
+#define RRD_READONLY 0
+#define RRD_READWRITE 1
+
+enum cf_en cf_conv(const char *string);
+enum dst_en dst_conv(char *string);
+long ds_match(rrd_t *rrd,char *ds_nam);
+double rrd_diff(char *a, char *b);
+
+ /* rrd_strerror is thread safe, but still it uses a global buffer
+ (but one per thread), thus subsequent calls within a single
+ thread overwrite the same buffer */
+const char *rrd_strerror(int err);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/program/src/rrd_tune.c b/program/src/rrd_tune.c
--- /dev/null
+++ b/program/src/rrd_tune.c
@@ -0,0 +1,406 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * change header parameters of an rrd
+ *****************************************************************************
+ * $Id$
+ * $Log$
+ * Revision 1.6 2004/05/26 22:11:12 oetiker
+ * reduce compiler warnings. Many small fixes. -- Mike Slifcak <slif@bellsouth.net>
+ *
+ * Revision 1.5 2002/02/01 20:34:49 oetiker
+ * fixed version number and date/time
+ *
+ * Revision 1.4 2001/08/22 22:29:07 jake
+ * Contents of this patch:
+ * (1) Adds/revises documentation for rrd tune in rrd_tool.c and pod files.
+ * (2) Moves some initialization code from rrd_create.c to rrd_hw.c.
+ * (3) Adds another pass to smoothing for SEASONAL and DEVSEASONAL RRAs.
+ * This pass computes the coefficients as deviations from an average; the
+ * average is added the baseline coefficient of HWPREDICT. Statistical texts
+ * suggest this to preserve algorithm stability. It will not invalidate
+ * RRD files created and smoothed with the old code.
+ * (4) Adds the aberrant-reset flag to rrd tune. This operation, which is
+ * specified for a single data source, causes the holt-winters algorithm to
+ * forget everthing it has learned and start over.
+ * (5) Fixes a few out-of-date code comments.
+ *
+ * Revision 1.3 2001/03/07 21:21:54 oetiker
+ * complete rewrite of rrdgraph documentation. This also includs info
+ * on upcomming/planned changes to the rrdgraph interface and functionality
+ * -- Alex van den Bogaerdt <alex@slot.hollandcasino.nl>
+ *
+ * Revision 1.2 2001/03/04 13:01:55 oetiker
+ * Aberrant Behavior Detection support. A brief overview added to rrdtool.pod.
+ * Major updates to rrd_update.c, rrd_create.c. Minor update to other core files.
+ * This is backwards compatible! But new files using the Aberrant stuff are not readable
+ * by old rrdtool versions. See http://cricket.sourceforge.net/aberrant/rrd_hw.htm
+ * -- Jake Brutlag <jakeb@corp.webtv.net>
+ *
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include "rrd_rpncalc.h"
+#include "rrd_hw.h"
+
+int set_hwarg(rrd_t *rrd,enum cf_en cf,enum rra_par_en rra_par,char *arg);
+int set_deltaarg(rrd_t *rrd,enum rra_par_en rra_par,char *arg);
+int set_windowarg(rrd_t *rrd,enum rra_par_en,char *arg);
+
+int
+rrd_tune(int argc, char **argv)
+{
+ rrd_t rrd;
+ FILE *rrd_file;
+ int matches;
+ int optcnt = 0;
+ long ds;
+ char ds_nam[DS_NAM_SIZE];
+ char ds_new[DS_NAM_SIZE];
+ long heartbeat;
+ double min;
+ double max;
+ char dst[DST_SIZE];
+ optind = 0; opterr = 0; /* initialize getopt */
+
+
+ if(rrd_open(argv[1],&rrd_file,&rrd, RRD_READWRITE)==-1){
+ return -1;
+ }
+
+
+ while (1){
+ static struct option long_options[] =
+ {
+ {"heartbeat", required_argument, 0, 'h'},
+ {"minimum", required_argument, 0, 'i'},
+ {"maximum", required_argument, 0, 'a'},
+ {"data-source-type", required_argument, 0, 'd'},
+ {"data-source-rename", required_argument, 0, 'r'},
+ /* added parameter tuning options for aberrant behavior detection */
+ {"deltapos",required_argument,0,'p'},
+ {"deltaneg",required_argument,0,'n'},
+ {"window-length",required_argument,0,'w'},
+ {"failure-threshold",required_argument,0,'f'},
+ {"alpha",required_argument,0,'x'},
+ {"beta",required_argument,0,'y'},
+ {"gamma",required_argument,0,'z'},
+ {"gamma-deviation",required_argument,0,'v'},
+ {"aberrant-reset",required_argument,0,'b'},
+ {0,0,0,0}
+ };
+ int option_index = 0;
+ int opt;
+ opt = getopt_long(argc, argv, "h:i:a:d:r:p:n:w:f:x:y:z:v:b:",
+ long_options, &option_index);
+ if (opt == EOF)
+ break;
+
+ optcnt++;
+ switch(opt) {
+ case 'h':
+ if ((matches = sscanf(optarg, DS_NAM_FMT ":%ld",ds_nam,&heartbeat)) != 2){
+ rrd_set_error("invalid arguments for heartbeat");
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return -1;
+ }
+ if ((ds=ds_match(&rrd,ds_nam))==-1){
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return -1;
+ }
+ rrd.ds_def[ds].par[DS_mrhb_cnt].u_cnt = heartbeat;
+ break;
+
+ case 'i':
+ if ((matches = sscanf(optarg,DS_NAM_FMT ":%lf",ds_nam,&min)) <1){
+ rrd_set_error("invalid arguments for minimum ds value");
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return -1;
+ }
+ if ((ds=ds_match(&rrd,ds_nam))==-1){
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return -1;
+ }
+
+ if(matches == 1)
+ min= DNAN;
+ rrd.ds_def[ds].par[DS_min_val].u_val = min;
+ break;
+
+ case 'a':
+ if ((matches = sscanf(optarg, DS_NAM_FMT ":%lf",ds_nam,&max)) <1){
+ rrd_set_error("invalid arguments for maximum ds value");
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return -1;
+ }
+ if ((ds=ds_match(&rrd,ds_nam))==-1){
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return -1;
+ }
+ if(matches == 1)
+ max= DNAN;
+ rrd.ds_def[ds].par[DS_max_val].u_val = max;
+ break;
+
+ case 'd':
+ if ((matches = sscanf(optarg, DS_NAM_FMT ":" DST_FMT ,ds_nam,dst)) != 2){
+ rrd_set_error("invalid arguments for data source type");
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return -1;
+ }
+ if ((ds=ds_match(&rrd,ds_nam))==-1){
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return -1;
+ }
+ if ((int)dst_conv(dst) == -1){
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return -1;
+ }
+ strncpy(rrd.ds_def[ds].dst,dst,DST_SIZE-1);
+ rrd.ds_def[ds].dst[DST_SIZE-1]='\0';
+
+ rrd.pdp_prep[ds].last_ds[0] = 'U';
+ rrd.pdp_prep[ds].last_ds[1] = 'N';
+ rrd.pdp_prep[ds].last_ds[2] = 'K';
+ rrd.pdp_prep[ds].last_ds[3] = 'N';
+ rrd.pdp_prep[ds].last_ds[4] = '\0';
+
+ break;
+ case 'r':
+ if ((matches =
+ sscanf(optarg,DS_NAM_FMT ":" DS_NAM_FMT , ds_nam,ds_new)) != 2){
+ rrd_set_error("invalid arguments for data source type");
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return -1;
+ }
+ if ((ds=ds_match(&rrd,ds_nam))==-1){
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return -1;
+ }
+ strncpy(rrd.ds_def[ds].ds_nam,ds_new,DS_NAM_SIZE-1);
+ rrd.ds_def[ds].ds_nam[DS_NAM_SIZE-1]='\0';
+ break;
+ case 'p':
+ if (set_deltaarg(&rrd,RRA_delta_pos,optarg)) {
+ rrd_free(&rrd);
+ return -1;
+ }
+ break;
+ case 'n':
+ if (set_deltaarg(&rrd,RRA_delta_neg,optarg)) {
+ rrd_free(&rrd);
+ return -1;
+ }
+ break;
+ case 'f':
+ if (set_windowarg(&rrd,RRA_failure_threshold,optarg)) {
+ rrd_free(&rrd);
+ return -1;
+ }
+ break;
+ case 'w':
+ if (set_windowarg(&rrd,RRA_window_len,optarg)) {
+ rrd_free(&rrd);
+ return -1;
+ }
+ break;
+ case 'x':
+ if (set_hwarg(&rrd,CF_HWPREDICT,RRA_hw_alpha,optarg)) {
+ rrd_free(&rrd);
+ return -1;
+ }
+ break;
+ case 'y':
+ if (set_hwarg(&rrd,CF_HWPREDICT,RRA_hw_beta,optarg)) {
+ rrd_free(&rrd);
+ return -1;
+ }
+ break;
+ case 'z':
+ if (set_hwarg(&rrd,CF_SEASONAL,RRA_seasonal_gamma,optarg)) {
+ rrd_free(&rrd);
+ return -1;
+ }
+ break;
+ case 'v':
+ if (set_hwarg(&rrd,CF_DEVSEASONAL,RRA_seasonal_gamma,optarg)) {
+ rrd_free(&rrd);
+ return -1;
+ }
+ break;
+ case 'b':
+ if (sscanf(optarg,DS_NAM_FMT,ds_nam) != 1){
+ rrd_set_error("invalid argument for aberrant-reset");
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return -1;
+ }
+ if ((ds=ds_match(&rrd,ds_nam))==-1){
+ /* ds_match handles it own errors */
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return -1;
+ }
+ reset_aberrant_coefficients(&rrd,rrd_file,(unsigned long) ds);
+ if (rrd_test_error()) {
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return -1;
+ }
+ break;
+ case '?':
+ if (optopt != 0)
+ rrd_set_error("unknown option '%c'", optopt);
+ else
+ rrd_set_error("unknown option '%s'",argv[optind-1]);
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return -1;
+ }
+ }
+ if(optcnt>0){
+
+ fseek(rrd_file,0,SEEK_SET);
+ fwrite(rrd.stat_head,
+ sizeof(stat_head_t),1, rrd_file);
+ fwrite(rrd.ds_def,
+ sizeof(ds_def_t), rrd.stat_head->ds_cnt, rrd_file);
+ /* need to write rra_defs for RRA parameter changes */
+ fwrite(rrd.rra_def, sizeof(rra_def_t), rrd.stat_head->rra_cnt,
+ rrd_file);
+ } else {
+ int i;
+ for(i=0;i< (int)rrd.stat_head->ds_cnt;i++)
+ if (dst_conv(rrd.ds_def[i].dst) != DST_CDEF) {
+ printf("DS[%s] typ: %s\thbt: %ld\tmin: %1.4f\tmax: %1.4f\n",
+ rrd.ds_def[i].ds_nam,
+ rrd.ds_def[i].dst,
+ rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt,
+ rrd.ds_def[i].par[DS_min_val].u_val,
+ rrd.ds_def[i].par[DS_max_val].u_val);
+ } else {
+ char *buffer = NULL;
+ rpn_compact2str((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef]),rrd.ds_def,&buffer);
+ printf("DS[%s] typ: %s\tcdef: %s\n", rrd.ds_def[i].ds_nam,rrd.ds_def[i].dst,buffer);
+ free(buffer);
+ }
+ }
+ fclose(rrd_file);
+ rrd_free(&rrd);
+ return 0;
+}
+
+int set_hwarg(rrd_t *rrd,enum cf_en cf,enum rra_par_en rra_par,char *arg)
+{
+ double param;
+ unsigned long i;
+ signed short rra_idx = -1;
+ /* read the value */
+ param = atof(arg);
+ if (param <= 0.0 || param >= 1.0)
+ {
+ rrd_set_error("Holt-Winters parameter must be between 0 and 1");
+ return -1;
+ }
+ /* does the appropriate RRA exist? */
+ for (i = 0; i < rrd -> stat_head -> rra_cnt; ++i)
+ {
+ if (cf_conv(rrd -> rra_def[i].cf_nam) == cf)
+ {
+ rra_idx = i;
+ break;
+ }
+ }
+ if (rra_idx == -1)
+ {
+ rrd_set_error("Holt-Winters RRA does not exist in this RRD");
+ return -1;
+ }
+
+ /* set the value */
+ rrd -> rra_def[rra_idx].par[rra_par].u_val = param;
+ return 0;
+}
+
+int set_deltaarg(rrd_t *rrd,enum rra_par_en rra_par,char *arg)
+{
+ rrd_value_t param;
+ unsigned long i;
+ signed short rra_idx = -1;
+
+ param = atof(arg);
+ if (param < 0.1)
+ {
+ rrd_set_error("Parameter specified is too small");
+ return -1;
+ }
+ /* does the appropriate RRA exist? */
+ for (i = 0; i < rrd -> stat_head -> rra_cnt; ++i)
+ {
+ if (cf_conv(rrd -> rra_def[i].cf_nam) == CF_FAILURES)
+ {
+ rra_idx = i;
+ break;
+ }
+ }
+ if (rra_idx == -1)
+ {
+ rrd_set_error("Failures RRA does not exist in this RRD");
+ return -1;
+ }
+
+ /* set the value */
+ rrd -> rra_def[rra_idx].par[rra_par].u_val = param;
+ return 0;
+}
+
+int set_windowarg(rrd_t *rrd,enum rra_par_en rra_par,char *arg)
+{
+ unsigned long param;
+ unsigned long i, cdp_idx;
+ signed short rra_idx = -1;
+ /* read the value */
+ param = atoi(arg);
+ if (param < 1 || param > MAX_FAILURES_WINDOW_LEN)
+ {
+ rrd_set_error("Parameter must be between %d and %d",
+ 1, MAX_FAILURES_WINDOW_LEN);
+ return -1;
+ }
+ /* does the appropriate RRA exist? */
+ for (i = 0; i < rrd -> stat_head -> rra_cnt; ++i)
+ {
+ if (cf_conv(rrd -> rra_def[i].cf_nam) == CF_FAILURES)
+ {
+ rra_idx = i;
+ break;
+ }
+ }
+ if (rra_idx == -1)
+ {
+ rrd_set_error("Failures RRA does not exist in this RRD");
+ return -1;
+ }
+
+ /* set the value */
+ rrd -> rra_def[rra_idx].par[rra_par].u_cnt = param;
+
+ /* erase existing violations */
+ for (i = 0; i < rrd -> stat_head -> ds_cnt; i++)
+ {
+ cdp_idx = rra_idx * (rrd -> stat_head -> ds_cnt) + i;
+ erase_violations(rrd,cdp_idx,rra_idx);
+ }
+ return 0;
+}
diff --git a/program/src/rrd_update.c b/program/src/rrd_update.c
--- /dev/null
+++ b/program/src/rrd_update.c
@@ -0,0 +1,1568 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_update.c RRD Update Function
+ *****************************************************************************
+ * $Id$
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+#include <sys/types.h>
+#include <fcntl.h>
+#ifdef HAVE_MMAP
+# include <sys/mman.h>
+#endif
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+ #include <sys/locking.h>
+ #include <sys/stat.h>
+ #include <io.h>
+#endif
+
+#include "rrd_hw.h"
+#include "rrd_rpncalc.h"
+
+#include "rrd_is_thread_safe.h"
+#include "unused.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+/*
+ * WIN32 does not have gettimeofday and struct timeval. This is a quick and dirty
+ * replacement.
+ */
+#include <sys/timeb.h>
+
+#ifndef __MINGW32__
+struct timeval {
+ time_t tv_sec; /* seconds */
+ long tv_usec; /* microseconds */
+};
+#endif
+
+struct __timezone {
+ int tz_minuteswest; /* minutes W of Greenwich */
+ int tz_dsttime; /* type of dst correction */
+};
+
+static int gettimeofday(struct timeval *t, struct __timezone *tz) {
+
+ struct _timeb current_time;
+
+ _ftime(¤t_time);
+
+ t->tv_sec = current_time.time;
+ t->tv_usec = current_time.millitm * 1000;
+
+ return 0;
+}
+
+#endif
+/*
+ * normilize time as returned by gettimeofday. usec part must
+ * be always >= 0
+ */
+static void normalize_time(struct timeval *t)
+{
+ if(t->tv_usec < 0) {
+ t->tv_sec--;
+ t->tv_usec += 1000000L;
+ }
+}
+
+/* Local prototypes */
+int LockRRD(FILE *rrd_file);
+#ifdef HAVE_MMAP
+info_t *write_RRA_row (rrd_t *rrd, unsigned long rra_idx,
+ unsigned long *rra_current,
+ unsigned short CDP_scratch_idx,
+#ifndef DEBUG
+FILE UNUSED(*rrd_file),
+#else
+FILE *rrd_file,
+#endif
+ info_t *pcdp_summary, time_t *rra_time, void *rrd_mmaped_file);
+#else
+info_t *write_RRA_row (rrd_t *rrd, unsigned long rra_idx,
+ unsigned long *rra_current,
+ unsigned short CDP_scratch_idx, FILE *rrd_file,
+ info_t *pcdp_summary, time_t *rra_time);
+#endif
+int rrd_update_r(const char *filename, const char *tmplt, int argc, const char **argv);
+int _rrd_update(const char *filename, const char *tmplt, int argc, const char **argv,
+ info_t*);
+
+#define IFDNAN(X,Y) (isnan(X) ? (Y) : (X));
+
+
+info_t *rrd_update_v(int argc, char **argv)
+{
+ char *tmplt = NULL;
+ info_t *result = NULL;
+ infoval rc;
+ rc.u_int = -1;
+ optind = 0; opterr = 0; /* initialize getopt */
+
+ while (1) {
+ static struct option long_options[] =
+ {
+ {"template", required_argument, 0, 't'},
+ {0,0,0,0}
+ };
+ int option_index = 0;
+ int opt;
+ opt = getopt_long(argc, argv, "t:",
+ long_options, &option_index);
+
+ if (opt == EOF)
+ break;
+
+ switch(opt) {
+ case 't':
+ tmplt = optarg;
+ break;
+
+ case '?':
+ rrd_set_error("unknown option '%s'",argv[optind-1]);
+ goto end_tag;
+ }
+ }
+
+ /* need at least 2 arguments: filename, data. */
+ if (argc-optind < 2) {
+ rrd_set_error("Not enough arguments");
+ goto end_tag;
+ }
+ rc.u_int = 0;
+ result = info_push(NULL,sprintf_alloc("return_value"),RD_I_INT,rc);
+ rc.u_int = _rrd_update(argv[optind], tmplt,
+ argc - optind - 1, (const char **)(argv + optind + 1), result);
+ result->value.u_int = rc.u_int;
+end_tag:
+ return result;
+}
+
+int
+rrd_update(int argc, char **argv)
+{
+ char *tmplt = NULL;
+ int rc;
+ optind = 0; opterr = 0; /* initialize getopt */
+
+ while (1) {
+ static struct option long_options[] =
+ {
+ {"template", required_argument, 0, 't'},
+ {0,0,0,0}
+ };
+ int option_index = 0;
+ int opt;
+ opt = getopt_long(argc, argv, "t:",
+ long_options, &option_index);
+
+ if (opt == EOF)
+ break;
+
+ switch(opt) {
+ case 't':
+ tmplt = optarg;
+ break;
+
+ case '?':
+ rrd_set_error("unknown option '%s'",argv[optind-1]);
+ return(-1);
+ }
+ }
+
+ /* need at least 2 arguments: filename, data. */
+ if (argc-optind < 2) {
+ rrd_set_error("Not enough arguments");
+
+ return -1;
+ }
+
+ rc = rrd_update_r(argv[optind], tmplt,
+ argc - optind - 1, (const char **)(argv + optind + 1));
+ return rc;
+}
+
+int
+rrd_update_r(const char *filename, const char *tmplt, int argc, const char **argv)
+{
+ return _rrd_update(filename, tmplt, argc, argv, NULL);
+}
+
+int
+_rrd_update(const char *filename, const char *tmplt, int argc, const char **argv,
+ info_t *pcdp_summary)
+{
+
+ int arg_i = 2;
+ short j;
+ unsigned long i,ii,iii=1;
+
+ unsigned long rra_begin; /* byte pointer to the rra
+ * area in the rrd file. this
+ * pointer never changes value */
+ unsigned long rra_start; /* byte pointer to the rra
+ * area in the rrd file. this
+ * pointer changes as each rrd is
+ * processed. */
+ unsigned long rra_current; /* byte pointer to the current write
+ * spot in the rrd file. */
+ unsigned long rra_pos_tmp; /* temporary byte pointer. */
+ double interval,
+ pre_int,post_int; /* interval between this and
+ * the last run */
+ unsigned long proc_pdp_st; /* which pdp_st was the last
+ * to be processed */
+ unsigned long occu_pdp_st; /* when was the pdp_st
+ * before the last update
+ * time */
+ unsigned long proc_pdp_age; /* how old was the data in
+ * the pdp prep area when it
+ * was last updated */
+ unsigned long occu_pdp_age; /* how long ago was the last
+ * pdp_step time */
+ rrd_value_t *pdp_new; /* prepare the incoming data
+ * to be added the the
+ * existing entry */
+ rrd_value_t *pdp_temp; /* prepare the pdp values
+ * to be added the the
+ * cdp values */
+
+ long *tmpl_idx; /* index representing the settings
+ transported by the tmplt index */
+ unsigned long tmpl_cnt = 2; /* time and data */
+
+ FILE *rrd_file;
+ rrd_t rrd;
+ time_t current_time = 0;
+ time_t rra_time = 0; /* time of update for a RRA */
+ unsigned long current_time_usec=0;/* microseconds part of current time */
+ struct timeval tmp_time; /* used for time conversion */
+
+ char **updvals;
+ int schedule_smooth = 0;
+ rrd_value_t *seasonal_coef = NULL, *last_seasonal_coef = NULL;
+ /* a vector of future Holt-Winters seasonal coefs */
+ unsigned long elapsed_pdp_st;
+ /* number of elapsed PDP steps since last update */
+ unsigned long *rra_step_cnt = NULL;
+ /* number of rows to be updated in an RRA for a data
+ * value. */
+ unsigned long start_pdp_offset;
+ /* number of PDP steps since the last update that
+ * are assigned to the first CDP to be generated
+ * since the last update. */
+ unsigned short scratch_idx;
+ /* index into the CDP scratch array */
+ enum cf_en current_cf;
+ /* numeric id of the current consolidation function */
+ rpnstack_t rpnstack; /* used for COMPUTE DS */
+ int version; /* rrd version */
+ char *endptr; /* used in the conversion */
+
+#ifdef HAVE_MMAP
+ void *rrd_mmaped_file;
+ unsigned long rrd_filesize;
+#endif
+
+
+ rpnstack_init(&rpnstack);
+
+ /* need at least 1 arguments: data. */
+ if (argc < 1) {
+ rrd_set_error("Not enough arguments");
+ return -1;
+ }
+
+
+
+ if(rrd_open(filename,&rrd_file,&rrd, RRD_READWRITE)==-1){
+ return -1;
+ }
+
+ /* initialize time */
+ version = atoi(rrd.stat_head->version);
+ gettimeofday(&tmp_time, 0);
+ normalize_time(&tmp_time);
+ current_time = tmp_time.tv_sec;
+ if(version >= 3) {
+ current_time_usec = tmp_time.tv_usec;
+ }
+ else {
+ current_time_usec = 0;
+ }
+
+ rra_current = rra_start = rra_begin = ftell(rrd_file);
+ /* This is defined in the ANSI C standard, section 7.9.5.3:
+
+ When a file is opened with udpate mode ('+' as the second
+ or third character in the ... list of mode argument
+ variables), both input and ouptut may be performed on the
+ associated stream. However, ... input may not be directly
+ followed by output without an intervening call to a file
+ positioning function, unless the input oepration encounters
+ end-of-file. */
+#ifdef HAVE_MMAP
+ fseek(rrd_file, 0, SEEK_END);
+ rrd_filesize = ftell(rrd_file);
+ fseek(rrd_file, rra_current, SEEK_SET);
+#else
+ fseek(rrd_file, 0, SEEK_CUR);
+#endif
+
+
+ /* get exclusive lock to whole file.
+ * lock gets removed when we close the file.
+ */
+ if (LockRRD(rrd_file) != 0) {
+ rrd_set_error("could not lock RRD");
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return(-1);
+ }
+
+ if((updvals = malloc( sizeof(char*) * (rrd.stat_head->ds_cnt+1)))==NULL){
+ rrd_set_error("allocating updvals pointer array");
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return(-1);
+ }
+
+ if ((pdp_temp = malloc(sizeof(rrd_value_t)
+ *rrd.stat_head->ds_cnt))==NULL){
+ rrd_set_error("allocating pdp_temp ...");
+ free(updvals);
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return(-1);
+ }
+
+ if ((tmpl_idx = malloc(sizeof(unsigned long)
+ *(rrd.stat_head->ds_cnt+1)))==NULL){
+ rrd_set_error("allocating tmpl_idx ...");
+ free(pdp_temp);
+ free(updvals);
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return(-1);
+ }
+ /* initialize tmplt redirector */
+ /* default config example (assume DS 1 is a CDEF DS)
+ tmpl_idx[0] -> 0; (time)
+ tmpl_idx[1] -> 1; (DS 0)
+ tmpl_idx[2] -> 3; (DS 2)
+ tmpl_idx[3] -> 4; (DS 3) */
+ tmpl_idx[0] = 0; /* time */
+ for (i = 1, ii = 1 ; i <= rrd.stat_head->ds_cnt ; i++)
+ {
+ if (dst_conv(rrd.ds_def[i-1].dst) != DST_CDEF)
+ tmpl_idx[ii++]=i;
+ }
+ tmpl_cnt= ii;
+
+ if (tmplt) {
+ /* we should work on a writeable copy here */
+ char *dsname;
+ unsigned int tmpl_len;
+ char *tmplt_copy = strdup(tmplt);
+ dsname = tmplt_copy;
+ tmpl_cnt = 1; /* the first entry is the time */
+ tmpl_len = strlen(tmplt_copy);
+ for(i=0;i<=tmpl_len ;i++) {
+ if (tmplt_copy[i] == ':' || tmplt_copy[i] == '\0') {
+ tmplt_copy[i] = '\0';
+ if (tmpl_cnt>rrd.stat_head->ds_cnt){
+ rrd_set_error("tmplt contains more DS definitions than RRD");
+ free(updvals); free(pdp_temp);
+ free(tmpl_idx); rrd_free(&rrd);
+ fclose(rrd_file); return(-1);
+ }
+ if ((tmpl_idx[tmpl_cnt++] = ds_match(&rrd,dsname)) == -1){
+ rrd_set_error("unknown DS name '%s'",dsname);
+ free(updvals); free(pdp_temp);
+ free(tmplt_copy);
+ free(tmpl_idx); rrd_free(&rrd);
+ fclose(rrd_file); return(-1);
+ } else {
+ /* the first element is always the time */
+ tmpl_idx[tmpl_cnt-1]++;
+ /* go to the next entry on the tmplt_copy */
+ dsname = &tmplt_copy[i+1];
+ /* fix the damage we did before */
+ if (i<tmpl_len) {
+ tmplt_copy[i]=':';
+ }
+
+ }
+ }
+ }
+ free(tmplt_copy);
+ }
+ if ((pdp_new = malloc(sizeof(rrd_value_t)
+ *rrd.stat_head->ds_cnt))==NULL){
+ rrd_set_error("allocating pdp_new ...");
+ free(updvals);
+ free(pdp_temp);
+ free(tmpl_idx);
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return(-1);
+ }
+
+#ifdef HAVE_MMAP
+ rrd_mmaped_file = mmap(0,
+ rrd_filesize,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ fileno(rrd_file),
+ 0);
+ if (rrd_mmaped_file == MAP_FAILED) {
+ rrd_set_error("error mmapping file %s", filename);
+ free(updvals);
+ free(pdp_temp);
+ free(tmpl_idx);
+ rrd_free(&rrd);
+ fclose(rrd_file);
+ return(-1);
+ }
+#ifdef HAVE_MADVISE
+ /* when we use mmaping we tell the kernel the mmap equivalent
+ of POSIX_FADV_RANDOM */
+ madvise(rrd_mmaped_file,rrd_filesize,POSIX_MADV_RANDOM);
+#endif
+#endif
+ /* loop through the arguments. */
+ for(arg_i=0; arg_i<argc;arg_i++) {
+ char *stepper = strdup(argv[arg_i]);
+ char *step_start = stepper;
+ char *p;
+ char *parsetime_error = NULL;
+ enum {atstyle, normal} timesyntax;
+ struct rrd_time_value ds_tv;
+ if (stepper == NULL){
+ rrd_set_error("failed duplication argv entry");
+ free(step_start);
+ free(updvals);
+ free(pdp_temp);
+ free(tmpl_idx);
+ rrd_free(&rrd);
+#ifdef HAVE_MMAP
+ munmap(rrd_mmaped_file, rrd_filesize);
+#endif
+ fclose(rrd_file);
+ return(-1);
+ }
+ /* initialize all ds input to unknown except the first one
+ which has always got to be set */
+ for(ii=1;ii<=rrd.stat_head->ds_cnt;ii++) updvals[ii] = "U";
+ updvals[0]=stepper;
+ /* separate all ds elements; first must be examined separately
+ due to alternate time syntax */
+ if ((p=strchr(stepper,'@'))!=NULL) {
+ timesyntax = atstyle;
+ *p = '\0';
+ stepper = p+1;
+ } else if ((p=strchr(stepper,':'))!=NULL) {
+ timesyntax = normal;
+ *p = '\0';
+ stepper = p+1;
+ } else {
+ rrd_set_error("expected timestamp not found in data source from %s",
+ argv[arg_i]);
+ free(step_start);
+ break;
+ }
+ ii=1;
+ updvals[tmpl_idx[ii]] = stepper;
+ while (*stepper) {
+ if (*stepper == ':') {
+ *stepper = '\0';
+ ii++;
+ if (ii<tmpl_cnt){
+ updvals[tmpl_idx[ii]] = stepper+1;
+ }
+ }
+ stepper++;
+ }
+
+ if (ii != tmpl_cnt-1) {
+ rrd_set_error("expected %lu data source readings (got %lu) from %s",
+ tmpl_cnt-1, ii, argv[arg_i]);
+ free(step_start);
+ break;
+ }
+
+ /* get the time from the reading ... handle N */
+ if (timesyntax == atstyle) {
+ if ((parsetime_error = parsetime(updvals[0], &ds_tv))) {
+ rrd_set_error("ds time: %s: %s", updvals[0], parsetime_error );
+ free(step_start);
+ break;
+ }
+ if (ds_tv.type == RELATIVE_TO_END_TIME ||
+ ds_tv.type == RELATIVE_TO_START_TIME) {
+ rrd_set_error("specifying time relative to the 'start' "
+ "or 'end' makes no sense here: %s",
+ updvals[0]);
+ free(step_start);
+ break;
+ }
+
+ current_time = mktime(&ds_tv.tm) + ds_tv.offset;
+ current_time_usec = 0; /* FIXME: how to handle usecs here ? */
+
+ } else if (strcmp(updvals[0],"N")==0){
+ gettimeofday(&tmp_time, 0);
+ normalize_time(&tmp_time);
+ current_time = tmp_time.tv_sec;
+ current_time_usec = tmp_time.tv_usec;
+ } else {
+ double tmp;
+ tmp = strtod(updvals[0], 0);
+ current_time = floor(tmp);
+ current_time_usec = (long)((tmp-(double)current_time) * 1000000.0);
+ }
+ /* dont do any correction for old version RRDs */
+ if(version < 3)
+ current_time_usec = 0;
+
+ if(current_time < rrd.live_head->last_up ||
+ (current_time == rrd.live_head->last_up &&
+ (long)current_time_usec <= (long)rrd.live_head->last_up_usec)) {
+ rrd_set_error("illegal attempt to update using time %ld when "
+ "last update time is %ld (minimum one second step)",
+ current_time, rrd.live_head->last_up);
+ free(step_start);
+ break;
+ }
+
+
+ /* seek to the beginning of the rra's */
+ if (rra_current != rra_begin) {
+#ifndef HAVE_MMAP
+ if(fseek(rrd_file, rra_begin, SEEK_SET) != 0) {
+ rrd_set_error("seek error in rrd");
+ free(step_start);
+ break;
+ }
+#endif
+ rra_current = rra_begin;
+ }
+ rra_start = rra_begin;
+
+ /* when was the current pdp started */
+ proc_pdp_age = rrd.live_head->last_up % rrd.stat_head->pdp_step;
+ proc_pdp_st = rrd.live_head->last_up - proc_pdp_age;
+
+ /* when did the last pdp_st occur */
+ occu_pdp_age = current_time % rrd.stat_head->pdp_step;
+ occu_pdp_st = current_time - occu_pdp_age;
+
+ /* interval = current_time - rrd.live_head->last_up; */
+ interval = (double)(current_time - rrd.live_head->last_up)
+ + (double)((long)current_time_usec - (long)rrd.live_head->last_up_usec)/1000000.0;
+
+ if (occu_pdp_st > proc_pdp_st){
+ /* OK we passed the pdp_st moment*/
+ pre_int = (long)occu_pdp_st - rrd.live_head->last_up; /* how much of the input data
+ * occurred before the latest
+ * pdp_st moment*/
+ pre_int -= ((double)rrd.live_head->last_up_usec)/1000000.0; /* adjust usecs */
+ post_int = occu_pdp_age; /* how much after it */
+ post_int += ((double)current_time_usec)/1000000.0; /* adjust usecs */
+ } else {
+ pre_int = interval;
+ post_int = 0;
+ }
+
+#ifdef DEBUG
+ printf(
+ "proc_pdp_age %lu\t"
+ "proc_pdp_st %lu\t"
+ "occu_pfp_age %lu\t"
+ "occu_pdp_st %lu\t"
+ "int %lf\t"
+ "pre_int %lf\t"
+ "post_int %lf\n", proc_pdp_age, proc_pdp_st,
+ occu_pdp_age, occu_pdp_st,
+ interval, pre_int, post_int);
+#endif
+
+ /* process the data sources and update the pdp_prep
+ * area accordingly */
+ for(i=0;i<rrd.stat_head->ds_cnt;i++){
+ enum dst_en dst_idx;
+ dst_idx= dst_conv(rrd.ds_def[i].dst);
+
+ /* make sure we do not build diffs with old last_ds values */
+ if(rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt < interval) {
+ strncpy(rrd.pdp_prep[i].last_ds,"U",LAST_DS_LEN-1);
+ rrd.pdp_prep[i].last_ds[LAST_DS_LEN-1]='\0';
+ }
+
+ /* NOTE: DST_CDEF should never enter this if block, because
+ * updvals[i+1][0] is initialized to 'U'; unless the caller
+ * accidently specified a value for the DST_CDEF. To handle
+ * this case, an extra check is required. */
+
+ if((updvals[i+1][0] != 'U') &&
+ (dst_idx != DST_CDEF) &&
+ rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt >= interval) {
+ double rate = DNAN;
+ /* the data source type defines how to process the data */
+ /* pdp_new contains rate * time ... eg the bytes
+ * transferred during the interval. Doing it this way saves
+ * a lot of math operations */
+
+
+ switch(dst_idx){
+ case DST_COUNTER:
+ case DST_DERIVE:
+ if(rrd.pdp_prep[i].last_ds[0] != 'U'){
+ for(ii=0;updvals[i+1][ii] != '\0';ii++){
+ if((updvals[i+1][ii] < '0' || updvals[i+1][ii] > '9') && (ii != 0 && updvals[i+1][ii] != '-')){
+ rrd_set_error("not a simple integer: '%s'",updvals[i+1]);
+ break;
+ }
+ }
+ if (rrd_test_error()){
+ break;
+ }
+ pdp_new[i]= rrd_diff(updvals[i+1],rrd.pdp_prep[i].last_ds);
+ if(dst_idx == DST_COUNTER) {
+ /* simple overflow catcher suggested by Andres Kroonmaa */
+ /* this will fail terribly for non 32 or 64 bit counters ... */
+ /* are there any others in SNMP land ? */
+ if (pdp_new[i] < (double)0.0 )
+ pdp_new[i] += (double)4294967296.0 ; /* 2^32 */
+ if (pdp_new[i] < (double)0.0 )
+ pdp_new[i] += (double)18446744069414584320.0; /* 2^64-2^32 */;
+ }
+ rate = pdp_new[i] / interval;
+ }
+ else {
+ pdp_new[i]= DNAN;
+ }
+ break;
+ case DST_ABSOLUTE:
+ errno = 0;
+ pdp_new[i] = strtod(updvals[i+1],&endptr);
+ if (errno > 0){
+ rrd_set_error("converting '%s' to float: %s",updvals[i+1],rrd_strerror(errno));
+ break;
+ };
+ if (endptr[0] != '\0'){
+ rrd_set_error("conversion of '%s' to float not complete: tail '%s'",updvals[i+1],endptr);
+ break;
+ }
+ rate = pdp_new[i] / interval;
+ break;
+ case DST_GAUGE:
+ errno = 0;
+ pdp_new[i] = strtod(updvals[i+1],&endptr) * interval;
+ if (errno > 0){
+ rrd_set_error("converting '%s' to float: %s",updvals[i+1],rrd_strerror(errno));
+ break;
+ };
+ if (endptr[0] != '\0'){
+ rrd_set_error("conversion of '%s' to float not complete: tail '%s'",updvals[i+1],endptr);
+ break;
+ }
+ rate = pdp_new[i] / interval;
+ break;
+ default:
+ rrd_set_error("rrd contains unknown DS type : '%s'",
+ rrd.ds_def[i].dst);
+ break;
+ }
+ /* break out of this for loop if the error string is set */
+ if (rrd_test_error()){
+ break;
+ }
+ /* make sure pdp_temp is neither too large or too small
+ * if any of these occur it becomes unknown ...
+ * sorry folks ... */
+ if ( ! isnan(rate) &&
+ (( ! isnan(rrd.ds_def[i].par[DS_max_val].u_val) &&
+ rate > rrd.ds_def[i].par[DS_max_val].u_val ) ||
+ ( ! isnan(rrd.ds_def[i].par[DS_min_val].u_val) &&
+ rate < rrd.ds_def[i].par[DS_min_val].u_val ))){
+ pdp_new[i] = DNAN;
+ }
+ } else {
+ /* no news is news all the same */
+ pdp_new[i] = DNAN;
+ }
+
+
+ /* make a copy of the command line argument for the next run */
+#ifdef DEBUG
+ fprintf(stderr,
+ "prep ds[%lu]\t"
+ "last_arg '%s'\t"
+ "this_arg '%s'\t"
+ "pdp_new %10.2f\n",
+ i,
+ rrd.pdp_prep[i].last_ds,
+ updvals[i+1], pdp_new[i]);
+#endif
+ strncpy(rrd.pdp_prep[i].last_ds, updvals[i+1],LAST_DS_LEN-1);
+ rrd.pdp_prep[i].last_ds[LAST_DS_LEN-1]='\0';
+ }
+ /* break out of the argument parsing loop if the error_string is set */
+ if (rrd_test_error()){
+ free(step_start);
+ break;
+ }
+ /* has a pdp_st moment occurred since the last run ? */
+
+ if (proc_pdp_st == occu_pdp_st){
+ /* no we have not passed a pdp_st moment. therefore update is simple */
+
+ for(i=0;i<rrd.stat_head->ds_cnt;i++){
+ if(isnan(pdp_new[i])) {
+ /* this is not realy accurate if we use subsecond data arival time
+ should have thought of it when going subsecond resolution ...
+ sorry next format change we will have it! */
+ rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt += floor(interval);
+ } else {
+ if (isnan( rrd.pdp_prep[i].scratch[PDP_val].u_val )){
+ rrd.pdp_prep[i].scratch[PDP_val].u_val= pdp_new[i];
+ } else {
+ rrd.pdp_prep[i].scratch[PDP_val].u_val+= pdp_new[i];
+ }
+ }
+#ifdef DEBUG
+ fprintf(stderr,
+ "NO PDP ds[%lu]\t"
+ "value %10.2f\t"
+ "unkn_sec %5lu\n",
+ i,
+ rrd.pdp_prep[i].scratch[PDP_val].u_val,
+ rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
+#endif
+ }
+ } else {
+ /* an pdp_st has occurred. */
+
+ /* in pdp_prep[].scratch[PDP_val].u_val we have collected rate*seconds which
+ * occurred up to the last run.
+ pdp_new[] contains rate*seconds from the latest run.
+ pdp_temp[] will contain the rate for cdp */
+
+ for(i=0;i<rrd.stat_head->ds_cnt;i++){
+ /* update pdp_prep to the current pdp_st. */
+ double pre_unknown = 0.0;
+ if(isnan(pdp_new[i]))
+ /* a final bit of unkonwn to be added bevore calculation
+ * we use a tempaorary variable for this so that we
+ * don't have to turn integer lines before using the value */
+ pre_unknown = pre_int;
+ else {
+ if (isnan( rrd.pdp_prep[i].scratch[PDP_val].u_val )){
+ rrd.pdp_prep[i].scratch[PDP_val].u_val= pdp_new[i]/interval*pre_int;
+ } else {
+ rrd.pdp_prep[i].scratch[PDP_val].u_val+= pdp_new[i]/interval*pre_int;
+ }
+ }
+
+
+ /* if too much of the pdp_prep is unknown we dump it */
+ if (
+ /* removed because this does not agree with the definition
+ a heart beat can be unknown */
+ /* (rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt
+ > rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt) || */
+ /* if the interval is larger thatn mrhb we get NAN */
+ (interval > rrd.ds_def[i].par[DS_mrhb_cnt].u_cnt) ||
+ (occu_pdp_st-proc_pdp_st <=
+ rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt)) {
+ pdp_temp[i] = DNAN;
+ } else {
+ pdp_temp[i] = rrd.pdp_prep[i].scratch[PDP_val].u_val
+ / ((double)(occu_pdp_st - proc_pdp_st
+ - rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt)
+ -pre_unknown);
+ }
+
+ /* process CDEF data sources; remember each CDEF DS can
+ * only reference other DS with a lower index number */
+ if (dst_conv(rrd.ds_def[i].dst) == DST_CDEF) {
+ rpnp_t *rpnp;
+ rpnp = rpn_expand((rpn_cdefds_t *) &(rrd.ds_def[i].par[DS_cdef]));
+ /* substitue data values for OP_VARIABLE nodes */
+ for (ii = 0; rpnp[ii].op != OP_END; ii++)
+ {
+ if (rpnp[ii].op == OP_VARIABLE) {
+ rpnp[ii].op = OP_NUMBER;
+ rpnp[ii].val = pdp_temp[rpnp[ii].ptr];
+ }
+ }
+ /* run the rpn calculator */
+ if (rpn_calc(rpnp,&rpnstack,0,pdp_temp,i) == -1) {
+ free(rpnp);
+ break; /* exits the data sources pdp_temp loop */
+ }
+ }
+
+ /* make pdp_prep ready for the next run */
+ if(isnan(pdp_new[i])){
+ /* this is not realy accurate if we use subsecond data arival time
+ should have thought of it when going subsecond resolution ...
+ sorry next format change we will have it! */
+ rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt = floor(post_int);
+ rrd.pdp_prep[i].scratch[PDP_val].u_val = DNAN;
+ } else {
+ rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt = 0;
+ rrd.pdp_prep[i].scratch[PDP_val].u_val =
+ pdp_new[i]/interval*post_int;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr,
+ "PDP UPD ds[%lu]\t"
+ "pdp_temp %10.2f\t"
+ "new_prep %10.2f\t"
+ "new_unkn_sec %5lu\n",
+ i, pdp_temp[i],
+ rrd.pdp_prep[i].scratch[PDP_val].u_val,
+ rrd.pdp_prep[i].scratch[PDP_unkn_sec_cnt].u_cnt);
+#endif
+ }
+
+ /* if there were errors during the last loop, bail out here */
+ if (rrd_test_error()){
+ free(step_start);
+ break;
+ }
+
+ /* compute the number of elapsed pdp_st moments */
+ elapsed_pdp_st = (occu_pdp_st - proc_pdp_st) / rrd.stat_head -> pdp_step;
+#ifdef DEBUG
+ fprintf(stderr,"elapsed PDP steps: %lu\n", elapsed_pdp_st);
+#endif
+ if (rra_step_cnt == NULL)
+ {
+ rra_step_cnt = (unsigned long *)
+ malloc((rrd.stat_head->rra_cnt)* sizeof(unsigned long));
+ }
+
+ for(i = 0, rra_start = rra_begin;
+ i < rrd.stat_head->rra_cnt;
+ rra_start += rrd.rra_def[i].row_cnt * rrd.stat_head -> ds_cnt * sizeof(rrd_value_t),
+ i++)
+ {
+ current_cf = cf_conv(rrd.rra_def[i].cf_nam);
+ start_pdp_offset = rrd.rra_def[i].pdp_cnt -
+ (proc_pdp_st / rrd.stat_head -> pdp_step) % rrd.rra_def[i].pdp_cnt;
+ if (start_pdp_offset <= elapsed_pdp_st) {
+ rra_step_cnt[i] = (elapsed_pdp_st - start_pdp_offset) /
+ rrd.rra_def[i].pdp_cnt + 1;
+ } else {
+ rra_step_cnt[i] = 0;
+ }
+
+ if (current_cf == CF_SEASONAL || current_cf == CF_DEVSEASONAL)
+ {
+ /* If this is a bulk update, we need to skip ahead in the seasonal
+ * arrays so that they will be correct for the next observed value;
+ * note that for the bulk update itself, no update will occur to
+ * DEVSEASONAL or SEASONAL; futhermore, HWPREDICT and DEVPREDICT will
+ * be set to DNAN. */
+ if (rra_step_cnt[i] > 2)
+ {
+ /* skip update by resetting rra_step_cnt[i],
+ * note that this is not data source specific; this is due
+ * to the bulk update, not a DNAN value for the specific data
+ * source. */
+ rra_step_cnt[i] = 0;
+ lookup_seasonal(&rrd,i,rra_start,rrd_file,elapsed_pdp_st,
+ &last_seasonal_coef);
+ lookup_seasonal(&rrd,i,rra_start,rrd_file,elapsed_pdp_st + 1,
+ &seasonal_coef);
+ }
+
+ /* periodically run a smoother for seasonal effects */
+ /* Need to use first cdp parameter buffer to track
+ * burnin (burnin requires a specific smoothing schedule).
+ * The CDP_init_seasonal parameter is really an RRA level,
+ * not a data source within RRA level parameter, but the rra_def
+ * is read only for rrd_update (not flushed to disk). */
+ iii = i*(rrd.stat_head -> ds_cnt);
+ if (rrd.cdp_prep[iii].scratch[CDP_init_seasonal].u_cnt
+ <= BURNIN_CYCLES)
+ {
+ if (rrd.rra_ptr[i].cur_row + elapsed_pdp_st
+ > rrd.rra_def[i].row_cnt - 1) {
+ /* mark off one of the burnin cycles */
+ ++(rrd.cdp_prep[iii].scratch[CDP_init_seasonal].u_cnt);
+ schedule_smooth = 1;
+ }
+ } else {
+ /* someone has no doubt invented a trick to deal with this
+ * wrap around, but at least this code is clear. */
+ if (rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt >
+ rrd.rra_ptr[i].cur_row)
+ {
+ /* here elapsed_pdp_st = rra_step_cnt[i] because of 1-1
+ * mapping between PDP and CDP */
+ if (rrd.rra_ptr[i].cur_row + elapsed_pdp_st
+ >= rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt)
+ {
+#ifdef DEBUG
+ fprintf(stderr,
+ "schedule_smooth 1: cur_row %lu, elapsed_pdp_st %lu, smooth idx %lu\n",
+ rrd.rra_ptr[i].cur_row, elapsed_pdp_st,
+ rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt);
+#endif
+ schedule_smooth = 1;
+ }
+ } else {
+ /* can't rely on negative numbers because we are working with
+ * unsigned values */
+ /* Don't need modulus here. If we've wrapped more than once, only
+ * one smooth is executed at the end. */
+ if (rrd.rra_ptr[i].cur_row + elapsed_pdp_st >= rrd.rra_def[i].row_cnt
+ && rrd.rra_ptr[i].cur_row + elapsed_pdp_st - rrd.rra_def[i].row_cnt
+ >= rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt)
+ {
+#ifdef DEBUG
+ fprintf(stderr,
+ "schedule_smooth 2: cur_row %lu, elapsed_pdp_st %lu, smooth idx %lu\n",
+ rrd.rra_ptr[i].cur_row, elapsed_pdp_st,
+ rrd.rra_def[i].par[RRA_seasonal_smooth_idx].u_cnt);
+#endif
+ schedule_smooth = 1;
+ }
+ }
+ }
+
+ rra_current = ftell(rrd_file);
+ } /* if cf is DEVSEASONAL or SEASONAL */
+
+ if (rrd_test_error()) break;
+
+ /* update CDP_PREP areas */
+ /* loop over data soures within each RRA */
+ for(ii = 0;
+ ii < rrd.stat_head->ds_cnt;
+ ii++)
+ {
+
+ /* iii indexes the CDP prep area for this data source within the RRA */
+ iii=i*rrd.stat_head->ds_cnt+ii;
+
+ if (rrd.rra_def[i].pdp_cnt > 1) {
+
+ if (rra_step_cnt[i] > 0) {
+ /* If we are in this block, as least 1 CDP value will be written to
+ * disk, this is the CDP_primary_val entry. If more than 1 value needs
+ * to be written, then the "fill in" value is the CDP_secondary_val
+ * entry. */
+ if (isnan(pdp_temp[ii]))
+ {
+ rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt += start_pdp_offset;
+ rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_val = DNAN;
+ } else {
+ /* CDP_secondary value is the RRA "fill in" value for intermediary
+ * CDP data entries. No matter the CF, the value is the same because
+ * the average, max, min, and last of a list of identical values is
+ * the same, namely, the value itself. */
+ rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_val = pdp_temp[ii];
+ }
+
+ if (rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt
+ > rrd.rra_def[i].pdp_cnt*
+ rrd.rra_def[i].par[RRA_cdp_xff_val].u_val)
+ {
+ rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = DNAN;
+ /* initialize carry over */
+ if (current_cf == CF_AVERAGE) {
+ if (isnan(pdp_temp[ii])) {
+ rrd.cdp_prep[iii].scratch[CDP_val].u_val = DNAN;
+ } else {
+ rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii] *
+ ((elapsed_pdp_st - start_pdp_offset) % rrd.rra_def[i].pdp_cnt);
+ }
+ } else {
+ rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+ }
+ } else {
+ rrd_value_t cum_val, cur_val;
+ switch (current_cf) {
+ case CF_AVERAGE:
+ cum_val = IFDNAN(rrd.cdp_prep[iii].scratch[CDP_val].u_val, 0.0);
+ cur_val = IFDNAN(pdp_temp[ii],0.0);
+ rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val =
+ (cum_val + cur_val * start_pdp_offset) /
+ (rrd.rra_def[i].pdp_cnt
+ -rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt);
+ /* initialize carry over value */
+ if (isnan(pdp_temp[ii])) {
+ rrd.cdp_prep[iii].scratch[CDP_val].u_val = DNAN;
+ } else {
+ rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii] *
+ ((elapsed_pdp_st - start_pdp_offset) % rrd.rra_def[i].pdp_cnt);
+ }
+ break;
+ case CF_MAXIMUM:
+ cum_val = IFDNAN(rrd.cdp_prep[iii].scratch[CDP_val].u_val, -DINF);
+ cur_val = IFDNAN(pdp_temp[ii],-DINF);
+#ifdef DEBUG
+ if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val) &&
+ isnan(pdp_temp[ii])) {
+ fprintf(stderr,
+ "RRA %lu, DS %lu, both CDP_val and pdp_temp are DNAN!",
+ i,ii);
+ exit(-1);
+ }
+#endif
+ if (cur_val > cum_val)
+ rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = cur_val;
+ else
+ rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = cum_val;
+ /* initialize carry over value */
+ rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+ break;
+ case CF_MINIMUM:
+ cum_val = IFDNAN(rrd.cdp_prep[iii].scratch[CDP_val].u_val, DINF);
+ cur_val = IFDNAN(pdp_temp[ii],DINF);
+#ifdef DEBUG
+ if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val) &&
+ isnan(pdp_temp[ii])) {
+ fprintf(stderr,
+ "RRA %lu, DS %lu, both CDP_val and pdp_temp are DNAN!",
+ i,ii);
+ exit(-1);
+ }
+#endif
+ if (cur_val < cum_val)
+ rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = cur_val;
+ else
+ rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = cum_val;
+ /* initialize carry over value */
+ rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+ break;
+ case CF_LAST:
+ default:
+ rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = pdp_temp[ii];
+ /* initialize carry over value */
+ rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+ break;
+ }
+ } /* endif meets xff value requirement for a valid value */
+ /* initialize carry over CDP_unkn_pdp_cnt, this must after CDP_primary_val
+ * is set because CDP_unkn_pdp_cnt is required to compute that value. */
+ if (isnan(pdp_temp[ii]))
+ rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt =
+ (elapsed_pdp_st - start_pdp_offset) % rrd.rra_def[i].pdp_cnt;
+ else
+ rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt = 0;
+ } else /* rra_step_cnt[i] == 0 */
+ {
+#ifdef DEBUG
+ if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val)) {
+ fprintf(stderr,"schedule CDP_val update, RRA %lu DS %lu, DNAN\n",
+ i,ii);
+ } else {
+ fprintf(stderr,"schedule CDP_val update, RRA %lu DS %lu, %10.2f\n",
+ i,ii,rrd.cdp_prep[iii].scratch[CDP_val].u_val);
+ }
+#endif
+ if (isnan(pdp_temp[ii])) {
+ rrd.cdp_prep[iii].scratch[CDP_unkn_pdp_cnt].u_cnt += elapsed_pdp_st;
+ } else if (isnan(rrd.cdp_prep[iii].scratch[CDP_val].u_val))
+ {
+ if (current_cf == CF_AVERAGE) {
+ rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii] *
+ elapsed_pdp_st;
+ } else {
+ rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+ }
+#ifdef DEBUG
+ fprintf(stderr,"Initialize CDP_val for RRA %lu DS %lu: %10.2f\n",
+ i,ii,rrd.cdp_prep[iii].scratch[CDP_val].u_val);
+#endif
+ } else {
+ switch (current_cf) {
+ case CF_AVERAGE:
+ rrd.cdp_prep[iii].scratch[CDP_val].u_val += pdp_temp[ii] *
+ elapsed_pdp_st;
+ break;
+ case CF_MINIMUM:
+ if (pdp_temp[ii] < rrd.cdp_prep[iii].scratch[CDP_val].u_val)
+ rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+ break;
+ case CF_MAXIMUM:
+ if (pdp_temp[ii] > rrd.cdp_prep[iii].scratch[CDP_val].u_val)
+ rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+ break;
+ case CF_LAST:
+ default:
+ rrd.cdp_prep[iii].scratch[CDP_val].u_val = pdp_temp[ii];
+ break;
+ }
+ }
+ }
+ } else { /* rrd.rra_def[i].pdp_cnt == 1 */
+ if (elapsed_pdp_st > 2)
+ {
+ switch (current_cf) {
+ case CF_AVERAGE:
+ default:
+ rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val=pdp_temp[ii];
+ rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_val=pdp_temp[ii];
+ break;
+ case CF_SEASONAL:
+ case CF_DEVSEASONAL:
+ /* need to update cached seasonal values, so they are consistent
+ * with the bulk update */
+ /* WARNING: code relies on the fact that CDP_hw_last_seasonal and
+ * CDP_last_deviation are the same. */
+ rrd.cdp_prep[iii].scratch[CDP_hw_last_seasonal].u_val =
+ last_seasonal_coef[ii];
+ rrd.cdp_prep[iii].scratch[CDP_hw_seasonal].u_val =
+ seasonal_coef[ii];
+ break;
+ case CF_HWPREDICT:
+ /* need to update the null_count and last_null_count.
+ * even do this for non-DNAN pdp_temp because the
+ * algorithm is not learning from batch updates. */
+ rrd.cdp_prep[iii].scratch[CDP_null_count].u_cnt +=
+ elapsed_pdp_st;
+ rrd.cdp_prep[iii].scratch[CDP_last_null_count].u_cnt +=
+ elapsed_pdp_st - 1;
+ /* fall through */
+ case CF_DEVPREDICT:
+ rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = DNAN;
+ rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_val = DNAN;
+ break;
+ case CF_FAILURES:
+ /* do not count missed bulk values as failures */
+ rrd.cdp_prep[iii].scratch[CDP_primary_val].u_val = 0;
+ rrd.cdp_prep[iii].scratch[CDP_secondary_val].u_val = 0;
+ /* need to reset violations buffer.
+ * could do this more carefully, but for now, just
+ * assume a bulk update wipes away all violations. */
+ erase_violations(&rrd, iii, i);
+ break;
+ }
+ }
+ } /* endif rrd.rra_def[i].pdp_cnt == 1 */
+
+ if (rrd_test_error()) break;
+
+ } /* endif data sources loop */
+ } /* end RRA Loop */
+
+ /* this loop is only entered if elapsed_pdp_st < 3 */
+ for (j = elapsed_pdp_st, scratch_idx = CDP_primary_val;
+ j > 0 && j < 3; j--, scratch_idx = CDP_secondary_val)
+ {
+ for(i = 0, rra_start = rra_begin;
+ i < rrd.stat_head->rra_cnt;
+ rra_start += rrd.rra_def[i].row_cnt * rrd.stat_head -> ds_cnt * sizeof(rrd_value_t),
+ i++)
+ {
+ if (rrd.rra_def[i].pdp_cnt > 1) continue;
+
+ current_cf = cf_conv(rrd.rra_def[i].cf_nam);
+ if (current_cf == CF_SEASONAL || current_cf == CF_DEVSEASONAL)
+ {
+ lookup_seasonal(&rrd,i,rra_start,rrd_file,
+ elapsed_pdp_st + (scratch_idx == CDP_primary_val ? 1 : 2),
+ &seasonal_coef);
+ rra_current = ftell(rrd_file);
+ }
+ if (rrd_test_error()) break;
+ /* loop over data soures within each RRA */
+ for(ii = 0;
+ ii < rrd.stat_head->ds_cnt;
+ ii++)
+ {
+ update_aberrant_CF(&rrd,pdp_temp[ii],current_cf,
+ i*(rrd.stat_head->ds_cnt) + ii,i,ii,
+ scratch_idx, seasonal_coef);
+ }
+ } /* end RRA Loop */
+ if (rrd_test_error()) break;
+ } /* end elapsed_pdp_st loop */
+
+ if (rrd_test_error()) break;
+
+ /* Ready to write to disk */
+ /* Move sequentially through the file, writing one RRA at a time.
+ * Note this architecture divorces the computation of CDP with
+ * flushing updated RRA entries to disk. */
+ for(i = 0, rra_start = rra_begin;
+ i < rrd.stat_head->rra_cnt;
+ rra_start += rrd.rra_def[i].row_cnt * rrd.stat_head -> ds_cnt * sizeof(rrd_value_t),
+ i++) {
+ /* is th5Aere anything to write for this RRA? If not, continue. */
+ if (rra_step_cnt[i] == 0) continue;
+
+ /* write the first row */
+#ifdef DEBUG
+ fprintf(stderr," -- RRA Preseek %ld\n",ftell(rrd_file));
+#endif
+ rrd.rra_ptr[i].cur_row++;
+ if (rrd.rra_ptr[i].cur_row >= rrd.rra_def[i].row_cnt)
+ rrd.rra_ptr[i].cur_row = 0; /* wrap around */
+ /* positition on the first row */
+ rra_pos_tmp = rra_start +
+ (rrd.stat_head->ds_cnt)*(rrd.rra_ptr[i].cur_row)*sizeof(rrd_value_t);
+ if(rra_pos_tmp != rra_current) {
+#ifndef HAVE_MMAP
+ if(fseek(rrd_file, rra_pos_tmp, SEEK_SET) != 0){
+ rrd_set_error("seek error in rrd");
+ break;
+ }
+#endif
+ rra_current = rra_pos_tmp;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr," -- RRA Postseek %ld\n",ftell(rrd_file));
+#endif
+ scratch_idx = CDP_primary_val;
+ if (pcdp_summary != NULL)
+ {
+ rra_time = (current_time - current_time
+ % (rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step))
+ - ((rra_step_cnt[i]-1)*rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step);
+ }
+#ifdef HAVE_MMAP
+ pcdp_summary = write_RRA_row(&rrd, i, &rra_current, scratch_idx, rrd_file,
+ pcdp_summary, &rra_time, rrd_mmaped_file);
+#else
+ pcdp_summary = write_RRA_row(&rrd, i, &rra_current, scratch_idx, rrd_file,
+ pcdp_summary, &rra_time);
+#endif
+ if (rrd_test_error()) break;
+
+ /* write other rows of the bulk update, if any */
+ scratch_idx = CDP_secondary_val;
+ for ( ; rra_step_cnt[i] > 1; rra_step_cnt[i]--)
+ {
+ if (++rrd.rra_ptr[i].cur_row == rrd.rra_def[i].row_cnt)
+ {
+#ifdef DEBUG
+ fprintf(stderr,"Wraparound for RRA %s, %lu updates left\n",
+ rrd.rra_def[i].cf_nam, rra_step_cnt[i] - 1);
+#endif
+ /* wrap */
+ rrd.rra_ptr[i].cur_row = 0;
+ /* seek back to beginning of current rra */
+ if (fseek(rrd_file, rra_start, SEEK_SET) != 0)
+ {
+ rrd_set_error("seek error in rrd");
+ break;
+ }
+#ifdef DEBUG
+ fprintf(stderr," -- Wraparound Postseek %ld\n",ftell(rrd_file));
+#endif
+ rra_current = rra_start;
+ }
+ if (pcdp_summary != NULL)
+ {
+ rra_time = (current_time - current_time
+ % (rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step))
+ - ((rra_step_cnt[i]-2)*rrd.rra_def[i].pdp_cnt*rrd.stat_head->pdp_step);
+ }
+#ifdef HAVE_MMAP
+ pcdp_summary = write_RRA_row(&rrd, i, &rra_current, scratch_idx, rrd_file,
+ pcdp_summary, &rra_time, rrd_mmaped_file);
+#else
+ pcdp_summary = write_RRA_row(&rrd, i, &rra_current, scratch_idx, rrd_file,
+ pcdp_summary, &rra_time);
+#endif
+ }
+
+ if (rrd_test_error())
+ break;
+ } /* RRA LOOP */
+
+ /* break out of the argument parsing loop if error_string is set */
+ if (rrd_test_error()){
+ free(step_start);
+ break;
+ }
+
+ } /* endif a pdp_st has occurred */
+ rrd.live_head->last_up = current_time;
+ rrd.live_head->last_up_usec = current_time_usec;
+ free(step_start);
+ } /* function argument loop */
+
+ if (seasonal_coef != NULL) free(seasonal_coef);
+ if (last_seasonal_coef != NULL) free(last_seasonal_coef);
+ if (rra_step_cnt != NULL) free(rra_step_cnt);
+ rpnstack_free(&rpnstack);
+
+#ifdef HAVE_MMAP
+ if (munmap(rrd_mmaped_file, rrd_filesize) == -1) {
+ rrd_set_error("error writing(unmapping) file: %s", filename);
+ }
+#endif
+ /* if we got here and if there is an error and if the file has not been
+ * written to, then close things up and return. */
+ if (rrd_test_error()) {
+ free(updvals);
+ free(tmpl_idx);
+ rrd_free(&rrd);
+ free(pdp_temp);
+ free(pdp_new);
+ fclose(rrd_file);
+ return(-1);
+ }
+
+ /* aargh ... that was tough ... so many loops ... anyway, its done.
+ * we just need to write back the live header portion now*/
+
+ if (fseek(rrd_file, (sizeof(stat_head_t)
+ + sizeof(ds_def_t)*rrd.stat_head->ds_cnt
+ + sizeof(rra_def_t)*rrd.stat_head->rra_cnt),
+ SEEK_SET) != 0) {
+ rrd_set_error("seek rrd for live header writeback");
+ free(updvals);
+ free(tmpl_idx);
+ rrd_free(&rrd);
+ free(pdp_temp);
+ free(pdp_new);
+ fclose(rrd_file);
+ return(-1);
+ }
+
+ if(version >= 3) {
+ if(fwrite( rrd.live_head,
+ sizeof(live_head_t), 1, rrd_file) != 1){
+ rrd_set_error("fwrite live_head to rrd");
+ free(updvals);
+ rrd_free(&rrd);
+ free(tmpl_idx);
+ free(pdp_temp);
+ free(pdp_new);
+ fclose(rrd_file);
+ return(-1);
+ }
+ }
+ else {
+ if(fwrite( &rrd.live_head->last_up,
+ sizeof(time_t), 1, rrd_file) != 1){
+ rrd_set_error("fwrite live_head to rrd");
+ free(updvals);
+ rrd_free(&rrd);
+ free(tmpl_idx);
+ free(pdp_temp);
+ free(pdp_new);
+ fclose(rrd_file);
+ return(-1);
+ }
+ }
+
+
+ if(fwrite( rrd.pdp_prep,
+ sizeof(pdp_prep_t),
+ rrd.stat_head->ds_cnt, rrd_file) != rrd.stat_head->ds_cnt){
+ rrd_set_error("ftwrite pdp_prep to rrd");
+ free(updvals);
+ rrd_free(&rrd);
+ free(tmpl_idx);
+ free(pdp_temp);
+ free(pdp_new);
+ fclose(rrd_file);
+ return(-1);
+ }
+
+ if(fwrite( rrd.cdp_prep,
+ sizeof(cdp_prep_t),
+ rrd.stat_head->rra_cnt *rrd.stat_head->ds_cnt, rrd_file)
+ != rrd.stat_head->rra_cnt *rrd.stat_head->ds_cnt){
+
+ rrd_set_error("ftwrite cdp_prep to rrd");
+ free(updvals);
+ free(tmpl_idx);
+ rrd_free(&rrd);
+ free(pdp_temp);
+ free(pdp_new);
+ fclose(rrd_file);
+ return(-1);
+ }
+
+ if(fwrite( rrd.rra_ptr,
+ sizeof(rra_ptr_t),
+ rrd.stat_head->rra_cnt,rrd_file) != rrd.stat_head->rra_cnt){
+ rrd_set_error("fwrite rra_ptr to rrd");
+ free(updvals);
+ free(tmpl_idx);
+ rrd_free(&rrd);
+ free(pdp_temp);
+ free(pdp_new);
+ fclose(rrd_file);
+ return(-1);
+ }
+
+#ifdef HAVE_POSIX_FADVISExxx
+
+ /* with update we have write ops, so they will probably not be done by now, this means
+ the buffers will not get freed. But calling this for the whole file - header
+ will let the data off the hook as soon as it is written when if it is from a previous
+ update cycle. Calling fdsync to force things is much too hard here. */
+
+ if (0 != posix_fadvise(fileno(rrd_file), rra_begin, 0, POSIX_FADV_DONTNEED)) {
+ rrd_set_error("setting POSIX_FADV_DONTNEED on '%s': %s",filename, rrd_strerror(errno));
+ fclose(rrd_file);
+ return(-1);
+ }
+#endif
+
+ /* OK now close the files and free the memory */
+ if(fclose(rrd_file) != 0){
+ rrd_set_error("closing rrd");
+ free(updvals);
+ free(tmpl_idx);
+ rrd_free(&rrd);
+ free(pdp_temp);
+ free(pdp_new);
+ return(-1);
+ }
+
+ /* calling the smoothing code here guarantees at most
+ * one smoothing operation per rrd_update call. Unfortunately,
+ * it is possible with bulk updates, or a long-delayed update
+ * for smoothing to occur off-schedule. This really isn't
+ * critical except during the burning cycles. */
+ if (schedule_smooth)
+ {
+ rrd_file = fopen(filename,"rb+");
+
+
+ rra_start = rra_begin;
+ for (i = 0; i < rrd.stat_head -> rra_cnt; ++i)
+ {
+ if (cf_conv(rrd.rra_def[i].cf_nam) == CF_DEVSEASONAL ||
+ cf_conv(rrd.rra_def[i].cf_nam) == CF_SEASONAL)
+ {
+#ifdef DEBUG
+ fprintf(stderr,"Running smoother for rra %ld\n",i);
+#endif
+ apply_smoother(&rrd,i,rra_start,rrd_file);
+ if (rrd_test_error())
+ break;
+ }
+ rra_start += rrd.rra_def[i].row_cnt
+ *rrd.stat_head->ds_cnt*sizeof(rrd_value_t);
+ }
+#ifdef HAVE_POSIX_FADVISExxx
+ /* same procedure as above ... */
+ if (0 != posix_fadvise(fileno(rrd_file), rra_begin, 0, POSIX_FADV_DONTNEED)) {
+ rrd_set_error("setting POSIX_FADV_DONTNEED on '%s': %s",filename, rrd_strerror(errno));
+ fclose(rrd_file);
+ return(-1);
+ }
+#endif
+ fclose(rrd_file);
+ }
+ rrd_free(&rrd);
+ free(updvals);
+ free(tmpl_idx);
+ free(pdp_new);
+ free(pdp_temp);
+ return(0);
+}
+
+/*
+ * get exclusive lock to whole file.
+ * lock gets removed when we close the file
+ *
+ * returns 0 on success
+ */
+int
+LockRRD(FILE *rrdfile)
+{
+ int rrd_fd; /* File descriptor for RRD */
+ int rcstat;
+
+ rrd_fd = fileno(rrdfile);
+
+ {
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+ struct _stat st;
+
+ if ( _fstat( rrd_fd, &st ) == 0 ) {
+ rcstat = _locking ( rrd_fd, _LK_NBLCK, st.st_size );
+ } else {
+ rcstat = -1;
+ }
+#else
+ struct flock lock;
+ lock.l_type = F_WRLCK; /* exclusive write lock */
+ lock.l_len = 0; /* whole file */
+ lock.l_start = 0; /* start of file */
+ lock.l_whence = SEEK_SET; /* end of file */
+
+ rcstat = fcntl(rrd_fd, F_SETLK, &lock);
+#endif
+ }
+
+ return(rcstat);
+}
+
+
+#ifdef HAVE_MMAP
+info_t
+*write_RRA_row (rrd_t *rrd, unsigned long rra_idx, unsigned long *rra_current,
+ unsigned short CDP_scratch_idx,
+#ifndef DEBUG
+FILE UNUSED(*rrd_file),
+#else
+FILE *rrd_file,
+#endif
+ info_t *pcdp_summary, time_t *rra_time, void *rrd_mmaped_file)
+#else
+info_t
+*write_RRA_row (rrd_t *rrd, unsigned long rra_idx, unsigned long *rra_current,
+ unsigned short CDP_scratch_idx, FILE *rrd_file,
+ info_t *pcdp_summary, time_t *rra_time)
+#endif
+{
+ unsigned long ds_idx, cdp_idx;
+ infoval iv;
+
+ for (ds_idx = 0; ds_idx < rrd -> stat_head -> ds_cnt; ds_idx++)
+ {
+ /* compute the cdp index */
+ cdp_idx =rra_idx * (rrd -> stat_head->ds_cnt) + ds_idx;
+#ifdef DEBUG
+ fprintf(stderr," -- RRA WRITE VALUE %e, at %ld CF:%s\n",
+ rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val,ftell(rrd_file),
+ rrd -> rra_def[rra_idx].cf_nam);
+#endif
+ if (pcdp_summary != NULL)
+ {
+ iv.u_val = rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val;
+ /* append info to the return hash */
+ pcdp_summary = info_push(pcdp_summary,
+ sprintf_alloc("[%d]RRA[%s][%lu]DS[%s]",
+ *rra_time, rrd->rra_def[rra_idx].cf_nam,
+ rrd->rra_def[rra_idx].pdp_cnt, rrd->ds_def[ds_idx].ds_nam),
+ RD_I_VAL, iv);
+ }
+#ifdef HAVE_MMAP
+ memcpy((char *)rrd_mmaped_file + *rra_current,
+ &(rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val),
+ sizeof(rrd_value_t));
+#else
+ if(fwrite(&(rrd -> cdp_prep[cdp_idx].scratch[CDP_scratch_idx].u_val),
+ sizeof(rrd_value_t),1,rrd_file) != 1)
+ {
+ rrd_set_error("writing rrd");
+ return 0;
+ }
+#endif
+ *rra_current += sizeof(rrd_value_t);
+ }
+ return (pcdp_summary);
+}
diff --git a/program/src/rrd_version.c b/program/src/rrd_version.c
--- /dev/null
@@ -0,0 +1,23 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrd_version Return
+ *****************************************************************************
+ * Initial version by Burton Strauss, ntopSupport.com - 5/2005
+ *****************************************************************************/
+
+#include "rrd_tool.h"
+
+double
+rrd_version(void)
+{
+ return NUMVERS;
+}
+
+char *
+rrd_strversion(void)
+{
+ return PACKAGE_VERSION;
+}
+
+
diff --git a/program/src/rrd_xport.c b/program/src/rrd_xport.c
--- /dev/null
+++ b/program/src/rrd_xport.c
@@ -0,0 +1,323 @@
+/****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd_xport.c export RRD data
+ ****************************************************************************/
+
+#include <sys/stat.h>
+
+#include "rrd_tool.h"
+#include "rrd_graph.h"
+#include "rrd_xport.h"
+#include "unused.h"
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
+#include <io.h>
+#include <fcntl.h>
+#endif
+
+
+int rrd_xport(int, char **, int *,
+ time_t *, time_t *,
+ unsigned long *, unsigned long *,
+ char ***, rrd_value_t **);
+
+int rrd_xport_fn(image_desc_t *,
+ time_t *, time_t *,
+ unsigned long *, unsigned long *,
+ char ***, rrd_value_t **);
+
+
+
+
+int
+rrd_xport(int argc, char **argv, int UNUSED(*xsize),
+ time_t *start,
+ time_t *end, /* which time frame do you want ?
+ * will be changed to represent reality */
+ unsigned long *step, /* which stepsize do you want?
+ * will be changed to represent reality */
+ unsigned long *col_cnt, /* number of data columns in the result */
+ char ***legend_v, /* legend entries */
+ rrd_value_t **data) /* two dimensional array containing the data */
+
+{
+
+ image_desc_t im;
+ time_t start_tmp=0,end_tmp=0;
+ struct rrd_time_value start_tv, end_tv;
+ char *parsetime_error = NULL;
+ optind = 0; opterr = 0; /* initialize getopt */
+
+ rrd_graph_init(&im);
+
+ parsetime("end-24h", &start_tv);
+ parsetime("now", &end_tv);
+
+ while (1){
+ static struct option long_options[] =
+ {
+ {"start", required_argument, 0, 's'},
+ {"end", required_argument, 0, 'e'},
+ {"maxrows", required_argument, 0, 'm'},
+ {"step", required_argument, 0, 261},
+ {"enumds", no_argument, 0, 262}, /* these are handled in the frontend ... */
+ {0,0,0,0}
+ };
+ int option_index = 0;
+ int opt;
+
+ opt = getopt_long(argc, argv, "s:e:m:",
+ long_options, &option_index);
+
+ if (opt == EOF)
+ break;
+
+ switch(opt) {
+ case 261:
+ im.step = atoi(optarg);
+ break;
+ case 262:
+ break;
+ case 's':
+ if ((parsetime_error = parsetime(optarg, &start_tv))) {
+ rrd_set_error( "start time: %s", parsetime_error );
+ return -1;
+ }
+ break;
+ case 'e':
+ if ((parsetime_error = parsetime(optarg, &end_tv))) {
+ rrd_set_error( "end time: %s", parsetime_error );
+ return -1;
+ }
+ break;
+ case 'm':
+ im.xsize = atol(optarg);
+ if (im.xsize < 10) {
+ rrd_set_error("maxrows below 10 rows");
+ return -1;
+ }
+ break;
+ case '?':
+ rrd_set_error("unknown option '%s'",argv[optind-1]);
+ return -1;
+ }
+ }
+
+ if (proc_start_end(&start_tv,&end_tv,&start_tmp,&end_tmp) == -1){
+ return -1;
+ }
+
+ if (start_tmp < 3600*24*365*10){
+ rrd_set_error("the first entry to fetch should be after 1980 (%ld)",start_tmp);
+ return -1;
+ }
+
+ if (end_tmp < start_tmp) {
+ rrd_set_error("start (%ld) should be less than end (%ld)",
+ start_tmp, end_tmp);
+ return -1;
+ }
+
+ im.start = start_tmp;
+ im.end = end_tmp;
+ im.step = max((long)im.step, (im.end-im.start)/im.xsize);
+
+ rrd_graph_script(argc,argv,&im,0);
+ if (rrd_test_error()) {
+ im_free(&im);
+ return -1;
+ }
+
+ if (im.gdes_c == 0){
+ rrd_set_error("can't make a graph without contents");
+ im_free(&im);
+ return(-1);
+ }
+
+ if (rrd_xport_fn(&im, start, end, step, col_cnt, legend_v, data) == -1){
+ im_free(&im);
+ return -1;
+ }
+
+ im_free(&im);
+ return 0;
+}
+
+
+
+int
+rrd_xport_fn(image_desc_t *im,
+ time_t *start,
+ time_t *end, /* which time frame do you want ?
+ * will be changed to represent reality */
+ unsigned long *step, /* which stepsize do you want?
+ * will be changed to represent reality */
+ unsigned long *col_cnt, /* number of data columns in the result */
+ char ***legend_v, /* legend entries */
+ rrd_value_t **data) /* two dimensional array containing the data */
+{
+
+ int i = 0, j = 0;
+ unsigned long *ds_cnt; /* number of data sources in file */
+ unsigned long col, dst_row, row_cnt;
+ rrd_value_t *srcptr, *dstptr;
+
+ unsigned long nof_xports = 0;
+ unsigned long xport_counter = 0;
+ unsigned long *ref_list;
+ rrd_value_t **srcptr_list;
+ char **legend_list;
+ int ii = 0;
+
+ time_t start_tmp = 0;
+ time_t end_tmp = 0;
+ unsigned long step_tmp = 1;
+
+ /* pull the data from the rrd files ... */
+ if(data_fetch(im)==-1)
+ return -1;
+
+ /* evaluate CDEF operations ... */
+ if(data_calc(im)==-1)
+ return -1;
+
+ /* how many xports? */
+ for(i = 0; i < im->gdes_c; i++) {
+ switch(im->gdes[i].gf) {
+ case GF_XPORT:
+ nof_xports++;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if(nof_xports == 0) {
+ rrd_set_error("no XPORT found, nothing to do");
+ return -1;
+ }
+
+ /* a list of referenced gdes */
+ ref_list = malloc(sizeof(int) * nof_xports);
+ if(ref_list == NULL)
+ return -1;
+
+ /* a list to save pointers into each gdes data */
+ srcptr_list = malloc(sizeof(srcptr) * nof_xports);
+ if(srcptr_list == NULL) {
+ free(ref_list);
+ return -1;
+ }
+
+ /* a list to save pointers to the column's legend entry */
+ /* this is a return value! */
+ legend_list = malloc(sizeof(char *) * nof_xports);
+ if(legend_list == NULL) {
+ free(srcptr_list);
+ free(ref_list);
+ return -1;
+ }
+
+ /* find referenced gdes and save their index and */
+ /* a pointer into their data */
+ for(i = 0; i < im->gdes_c; i++) {
+ switch(im->gdes[i].gf) {
+ case GF_XPORT:
+ ii = im->gdes[i].vidx;
+ if(xport_counter > nof_xports) {
+ rrd_set_error( "too many xports: should not happen. Hmmm");
+ free(srcptr_list);
+ free(ref_list);
+ free(legend_list);
+ return -1;
+ }
+ srcptr_list[xport_counter] = im->gdes[ii].data;
+ ref_list[xport_counter++] = i;
+ break;
+ default:
+ break;
+ }
+ }
+
+ start_tmp = im->gdes[0].start;
+ end_tmp = im->gdes[0].end;
+ step_tmp = im->gdes[0].step;
+
+ /* fill some return values */
+ *col_cnt = nof_xports;
+ *start = start_tmp;
+ *end = end_tmp;
+ *step = step_tmp;
+
+ row_cnt = ((*end)-(*start))/(*step);
+
+ /* room for rearranged data */
+ /* this is a return value! */
+ if (((*data) = malloc((*col_cnt) * row_cnt * sizeof(rrd_value_t)))==NULL){
+ free(srcptr_list);
+ free(ref_list);
+ free(legend_list);
+ rrd_set_error("malloc xport data area");
+ return(-1);
+ }
+ dstptr = (*data);
+
+ j = 0;
+ for(i = 0; i < im->gdes_c; i++) {
+ switch(im->gdes[i].gf) {
+ case GF_XPORT:
+ /* reserve room for one legend entry */
+ /* is FMT_LEG_LEN + 5 the correct size? */
+ if ((legend_list[j] = malloc(sizeof(char) * (FMT_LEG_LEN+5)))==NULL) {
+ free(srcptr_list);
+ free(ref_list);
+ free(*data); *data = NULL;
+ while (--j > -1) free(legend_list[j]);
+ free(legend_list);
+ rrd_set_error("malloc xport legend entry");
+ return(-1);
+ }
+
+ if (im->gdes[i].legend)
+ /* omit bounds check, should have the same size */
+ strcpy (legend_list[j++], im->gdes[i].legend);
+ else
+ legend_list[j++][0] = '\0';
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* fill data structure */
+ for(dst_row = 0; (int)dst_row < (int)row_cnt; dst_row++) {
+ for(i = 0; i < (int)nof_xports; i++) {
+ j = ref_list[i];
+ ii = im->gdes[j].vidx;
+ ds_cnt = &im->gdes[ii].ds_cnt;
+
+ srcptr = srcptr_list[i];
+ for(col = 0; col < (*ds_cnt); col++) {
+ rrd_value_t newval = DNAN;
+ newval = srcptr[col];
+
+ if (im->gdes[ii].ds_namv && im->gdes[ii].ds_nam) {
+ if(strcmp(im->gdes[ii].ds_namv[col],im->gdes[ii].ds_nam) == 0)
+ (*dstptr++) = newval;
+ } else {
+ (*dstptr++) = newval;
+ }
+
+ }
+ srcptr_list[i] += (*ds_cnt);
+ }
+ }
+
+ *legend_v = legend_list;
+ free(srcptr_list);
+ free(ref_list);
+ return 0;
+
+}
diff --git a/program/src/rrd_xport.h b/program/src/rrd_xport.h
--- /dev/null
+++ b/program/src/rrd_xport.h
@@ -0,0 +1,34 @@
+/****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ ****************************************************************************
+ * rrd_xport.h contains XML related constants
+ ****************************************************************************/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef _RRD_XPORT_H
+#define _RRD_XPORT_H
+
+#define XML_ENCODING "ISO-8859-1"
+#define ROOT_TAG "xport"
+#define META_TAG "meta"
+#define META_START_TAG "start"
+#define META_STEP_TAG "step"
+#define META_END_TAG "end"
+#define META_ROWS_TAG "rows"
+#define META_COLS_TAG "columns"
+#define LEGEND_TAG "legend"
+#define LEGEND_ENTRY_TAG "entry"
+#define DATA_TAG "data"
+#define DATA_ROW_TAG "row"
+#define COL_TIME_TAG "t"
+#define COL_DATA_TAG "v"
+
+
+#endif
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/program/src/rrdupdate.c b/program/src/rrdupdate.c
--- /dev/null
+++ b/program/src/rrdupdate.c
@@ -0,0 +1,35 @@
+/*****************************************************************************
+ * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ *****************************************************************************
+ * rrdupdate.c Main program for the (standalone) rrdupdate utility
+ *****************************************************************************
+ * $Id$
+ *****************************************************************************/
+
+#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__) && !defined(HAVE_CONFIG_H)
+#include "../win32/config.h"
+#else
+#ifdef HAVE_CONFIG_H
+#include "../rrd_config.h"
+#endif
+#endif
+
+#include "rrd.h"
+
+int
+main(int argc, char **argv){
+ rrd_update(argc,argv);
+ if (rrd_test_error()) {
+ printf("RRDtool " PACKAGE_VERSION " Copyright by Tobi Oetiker, 1997-2007\n\n"
+ "Usage: rrdupdate filename\n"
+ "\t\t\t[--template|-t ds-name:ds-name:...]\n"
+ "\t\t\ttime|N:value[:value...]\n\n"
+ "\t\t\tat-time@value[:value...]\n\n"
+ "\t\t\t[ time:value[:value...] ..]\n\n");
+
+ printf("ERROR: %s\n",rrd_get_error());
+ rrd_clear_error();
+ return 1;
+ }
+ return 0;
+}
diff --git a/program/src/strftime.c b/program/src/strftime.c
--- /dev/null
+++ b/program/src/strftime.c
@@ -0,0 +1,356 @@
+/**
+ *
+ * strftime.c
+ *
+ * implements the ansi c function strftime()
+ *
+ * written 6 september 1989 by jim nutt
+ * released into the public domain by jim nutt
+ *
+ * modified 21-Oct-89 by Rob Duff
+ *
+ * modified 08-Dec-04 by Tobi Oetiker (added %V)
+**/
+
+#include <stddef.h> /* for size_t */
+#include <stdarg.h> /* for va_arg */
+#include <time.h> /* for struct tm */
+#include "strftime.h"
+
+/* Define your own defaults in config.h if necessary */
+#if defined(TZNAME_STD) && defined(TZNAME_DST)
+char *tzname_[2] = {TZNAME_STD, TZNAME_DST};
+#else
+#define tzname_ tzname
+#endif
+
+static char *aday[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+static char *day[] = {
+ "Sunday", "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday"
+};
+
+static char *amonth[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+static char *month[] = {
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+};
+
+static char buf[26];
+
+static void strfmt(char *str, const char *fmt, ...);
+
+/**
+ *
+ * size_t strftime_(char *str,
+ * size_t maxs,
+ * const char *fmt,
+ * const struct tm *t)
+ *
+ * this functions acts much like a sprintf for time/date output.
+ * given a pointer to an output buffer, a format string and a
+ * time, it copies the time to the output buffer formatted in
+ * accordance with the format string. the parameters are used
+ * as follows:
+ *
+ * str is a pointer to the output buffer, there should
+ * be at least maxs characters available at the address
+ * pointed to by str.
+ *
+ * maxs is the maximum number of characters to be copied
+ * into the output buffer, included the '\0' terminator
+ *
+ * fmt is the format string. a percent sign (%) is used
+ * to indicate that the following character is a special
+ * format character. the following are valid format
+ * characters:
+ *
+ * %A full weekday name (Monday)
+ * %a abbreviated weekday name (Mon)
+ * %B full month name (January)
+ * %b abbreviated month name (Jan)
+ * %c standard date and time representation
+ * %d day-of-month (01-31)
+ * %H hour (24 hour clock) (00-23)
+ * %I hour (12 hour clock) (01-12)
+ * %j day-of-year (001-366)
+ * %M minute (00-59)
+ * %m month (01-12)
+ * %p local equivalent of AM or PM
+ * %S second (00-59)
+ * %U week-of-year, first day sunday (00-53)
+ * %W week-of-year, first day monday (00-53)
+ * %V ISO 8601 Week number
+ * %w weekday (0-6, sunday is 0)
+ * %X standard time representation
+ * %x standard date representation
+ * %Y year with century
+ * %y year without century (00-99)
+ * %Z timezone name
+ * %% percent sign
+ *
+ * the standard date string is equivalent to:
+ *
+ * %a %b %d %Y
+ *
+ * the standard time string is equivalent to:
+ *
+ * %H:%M:%S
+ *
+ * the standard date and time string is equivalent to:
+ *
+ * %a %b %d %H:%M:%S %Y
+ *
+ * strftime_() returns the number of characters placed in the
+ * buffer, not including the terminating \0, or zero if more
+ * than maxs characters were produced.
+ *
+**/
+
+size_t strftime_(char *s, size_t maxs, const char *f, const struct tm *t)
+{
+ int w,d;
+ char *p, *q, *r;
+
+ p = s;
+ q = s + maxs - 1;
+ while ((*f != '\0'))
+ {
+ if (*f++ == '%')
+ {
+ r = buf;
+ switch (*f++)
+ {
+ case '%' :
+ r = "%";
+ break;
+
+ case 'a' :
+ r = aday[t->tm_wday];
+ break;
+
+ case 'A' :
+ r = day[t->tm_wday];
+ break;
+
+ case 'b' :
+ r = amonth[t->tm_mon];
+ break;
+
+ case 'B' :
+ r = month[t->tm_mon];
+ break;
+
+ case 'c' :
+ strfmt(r, "%0 %0 %2 %2:%2:%2 %4",
+ aday[t->tm_wday], amonth[t->tm_mon],
+ t->tm_mday,t->tm_hour, t->tm_min,
+ t->tm_sec, t->tm_year+1900);
+ break;
+
+ case 'd' :
+ strfmt(r,"%2",t->tm_mday);
+ break;
+
+ case 'H' :
+ strfmt(r,"%2",t->tm_hour);
+ break;
+
+ case 'I' :
+ strfmt(r,"%2",(t->tm_hour%12)?t->tm_hour%12:12);
+ break;
+
+ case 'j' :
+ strfmt(r,"%3",t->tm_yday+1);
+ break;
+
+ case 'm' :
+ strfmt(r,"%2",t->tm_mon+1);
+ break;
+
+ case 'M' :
+ strfmt(r,"%2",t->tm_min);
+ break;
+
+ case 'p' :
+ r = (t->tm_hour>11)?"PM":"AM";
+ break;
+
+ case 'S' :
+ strfmt(r,"%2",t->tm_sec);
+ break;
+
+ case 'U' :
+ w = t->tm_yday/7;
+ if (t->tm_yday%7 > t->tm_wday)
+ w++;
+ strfmt(r, "%2", w);
+ break;
+
+ case 'W' :
+ w = t->tm_yday/7;
+ if (t->tm_yday%7 > (t->tm_wday+6)%7)
+ w++;
+ strfmt(r, "%2", w);
+ break;
+
+ case 'V':
+
+ /* ISO 8601 Week Of Year:
+ If the week (Monday - Sunday) containing January 1 has four or more
+ days in the new year, then it is week 1; otherwise it is week 53 of
+ the previous year and the next week is week one. */
+
+ w = (t->tm_yday + 7 - (t->tm_wday ? t->tm_wday - 1 : 6)) / 7;
+ d = (t->tm_yday + 7 - (t->tm_wday ? t->tm_wday - 1 : 6)) % 7;
+
+ if (d >= 4) { w++; } else if (w == 0) { w = 53; }
+ strfmt(r, "%2", w);
+ break;
+
+ case 'w' :
+ strfmt(r,"%1",t->tm_wday);
+ break;
+
+ case 'x' :
+ strfmt(r, "%3s %3s %2 %4", aday[t->tm_wday],
+ amonth[t->tm_mon], t->tm_mday, t->tm_year+1900);
+ break;
+
+ case 'X' :
+ strfmt(r, "%2:%2:%2", t->tm_hour,
+ t->tm_min, t->tm_sec);
+ break;
+
+ case 'y' :
+ strfmt(r,"%2",t->tm_year%100);
+ break;
+
+ case 'Y' :
+ strfmt(r,"%4",t->tm_year+1900);
+ break;
+
+ case 'Z' :
+ r = (t->tm_isdst && tzname_[1][0]) ?
+ tzname_[1] : tzname_[0];
+ break;
+
+ default:
+ buf[0] = '%'; /* reconstruct the format */
+ buf[1] = f[-1];
+ buf[2] = '\0';
+ if (buf[1] == 0)
+ f--; /* back up if at end of string */
+ }
+ while (*r)
+ {
+ if (p == q)
+ {
+ *q = '\0';
+ return 0;
+ }
+ *p++ = *r++;
+ }
+ }
+ else
+ {
+ if (p == q)
+ {
+ *q = '\0';
+ return 0;
+ }
+ *p++ = f[-1];
+ }
+ }
+ *p = '\0';
+ return p - s;
+}
+
+/*
+ * stdarg.h
+ *
+typedef void *va_list;
+#define va_start(vp,v) (vp=((char*)&v)+sizeof(v))
+#define va_arg(vp,t) (*((t*)(vp))++)
+#define va_end(vp)
+ *
+ */
+
+static int powers[5] = { 1, 10, 100, 1000, 10000 };
+
+/**
+ * static void strfmt(char *str, char *fmt);
+ *
+ * simple sprintf for strftime
+ *
+ * each format descriptor is of the form %n
+ * where n goes from zero to four
+ *
+ * 0 -- string %s
+ * 1..4 -- int %?.?d
+ *
+**/
+
+static void strfmt(char *str, const char *fmt, ...)
+{
+ int ival, ilen;
+ char *sval;
+ va_list vp;
+
+ va_start(vp, fmt);
+ while (*fmt)
+ {
+ if (*fmt++ == '%')
+ {
+ ilen = *fmt++ - '0';
+ if (ilen == 0) /* zero means string arg */
+ {
+ sval = va_arg(vp, char*);
+ while (*sval)
+ *str++ = *sval++;
+ }
+ else /* always leading zeros */
+ {
+ ival = va_arg(vp, int);
+ while (ilen)
+ {
+ ival %= powers[ilen--];
+ *str++ = (char)('0' + ival / powers[ilen]);
+ }
+ }
+ }
+ else *str++ = fmt[-1];
+ }
+ *str = '\0';
+ va_end(vp);
+}
+
+#ifdef TEST
+
+#include <stdio.h> /* for printf */
+#include <time.h> /* for strftime */
+
+char test[80];
+
+int main(int argc, char *argv[])
+{
+ int len;
+ char *fmt;
+ time_t now;
+
+ time(&now);
+
+ fmt = (argc == 1) ? "%I:%M %p\n%c\n" : argv[1];
+ len = strftime_(test,sizeof test, fmt, localtime(&now));
+ printf("%d: %s\n", len, test);
+ return !len;
+}
+
+#endif /* TEST */
diff --git a/program/src/strftime.h b/program/src/strftime.h
--- /dev/null
+++ b/program/src/strftime.h
@@ -0,0 +1,20 @@
+/*
+** STRFTIME.H - For older compilers which lack strftime()
+**
+** Note: To avoid name collision with newer compilers, the function name
+** strftime_() is used.
+*/
+
+#ifndef STRFTIME__H
+#define STRFTIME__H
+
+#include <stddef.h> /* for size_t */
+#include <time.h> /* for struct tm */
+
+size_t strftime_(char *s, size_t maxs, const char *f, const struct tm *t);
+
+#if defined(TZNAME_STD) && defined(TZNAME_DST)
+extern char * tzname_[2];
+#endif
+
+#endif /* STRFTIME__H */
diff --git a/program/src/unused.h b/program/src/unused.h
--- /dev/null
+++ b/program/src/unused.h
@@ -0,0 +1,11 @@
+/* define a macro to wrap variables in that would
+ otherwhise generate UNUSED variable warnings */
+
+#ifdef UNUSED
+#elif defined(__GNUC__)
+# define UNUSED(x) x __attribute__((unused))
+#elif defined(__LCLINT__)
+# define UNUSED(x) /*@unused@*/ x
+#else
+# define UNUSED(x) x
+#endif
diff --git a/program/src/win32comp.c b/program/src/win32comp.c
--- /dev/null
+++ b/program/src/win32comp.c
@@ -0,0 +1,67 @@
+// compatibility routines, non reentrant ....
+
+#include <string.h>
+#include <time.h>
+
+struct tm* localtime_r(const time_t* t, struct tm* r) {
+ struct tm * temp;
+ temp = localtime(t);
+ memcpy(r,temp,sizeof(struct tm));
+ return(r);
+}
+
+struct tm* gmtime_r(const time_t* t, struct tm* r) {
+ struct tm * temp;
+ temp = gmtime(t);
+ memcpy(r,temp,sizeof(struct tm));
+ return r;
+}
+
+char* ctime_r (const time_t* t, char* buf) {
+ char * temp;
+ temp = asctime(localtime(t));
+ strcpy(buf,temp);
+ return(buf);
+}
+
+/*
+ s
+ Points to the string from which to extract tokens.
+
+ delim
+ Points to a null-terminated set of delimiter characters.
+
+ save_ptr
+ Is a value-return parameter used by strtok_r() to record its progress through s1.
+*/
+
+
+char * strtok_r (char *s, const char *delim, char **save_ptr) {
+ char *token;
+
+ if (s == NULL) s = *save_ptr;
+
+ /* Scan leading delimiters. */
+ s += strspn(s, delim);
+ if (*s == '\0')
+ {
+ *save_ptr = s;
+ return NULL;
+ }
+
+ /* Find the end of the token. */
+ token = s;
+ s = strpbrk (token, delim);
+ if (s == NULL) {
+ /* This token finishes the string. */
+ *save_ptr = token;
+ while (**save_ptr != '\0') (*save_ptr)++;
+ } else
+ {
+ /* Terminate the token and make *SAVE_PTR point past it. */
+ *s = '\0';
+ *save_ptr = s + 1;
+ }
+ return token;
+}
+
diff --git a/program/win32/Makefile b/program/win32/Makefile
--- /dev/null
+++ b/program/win32/Makefile
@@ -0,0 +1,460 @@
+# Gnu Makefile for Win32 target
+# for use with MingW32 gcc or Metrowerks CodeWarrior compiler
+# use with: make -f Makefile [help|all|clean|dev|devclean|dist|distclean]
+#
+# $id: $
+#
+
+DESCR = Round Robin Database Tool
+COPYR = Copyright (c) 1997-2007 by Tobias Oetiker
+WWWURL = http://www.rrdtool.org/
+ICON = $(PROOT)/favicon.ico
+
+# You can set the default font used in graphs.
+# If not set here RRD defaults to DejaVuSansMono-Roman.ttf
+#RRD_DEFAULT_FONT = "arial.ttf"
+#RRD_DEFAULT_FONT = "VeraMono.ttf"
+
+# Vertical label angle: 90.0 (default) or 270.0
+RRDGRAPH_YLEGEND_ANGLE = 90.0
+
+# Set to one if you want to have piecharts.
+WITH_PIECHART = 0
+
+# Set the extension used for rrdcgi.
+ifndef CGIEXT
+CGIEXT = exe
+endif
+
+# Base for the lib sources
+ifndef LIBBASE
+LIBBASE = ../..
+endif
+# All library code is statically linked to avoid problems with other lib DLLs.
+# Edit the path below to point to your libpng sources or set environment var.
+ifndef LIBPNG
+LIBPNG = $(LIBBASE)/libpng-1.2.16
+endif
+# Edit the path below to point to your freetype sources or set environment var.
+ifndef LIBFT2
+#LIBFT2 = $(LIBBASE)/freetype-2.3.4
+LIBFT2 = $(LIBBASE)/../mingw32/freetype-2.3.4
+endif
+# Edit the path below to point to your libart sources or set environment var.
+ifndef LIBART
+LIBART = $(LIBBASE)/libart_lgpl-2.3.17
+endif
+# Edit the path below to point to your zlib sources or set environment var.
+ifndef ZLIBSDK
+ZLIBSDK = $(LIBBASE)/zlib-1.2.3
+endif
+
+# Edit the path below to point to your distribution folder.
+ifndef DISTDIR
+DISTDIR = rrdtool-$(RRD_VERSION_STR)-w32
+endif
+DISTARC = $(DISTDIR).zip
+
+# Edit the path below to point to your distribution folder.
+ifndef DEVLDIR
+DEVLDIR = rrdtool-$(RRD_VERSION_STR)-sdk-w32
+endif
+DEVLARC = $(DEVLDIR).zip
+
+# whatever...
+NO_NULL_REALLOC = 1
+
+# The following line defines your compiler.
+ifdef METROWERKS
+ CC = mwcc
+else
+ CC = gcc
+endif
+# RM = rm -f
+CP = cp -afv
+# Here you can find a native Win32 binary of the original awk:
+# http://www.gknw.net/development/prgtools/awk.zip
+AWK = awk
+ZIP = zip -qzr9
+
+# must be equal to DEBUG or NDEBUG
+DB = NDEBUG
+# DB = DEBUG
+# Optimization: -O<n> or debugging: -g
+ifeq ($(DB),NDEBUG)
+ OPT = -O2
+ OBJDIR = release
+else
+ OPT = -g
+ OBJDIR = debug
+endif
+
+# Project root
+PROOT = ..
+
+# Include the version info retrieved from source.
+-include $(OBJDIR)/version.inc
+
+# Global flags for all compilers
+CFLAGS = $(OPT) -D$(DB) -DHAVE_CONFIG_H
+
+ifeq ($(CC),mwcc)
+LD = mwld
+RC = mwwinrc
+LDFLAGS = -nostdlib
+AR = $(LD)
+ARFLAGS = -type library -w nocmdline $(OBJS) -o
+LIBEXT = lib
+LIBPATH += -lr "$(METROWERKS)/MSL" -lr "$(METROWERKS)/Win32-x86 Support"
+LDLIBS += -lkernel32.lib -luser32.lib
+LDLIBS += -lMSL_Runtime_x86.lib -lMSL_C_x86.lib -lMSL_Extras_x86.lib
+RCFLAGS =
+CFLAGS += -DWIN32
+CFLAGS += -nostdinc -gccinc -msgstyle gcc -inline off -opt nointrinsics -proc 586
+CFLAGS += -ir "$(METROWERKS)/MSL" -ir "$(METROWERKS)/Win32-x86 Support"
+CFLAGS += -w on,nounused,nounusedexpr # -ansi strict
+else
+LD = gcc
+RC = windres
+LDFLAGS = -s
+AR = ar
+ARFLAGS = -cq
+LIBEXT = a
+RCFLAGS = -O coff -i
+CFLAGS += -fno-strict-aliasing
+CFLAGS += -Wall -Wno-unused # -pedantic
+endif
+
+ifeq ($(findstring msys,$(OSTYPE)),msys)
+DL = '
+DS = /
+else
+DS = \\
+endif
+
+ifndef DESCR
+ DESCR = $(notdir $(@:.rc=)) Command Extension
+endif
+DESCR += - $(CC) build
+
+INCLUDES += -I$(PROOT) -I$(PROOT)/src -I$(LIBPNG) -I$(LIBFT2)/include -I$(LIBART) -I$(ZLIBSDK)
+
+CFLAGS += $(INCLUDES)
+
+vpath %.c $(PROOT)/src $(LIBPNG) $(LIBART)/libart_lgpl $(ZLIBSDK)
+
+RRDLIBOBJS = \
+ $(OBJDIR)/rrd_afm.o \
+ $(OBJDIR)/rrd_afm_data.o \
+ $(OBJDIR)/rrd_create.o \
+ $(OBJDIR)/rrd_diff.o \
+ $(OBJDIR)/rrd_dump.o \
+ $(OBJDIR)/rrd_error.o \
+ $(OBJDIR)/rrd_fetch.o \
+ $(OBJDIR)/rrd_first.o \
+ $(OBJDIR)/rrd_format.o \
+ $(OBJDIR)/rrd_gfx.o \
+ $(OBJDIR)/rrd_graph.o \
+ $(OBJDIR)/rrd_graph_helper.o \
+ $(OBJDIR)/rrd_hw.o \
+ $(OBJDIR)/rrd_info.o \
+ $(OBJDIR)/rrd_last.o \
+ $(OBJDIR)/rrd_lastupdate.o \
+ $(OBJDIR)/rrd_nan_inf.o \
+ $(OBJDIR)/rrd_open.o \
+ $(OBJDIR)/rrd_resize.o \
+ $(OBJDIR)/rrd_restore.o \
+ $(OBJDIR)/rrd_rpncalc.o \
+ $(OBJDIR)/rrd_tune.o \
+ $(OBJDIR)/rrd_update.o \
+ $(OBJDIR)/rrd_version.o \
+ $(OBJDIR)/rrd_xport.o \
+ $(OBJDIR)/rrd_thread_safe_nt.o \
+ $(EOLIST)
+
+XLIBOBJS = \
+ $(OBJDIR)/rrd_getopt.o \
+ $(OBJDIR)/rrd_getopt1.o \
+ $(OBJDIR)/art_rgba_svp.o \
+ $(OBJDIR)/hash_32.o \
+ $(OBJDIR)/parsetime.o \
+ $(OBJDIR)/pngsize.o \
+ $(OBJDIR)/strftime.o \
+ $(EOLIST)
+
+PNGLIBOBJS = \
+ $(OBJDIR)/png.o \
+ $(OBJDIR)/pngerror.o \
+ $(OBJDIR)/pngget.o \
+ $(OBJDIR)/pngmem.o \
+ $(OBJDIR)/pngpread.o \
+ $(OBJDIR)/pngread.o \
+ $(OBJDIR)/pngrio.o \
+ $(OBJDIR)/pngrtran.o \
+ $(OBJDIR)/pngrutil.o \
+ $(OBJDIR)/pngset.o \
+ $(OBJDIR)/pngtrans.o \
+ $(OBJDIR)/pngwio.o \
+ $(OBJDIR)/pngwrite.o \
+ $(OBJDIR)/pngwtran.o \
+ $(OBJDIR)/pngwutil.o \
+ $(EOLIST)
+ifeq "$(wildcard $(LIBPNG)/pnggccrd.c)" "$(LIBPNG)/pnggccrd.c"
+PNGLIBOBJS += \
+ $(OBJDIR)/pnggccrd.o \
+ $(OBJDIR)/pngvcrd.o \
+ $(EOLIST)
+endif
+
+ZLIBOBJS = \
+ $(OBJDIR)/adler32.o \
+ $(OBJDIR)/compress.o \
+ $(OBJDIR)/crc32.o \
+ $(OBJDIR)/deflate.o \
+ $(OBJDIR)/inflate.o \
+ $(OBJDIR)/inffast.o \
+ $(OBJDIR)/inftrees.o \
+ $(OBJDIR)/trees.o \
+ $(OBJDIR)/zutil.o \
+ $(EOLIST)
+ifeq "$(wildcard $(ZLIBSDK)/infblock.c)" "$(ZLIBSDK)/infblock.c"
+ZLIBOBJS += \
+ $(OBJDIR)/infblock.o \
+ $(OBJDIR)/infcodes.o \
+ $(OBJDIR)/infutil.o \
+ $(EOLIST)
+endif
+
+ARTLIBOBJS = \
+ $(patsubst $(LIBART)/libart_lgpl/%.c,$(OBJDIR)/%.o,$(wildcard $(LIBART)/libart_lgpl/art_*.c))
+
+OBJS := $(RRDLIBOBJS) $(XLIBOBJS) $(PNGLIBOBJS) $(ARTLIBOBJS) $(ZLIBOBJS)
+OBJCGI := $(OBJS) $(OBJDIR)/rrd_cgi.o
+OBJTOOL := $(OBJS) $(OBJDIR)/rrd_tool.o
+
+LDLIBS += $(LIBFT2)/objs/freetype.$(LIBEXT)
+
+
+all: rrdtool rrdcgi
+
+rrdtool: $(OBJDIR) $(PROOT)/rrd_config.h $(OBJDIR)/rrdtool.exe
+rrdcgi: $(OBJDIR) $(PROOT)/rrd_config.h $(OBJDIR)/rrdcgi.$(CGIEXT)
+librrd: $(OBJDIR) $(PROOT)/rrd_config.h $(OBJDIR)/librrd.$(LIBEXT)
+
+FORCE: ;
+
+dist: all $(DISTDIR) $(DISTDIR)/readme.txt
+ @-$(CP) $(OBJDIR)/rrdcgi.$(CGIEXT) $(DISTDIR)
+ @-$(CP) $(OBJDIR)/rrdtool.exe $(DISTDIR)
+ @-$(CP) $(PROOT)/src/*.ttf $(DISTDIR)
+ @-$(CP) $(PROOT)/CHANGES $(DISTDIR)
+ @-$(CP) $(PROOT)/COPYING $(DISTDIR)
+ @-$(CP) $(PROOT)/COPYRIGHT $(DISTDIR)
+ @-$(CP) $(PROOT)/NEWS $(DISTDIR)
+ @-$(CP) $(PROOT)/README $(DISTDIR)
+ @echo Creating $(DISTARC)
+ @$(ZIP) $(DISTARC) $(DISTDIR)/* < $(DISTDIR)/readme.txt
+
+dev: librrd $(DEVLDIR) $(DEVLDIR)/readme.txt
+ @-mkdir $(DEVLDIR)$(DS)include
+ @-mkdir $(DEVLDIR)$(DS)lib
+ @-mkdir $(DEVLDIR)$(DS)src
+ @-$(CP) $(OBJDIR)/librrd.$(LIBEXT) $(DEVLDIR)/lib
+ @-$(CP) $(PROOT)/rrd_config.h $(DEVLDIR)/include
+ @-$(CP) $(PROOT)/src/rrd.h $(DEVLDIR)/include
+ @-$(CP) $(PROOT)/src/*.ttf $(DEVLDIR)/src
+ @-$(CP) $(PROOT)/CHANGES $(DEVLDIR)
+ @-$(CP) $(PROOT)/COPYING $(DEVLDIR)
+ @-$(CP) $(PROOT)/COPYRIGHT $(DEVLDIR)
+ @-$(CP) $(PROOT)/NEWS $(DEVLDIR)
+ @-$(CP) $(PROOT)/README $(DEVLDIR)
+ @echo Creating $(DEVLARC)
+ @$(ZIP) $(DEVLARC) $(DEVLDIR)/* < $(DEVLDIR)/readme.txt
+
+clean:
+ -$(RM) -r $(OBJDIR)
+ -$(RM) $(PROOT)/rrd_config.h
+
+distclean: clean
+ -$(RM) -r $(DISTDIR)
+ -$(RM) $(DISTARC)
+
+devclean: clean
+ -$(RM) -r $(DEVLDIR)
+ -$(RM) $(DEVLARC)
+
+$(OBJDIR):
+ @mkdir $@
+
+$(DISTDIR):
+ @mkdir $@
+
+$(DEVLDIR):
+ @mkdir $@
+
+$(OBJDIR)/version.inc: $(PROOT)/configure.ac $(OBJDIR) $(PROOT)/src/get_ver.awk
+ @echo Creating $@
+ @$(AWK) -f $(PROOT)/src/get_ver.awk $< > $@
+
+$(OBJDIR)/%.o: %.c
+ @echo Compiling $<
+ @$(CC) $(CFLAGS) -c $< -o $@
+
+$(OBJDIR)/rrdcgi.$(CGIEXT): $(OBJCGI) $(OBJDIR)/rrdcgi.res
+ @echo Linking $@
+ @-$(RM) $@
+ @$(LD) $(LDFLAGS) $^ -o $@ $(LIBPATH) $(LDLIBS)
+
+$(OBJDIR)/rrdtool.exe: $(OBJTOOL) $(OBJDIR)/rrdtool.res
+ @echo Linking $@
+ @-$(RM) $@
+ @$(LD) $(LDFLAGS) $^ -o $@ $(LIBPATH) $(LDLIBS)
+
+$(OBJDIR)/librrd.$(LIBEXT): $(OBJS)
+ @echo Creating $@
+ @-$(RM) $@
+ @$(AR) $(ARFLAGS) $@ $^
+
+$(OBJDIR)/%.res: $(OBJDIR)/%.rc
+ @echo Creating $@
+ @$(RC) $(RCFLAGS) $< -o $@
+
+$(OBJDIR)/%.rc: Makefile $(OBJDIR)/version.inc
+ @echo $(DL)1 VERSIONINFO$(DL) > $@
+ @echo $(DL) FILEVERSION $(RRD_VERSION),0$(DL) >> $@
+ @echo $(DL) PRODUCTVERSION $(RRD_VERSION),0$(DL) >> $@
+ @echo $(DL) FILEFLAGSMASK 0x3fL$(DL) >> $@
+ @echo $(DL) FILEOS 0x40004L$(DL) >> $@
+ @echo $(DL) FILEFLAGS 0x0L$(DL) >> $@
+ @echo $(DL) FILETYPE 0x1L$(DL) >> $@
+ @echo $(DL) FILESUBTYPE 0x0L$(DL) >> $@
+ @echo $(DL)BEGIN$(DL) >> $@
+ @echo $(DL) BLOCK "StringFileInfo"$(DL) >> $@
+ @echo $(DL) BEGIN$(DL) >> $@
+ @echo $(DL) BLOCK "040904E4"$(DL) >> $@
+ @echo $(DL) BEGIN$(DL) >> $@
+ @echo $(DL) VALUE "LegalCopyright","$(COPYR)\0"$(DL) >> $@
+ifdef COMPANY
+ @echo $(DL) VALUE "CompanyName","$(COMPANY)\0"$(DL) >> $@
+endif
+ @echo $(DL) VALUE "ProductName","$(notdir $(@:.rc=.exe))\0"$(DL) >> $@
+ @echo $(DL) VALUE "ProductVersion","$(RRD_VERSION_STR)\0"$(DL) >> $@
+ @echo $(DL) VALUE "License","Released under GPL.\0"$(DL) >> $@
+ @echo $(DL) VALUE "FileDescription","$(DESCR)\0"$(DL) >> $@
+ @echo $(DL) VALUE "FileVersion","$(RRD_VERSION_STR)\0"$(DL) >> $@
+ @echo $(DL) VALUE "InternalName","$(notdir $(@:.rc=))\0"$(DL) >> $@
+ @echo $(DL) VALUE "OriginalFilename","$(notdir $(@:.rc=.exe))\0"$(DL) >> $@
+ @echo $(DL) VALUE "WWW","$(WWWURL)\0"$(DL) >> $@
+ @echo $(DL) END$(DL) >> $@
+ @echo $(DL) END$(DL) >> $@
+ @echo $(DL) BLOCK "VarFileInfo"$(DL) >> $@
+ @echo $(DL) BEGIN$(DL) >> $@
+ @echo $(DL) VALUE "Translation", 0x409, 1252$(DL) >> $@
+ @echo $(DL) END$(DL) >> $@
+ @echo $(DL)END$(DL) >> $@
+ifdef ICON
+ @echo $(DL)10 ICON DISCARDABLE "$(ICON)"$(DL) >> $@
+endif
+
+$(PROOT)/rrd_config.h: FORCE Makefile $(OBJDIR)/version.inc
+ @echo Creating $@
+ @echo $(DL)/* $(notdir $@) for Win32 target.$(DL) > $@
+ @echo $(DL)** Do not edit this file - it is created by make!$(DL) >> $@
+ @echo $(DL)** All your changes will be lost!!$(DL) >> $@
+ @echo $(DL)*/$(DL) >> $@
+ @echo $(DL)#ifndef WIN32$(DL) >> $@
+ @echo $(DL)#error This $(notdir $@) is created for Win32 platform!$(DL) >> $@
+ @echo $(DL)#endif$(DL) >> $@
+ @echo $(DL)#ifndef RRD_CONFIG_H$(DL) >> $@
+ @echo $(DL)#define RRD_CONFIG_H$(DL) >> $@
+ @echo $(DL)#define OS "i586-pc-Win32"$(DL) >> $@
+ @echo $(DL)#define PACKAGE_VERSION "$(RRD_VERSION_STR)"$(DL) >> $@
+ @echo $(DL)#define PACKAGE_BUGREPORT "tobi@oetiker.ch"$(DL) >> $@
+ @echo $(DL)#define NUMVERS $(RRD_NUMVERS)$(DL) >> $@
+ @echo $(DL)#define HAVE_ASSERT_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_DLFCN_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_DLOPEN 1$(DL) >> $@
+ @echo $(DL)#define HAVE_ERRNO_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_FCNTL_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_FIONBIO 1$(DL) >> $@
+ @echo $(DL)#define HAVE_INTTYPES_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_LIMITS_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_LONGLONG 1$(DL) >> $@
+ @echo $(DL)#define HAVE_LOCALE_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_MALLOC_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_MATH_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_MBSTOWCS 1$(DL) >> $@
+ @echo $(DL)#define HAVE_SETJMP_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_SNPRINTF 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STDARG_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STDDEF_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STDINT_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STDLIB_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STRCASECMP 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STRDUP 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STRFTIME 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STRING_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STRLCAT 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STRLCPY 1$(DL) >> $@
+ @echo $(DL)#define HAVE_STRSTR 1$(DL) >> $@
+ @echo $(DL)#define HAVE_SYS_PARAM_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_SYS_STAT_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_SYS_TIME_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_TIME_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_VSNPRINTF 1$(DL) >> $@
+ @echo $(DL)#define STDC_HEADERS 1$(DL) >> $@
+ @echo $(DL)#define TIME_WITH_SYS_TIME 1$(DL) >> $@
+ @echo $(DL)#define HAVE_ZLIB_H 1$(DL) >> $@
+ @echo $(DL)#define HAVE_LIBZ 1$(DL) >> $@
+ifdef NO_NULL_REALLOC
+ @echo $(DL)#define NO_NULL_REALLOC 1$(DL) >> $@
+ @echo $(DL)#define rrd_realloc(a,b) ( (a) == NULL ? malloc( (b) ) : realloc( (a) , (b) ))$(DL) >> $@
+else
+ @echo $(DL)#define rrd_realloc(a,b) realloc((a), (b))$(DL) >> $@
+endif
+ifdef RRD_DEFAULT_FONT
+ @echo $(DL)#define RRD_DEFAULT_FONT $(RRD_DEFAULT_FONT)$(DL) >> $@
+endif
+ @echo $(DL)#define RRDGRAPH_YLEGEND_ANGLE $(RRDGRAPH_YLEGEND_ANGLE)$(DL) >> $@
+ @echo $(DL)#define strftime strftime_$(DL) >> $@
+ifdef WITH_PIECHART
+ @echo $(DL)#define WITH_PIECHART $(WITH_PIECHART)$(DL) >> $@
+endif
+ @echo $(DL)#endif /* RRD_CONFIG_H */$(DL) >> $@
+
+$(DISTDIR)/readme.txt: Makefile
+ @echo Creating $@
+ @echo $(DL)This is a binary distribution for Win32 platform.$(DL) > $@
+ @echo $(DL)RRDTool version $(RRD_VERSION_STR)$(DL) >> $@
+ @echo $(DL)Please download the complete RRDTool package for$(DL) >> $@
+ @echo $(DL)any further documentation:$(DL) >> $@
+ @echo $(DL)$(WWWURL)$(DL) >> $@
+
+$(DEVLDIR)/readme.txt: Makefile
+ @echo Creating $@
+ @echo $(DL)This is a development distribution for Win32 platform.$(DL) > $@
+ @echo $(DL)RRDTool version $(RRD_VERSION_STR)$(DL) >> $@
+ @echo $(DL)Please download the complete RRDTool package for$(DL) >> $@
+ @echo $(DL)any further documentation:$(DL) >> $@
+ @echo $(DL)$(WWWURL)$(DL) >> $@
+
+help:
+ @echo $(DL)===========================================================$(DL)
+ @echo $(DL)libpng Source = $(LIBPNG)$(DL)
+ @echo $(DL)libart Source = $(LIBART)$(DL)
+ @echo $(DL)Freetype 2 SDK = $(LIBFT2)$(DL)
+ @echo $(DL)Zlib SDK = $(ZLIBSDK)$(DL)
+ @echo $(DL)===========================================================$(DL)
+ @echo $(DL)RRDTool $(RRD_VERSION_STR) - available targets are:$(DL)
+ @echo $(DL)$(MAKE) all$(DL)
+ @echo $(DL)$(MAKE) rrdtool$(DL)
+ @echo $(DL)$(MAKE) rrdcgi$(DL)
+ @echo $(DL)$(MAKE) librrd$(DL)
+ @echo $(DL)$(MAKE) clean$(DL)
+ @echo $(DL)$(MAKE) dev$(DL)
+ @echo $(DL)$(MAKE) devclean$(DL)
+ @echo $(DL)$(MAKE) dist$(DL)
+ @echo $(DL)$(MAKE) distclean$(DL)
+ @echo $(DL)===========================================================$(DL)
+
+
diff --git a/program/win32/config.h b/program/win32/config.h
--- /dev/null
+++ b/program/win32/config.h
@@ -0,0 +1,60 @@
+/* config.h.msvc. Hand-tweaked config.h for MSVC compiler. */
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include <math.h>
+#include <float.h>
+#include <direct.h>
+
+/* realloc does not support NULL as argument */
+
+#define HAVE_STRFTIME 1
+#define HAVE_TIME_H 1
+#define HAVE_LOCALE_H 1
+#define HAVE_TZSET 1
+#define HAVE_SETLOCALE 1
+#define HAVE_MATH_H 1
+#define HAVE_FLOAT_H 1
+#define HAVE_MEMMOVE 1
+#define HAVE_MALLOC_H 1
+#define HAVE_MKTIME 1
+#define HAVE_STRFTIME 1
+#define HAVE_STRING_H 1
+#define HAVE_VSNPRINTF 1
+#define HAVE_SYS_TYPES_H 1
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+#define NUMVERS 1.2015
+#define PACKAGE_NAME "rrdtool"
+#define PACKAGE_VERSION "1.2.15"
+#define PACKAGE_STRING PACKAGE_NAME " " PACKAGE_VERSION
+
+#define isinf(a) (_fpclass(a) == _FPCLASS_NINF || _fpclass(a) == _FPCLASS_PINF)
+#define isnan _isnan
+#define finite _finite
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#define strftime strftime_
+
+#define NO_NULL_REALLOC 1
+#if NO_NULL_REALLOC
+# define rrd_realloc(a,b) ( (a) == NULL ? malloc( (b) ) : realloc( (a) , (b) ))
+#else
+# define rrd_realloc(a,b) realloc((a), (b))
+#endif
+
+/* Vertical label angle: 90.0 (default) or 270.0 */
+#define RRDGRAPH_YLEGEND_ANGLE 90.0
+
+#define RRD_DEFAULT_FONT "arial.ttf"
+/* #define RRD_DEFAULT_FONT "DejaVuSansMono-Roman.ttf" */
+
+/* #define WITH_PIECHART 1 */
+
+/* #define DEBUG 1 */
+
+#endif /* CONFIG_H */
+
diff --git a/program/win32/rrd.dsp b/program/win32/rrd.dsp
--- /dev/null
+++ b/program/win32/rrd.dsp
@@ -0,0 +1,247 @@
+# Microsoft Developer Studio Project File - Name="rrd" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Static Library" 0x0104\r
+\r
+CFG=rrd - Win32 Debug\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "rrd.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "rrd.mak" CFG="rrd - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "rrd - Win32 Release" (based on "Win32 (x86) Static Library")\r
+!MESSAGE "rrd - Win32 Debug" (based on "Win32 (x86) Static Library")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+RSC=rc.exe\r
+\r
+!IF "$(CFG)" == "rrd - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "rrd___Wi"\r
+# PROP BASE Intermediate_Dir "rrd___Wi"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "release"\r
+# PROP Intermediate_Dir "release"\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c\r
+# ADD CPP /nologo /MD /W3 /GX /I "../src" /I "../../zlib-1.2.3" /I "../../libpng-1.2.16" /I "../../libart_lgpl-2.3.17" /I "../../freetype-2.3.1/include" /D "HAVE_CONFIG_H" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_CTYPE_DISABLE_MACROS" /FD /c\r
+# SUBTRACT CPP /X /YX\r
+# ADD BASE RSC /l 0x100c\r
+# ADD RSC /l 0x409\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LIB32=link.exe -lib\r
+# ADD BASE LIB32 /nologo\r
+# ADD LIB32 /nologo\r
+\r
+!ELSEIF "$(CFG)" == "rrd - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "rrd___W0"\r
+# PROP BASE Intermediate_Dir "rrd___W0"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "debug"\r
+# PROP Intermediate_Dir "debug"\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c\r
+# ADD CPP /nologo /MD /W3 /Gm /GX /ZI /Od /I "../src" /I "../../zlib-1.2.3" /I "../../libpng-1.2.16" /I "../../libart_lgpl-2.3.17" /I "../../freetype-2.3.1/include" /D "HAVE_CONFIG_H" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "_CTYPE_DISABLE_MACROS" /FR /FD /c\r
+# SUBTRACT CPP /X /YX\r
+# ADD BASE RSC /l 0x100c\r
+# ADD RSC /l 0x409\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo /o"rrd.bsc"\r
+LIB32=link.exe -lib\r
+# ADD BASE LIB32 /nologo\r
+# ADD LIB32 /nologo\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "rrd - Win32 Release"\r
+# Name "rrd - Win32 Debug"\r
+# Begin Source File\r
+\r
+SOURCE="..\src\get_ver.awk"\r
+\r
+!IF "$(CFG)" == "rrd - Win32 Release"\r
+\r
+# PROP Ignore_Default_Tool 1\r
+# Begin Custom Build - Creating ..\rrd_config.h\r
+InputPath="..\src\get_ver.awk"\r
+\r
+"..\rrd_config.h" : $(SOURCE) "..\configure.ac" "..\win32\rrd_config.h.msvc"\r
+ awk -f ..\src\get_ver.awk ..\configure.ac ..\win32\rrd_config.h.msvc > ..\rrd_config.h\r
+\r
+# End Custom Build\r
+\r
+!ELSEIF "$(CFG)" == "rrd - Win32 Debug"\r
+\r
+# PROP Ignore_Default_Tool 1\r
+# Begin Custom Build - Creating ..\rrd_config.h\r
+InputPath="..\src\get_ver.awk"\r
+\r
+"..\rrd_config.h" : $(SOURCE) "..\configure.ac" "..\win32\rrd_config.h.msvc"\r
+ awk -f ..\src\get_ver.awk ..\configure.ac ..\win32\rrd_config.h.msvc > ..\rrd_config.h\r
+\r
+# End Custom Build\r
+\r
+!ENDIF\r
+\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_afm.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_afm_data.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_create.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_diff.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_dump.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_error.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_fetch.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_first.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_format.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_gfx.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_graph.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_graph_helper.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_hw.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_info.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_last.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_lastupdate.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_nan_inf.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_open.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_resize.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_restore.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_rpncalc.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_thread_safe_nt.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_tune.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_update.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_version.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_xport.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_getopt.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_getopt1.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\art_rgba_svp.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\hash_32.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\parsetime.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\pngsize.c\r
+# End Source File\r
+# Begin Source File\r
+\r
+SOURCE=..\src\strftime.c\r
+# End Source File\r
+# End Target\r
+# End Project\r
diff --git a/program/win32/rrd.vcproj b/program/win32/rrd.vcproj
--- /dev/null
+++ b/program/win32/rrd.vcproj
@@ -0,0 +1,648 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+ ProjectType="Visual C++"\r
+ Version="7.10"\r
+ Name="rrd"\r
+ SccProjectName=""\r
+ SccLocalPath="">\r
+ <Platforms>\r
+ <Platform\r
+ Name="Win32"/>\r
+ </Platforms>\r
+ <Configurations>\r
+ <Configuration\r
+ Name="Release|Win32"\r
+ OutputDirectory=".\release"\r
+ IntermediateDirectory=".\release"\r
+ ConfigurationType="4"\r
+ UseOfMFC="0"\r
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"\r
+ CharacterSet="2">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="4"\r
+ AdditionalIncludeDirectories="\Program Files\GnuWin32\include,\Program Files\GnuWin32\include\freetype2"\r
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_CTYPE_DISABLE_MACROS"\r
+ RuntimeLibrary="2"\r
+ PrecompiledHeaderFile=".\release/rrd.pch"\r
+ AssemblerListingLocation=".\release/"\r
+ ObjectFile=".\release/"\r
+ ProgramDataBaseFileName=".\release/"\r
+ WarningLevel="3"\r
+ SuppressStartupBanner="TRUE"\r
+ CompileAs="0"/>\r
+ <Tool\r
+ Name="VCCustomBuildTool"/>\r
+ <Tool\r
+ Name="VCLibrarianTool"\r
+ OutputFile=".\release\rrd.lib"\r
+ SuppressStartupBanner="TRUE"/>\r
+ <Tool\r
+ Name="VCMIDLTool"/>\r
+ <Tool\r
+ Name="VCPostBuildEventTool"/>\r
+ <Tool\r
+ Name="VCPreBuildEventTool"/>\r
+ <Tool\r
+ Name="VCPreLinkEventTool"/>\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ Culture="4108"/>\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"/>\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"/>\r
+ <Tool\r
+ Name="VCManagedWrapperGeneratorTool"/>\r
+ <Tool\r
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>\r
+ </Configuration>\r
+ <Configuration\r
+ Name="Debug|Win32"\r
+ OutputDirectory=".\debug"\r
+ IntermediateDirectory=".\debug"\r
+ ConfigurationType="4"\r
+ UseOfMFC="0"\r
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"\r
+ CharacterSet="2">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories="\Program Files\GnuWin32\include\freetype2,\Program Files\GnuWin32\include"\r
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_CTYPE_DISABLE_MACROS"\r
+ RuntimeLibrary="2"\r
+ PrecompiledHeaderFile=".\debug/rrd.pch"\r
+ AssemblerListingLocation=".\debug/"\r
+ ObjectFile=".\debug/"\r
+ ProgramDataBaseFileName=".\debug/"\r
+ BrowseInformation="1"\r
+ WarningLevel="3"\r
+ SuppressStartupBanner="TRUE"\r
+ DebugInformationFormat="4"\r
+ CompileAs="0"/>\r
+ <Tool\r
+ Name="VCCustomBuildTool"/>\r
+ <Tool\r
+ Name="VCLibrarianTool"\r
+ OutputFile=".\debug\rrd.lib"\r
+ SuppressStartupBanner="TRUE"/>\r
+ <Tool\r
+ Name="VCMIDLTool"/>\r
+ <Tool\r
+ Name="VCPostBuildEventTool"/>\r
+ <Tool\r
+ Name="VCPreBuildEventTool"/>\r
+ <Tool\r
+ Name="VCPreLinkEventTool"/>\r
+ <Tool\r
+ Name="VCResourceCompilerTool"\r
+ Culture="4108"/>\r
+ <Tool\r
+ Name="VCWebServiceProxyGeneratorTool"/>\r
+ <Tool\r
+ Name="VCXMLDataGeneratorTool"/>\r
+ <Tool\r
+ Name="VCManagedWrapperGeneratorTool"/>\r
+ <Tool\r
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>\r
+ </Configuration>\r
+ </Configurations>\r
+ <References>\r
+ </References>\r
+ <Files>\r
+ <File\r
+ RelativePath="getopt.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="getopt1.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="hash_32.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="parsetime.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="pngsize.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_afm.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_afm_data.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_create.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_diff.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_dump.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_error.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_fetch.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_format.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_gfx.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_graph.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_graph_helper.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_hw.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_info.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_last.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_nan_inf.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_open.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_resize.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_restore.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_rpncalc.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_thread_safe_nt.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_tune.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_update.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ <File\r
+ RelativePath="rrd_xport.c">\r
+ <FileConfiguration\r
+ Name="Release|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""/>\r
+ </FileConfiguration>\r
+ <FileConfiguration\r
+ Name="Debug|Win32">\r
+ <Tool\r
+ Name="VCCLCompilerTool"\r
+ Optimization="0"\r
+ AdditionalIncludeDirectories=""\r
+ PreprocessorDefinitions=""\r
+ BrowseInformation="1"/>\r
+ </FileConfiguration>\r
+ </File>\r
+ </Files>\r
+ <Globals>\r
+ </Globals>\r
+</VisualStudioProject>\r
diff --git a/program/win32/rrd_config.h.msvc b/program/win32/rrd_config.h.msvc
--- /dev/null
@@ -0,0 +1,65 @@
+/* rrd_config.h.msvc. Hand-tweaked rrd_config.h for MSVC compiler. */\r
+#ifndef WIN32 \r
+#error This rrd_config.h is created for Win32 platform! \r
+#endif \r
+#ifndef RRD_CONFIG_H\r
+#define RRD_CONFIG_H\r
+\r
+#include <math.h>\r
+#include <float.h>\r
+#include <direct.h>\r
+\r
+/* the placeholders will be filled in by get_ver.awk */\r
+/* http://cm.bell-labs.com/cm/cs/awkbook/index.html */\r
+#define NUMVERS @@NUMVERS@@\r
+#define PACKAGE_VERSION "@@PACKAGE_VERSION@@"\r
+\r
+#define PACKAGE_NAME "rrdtool"\r
+#define PACKAGE_STRING PACKAGE_NAME " " PACKAGE_VERSION\r
+\r
+#define HAVE_STRFTIME 1\r
+#define HAVE_TIME_H 1\r
+#define HAVE_LOCALE_H 1\r
+#define HAVE_TZSET 1\r
+#define HAVE_SETLOCALE 1\r
+#define HAVE_MATH_H 1\r
+#define HAVE_FLOAT_H 1\r
+#define HAVE_MEMMOVE 1\r
+#define HAVE_MALLOC_H 1\r
+#define HAVE_MKTIME 1\r
+#define HAVE_STRFTIME 1\r
+#define HAVE_STRING_H 1\r
+#define HAVE_VSNPRINTF 1\r
+#define HAVE_SYS_TYPES_H 1\r
+#define HAVE_SYS_STAT_H 1\r
+\r
+/* Define to 1 if you have the ANSI C header files. */\r
+#define STDC_HEADERS 1\r
+\r
+#define isinf(a) (_fpclass(a) == _FPCLASS_NINF || _fpclass(a) == _FPCLASS_PINF)\r
+#define isnan _isnan\r
+#define finite _finite\r
+#define snprintf _snprintf\r
+#define vsnprintf _vsnprintf\r
+#define strftime strftime_ \r
+\r
+/* realloc does not support NULL as argument */\r
+#define NO_NULL_REALLOC 1\r
+#if NO_NULL_REALLOC\r
+# define rrd_realloc(a,b) ( (a) == NULL ? malloc( (b) ) : realloc( (a) , (b) ))\r
+#else\r
+# define rrd_realloc(a,b) realloc((a), (b))\r
+#endif \r
+\r
+/* Vertical label angle: 90.0 (default) or 270.0 */\r
+#define RRDGRAPH_YLEGEND_ANGLE 90.0\r
+\r
+#define RRD_DEFAULT_FONT "arial.ttf"\r
+/* #define RRD_DEFAULT_FONT "DejaVuSansMono-Roman.ttf" */\r
+\r
+/* #define WITH_PIECHART 1 */\r
+\r
+/* #define DEBUG 1 */\r
+\r
+#endif /* RRD_CONFIG_H */\r
+\r
diff --git a/program/win32/rrdtool.dsp b/program/win32/rrdtool.dsp
--- /dev/null
@@ -0,0 +1,92 @@
+# Microsoft Developer Studio Project File - Name="rrdtool" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** DO NOT EDIT **\r
+\r
+# TARGTYPE "Win32 (x86) Console Application" 0x0103\r
+\r
+CFG=rrdtool - Win32 Debug\r
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r
+!MESSAGE use the Export Makefile command and run\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "rrdtool.mak".\r
+!MESSAGE \r
+!MESSAGE You can specify a configuration when running NMAKE\r
+!MESSAGE by defining the macro CFG on the command line. For example:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "rrdtool.mak" CFG="rrdtool - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Possible choices for configuration are:\r
+!MESSAGE \r
+!MESSAGE "rrdtool - Win32 Release" (based on "Win32 (x86) Console Application")\r
+!MESSAGE "rrdtool - Win32 Debug" (based on "Win32 (x86) Console Application")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+RSC=rc.exe\r
+\r
+!IF "$(CFG)" == "rrdtool - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "rrdtool_"\r
+# PROP BASE Intermediate_Dir "rrdtool_"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "toolrelease"\r
+# PROP Intermediate_Dir "toolrelease"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c\r
+# ADD CPP /nologo /MD /W3 /GX /I "../src" /I "../../zlib-1.2.3" /I "../../libpng-1.2.16" /I "../../libart_lgpl-2.3.17" /I "../../freetype-2.3.1/include" /D "HAVE_CONFIG_H" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_CTYPE_DISABLE_MACROS" /FD /c\r
+# SUBTRACT CPP /YX\r
+# ADD BASE RSC /l 0x100c /d "NDEBUG"\r
+# ADD RSC /l 0x409 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386\r
+# ADD LINK32 libpng.lib zlib.lib libart.lib freetype231MT.lib kernel32.lib user32.lib /nologo /subsystem:console /incremental:yes /debug /machine:I386 /libpath:"../../libpng-1.2.16/projects/visualc6/Win32_LIB_Release" /libpath:"../../zlib-1.2.3" /libpath:"../../libart_lgpl-2.3.17/win32/release" /libpath:"../../freetype-2.3.1/objs"\r
+\r
+!ELSEIF "$(CFG)" == "rrdtool - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "rrdtool0"\r
+# PROP BASE Intermediate_Dir "rrdtool0"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "tooldebug"\r
+# PROP Intermediate_Dir "tooldebug"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c\r
+# ADD CPP /nologo /MD /W3 /Gm /GX /ZI /Od /I "../src" /I "../../zlib-1.2.3" /I "../../libpng-1.2.16" /I "../../libart_lgpl-2.3.17" /I "../../freetype-2.3.1/include" /D "HAVE_CONFIG_H" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "_CTYPE_DISABLE_MACROS" /FR /FD /c\r
+# SUBTRACT CPP /YX\r
+# ADD BASE RSC /l 0x100c /d "_DEBUG"\r
+# ADD RSC /l 0x409 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo /o"rrdtool.bsc"\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 libpng.lib zlib.lib libart.lib freetype231MT.lib kernel32.lib user32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"../../libpng-1.2.16/projects/visualc6/Win32_LIB_Release" /libpath:"../../zlib-1.2.3" /libpath:"../../libart_lgpl-2.3.17/win32/release" /libpath:"../../freetype-2.3.1/objs"\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "rrdtool - Win32 Release"\r
+# Name "rrdtool - Win32 Debug"\r
+# Begin Source File\r
+\r
+SOURCE=..\src\rrd_tool.c\r
+# End Source File\r
+# End Target\r
+# End Project\r
diff --git a/program/win32/rrdtool.dsw b/program/win32/rrdtool.dsw
--- /dev/null
@@ -0,0 +1,44 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00\r
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\r
+\r
+###############################################################################\r
+\r
+Project: "rrd"=".\rrd.dsp" - Package Owner=<4>\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<4>\r
+{{{\r
+}}}\r
+\r
+###############################################################################\r
+\r
+Project: "rrdtool"=".\rrdtool.dsp" - Package Owner=<4>\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<4>\r
+{{{\r
+ Begin Project Dependency\r
+ Project_Dep_Name rrd\r
+ End Project Dependency\r
+}}}\r
+\r
+###############################################################################\r
+\r
+Global:\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<3>\r
+{{{\r
+}}}\r
+\r
+###############################################################################\r
+\r
diff --git a/program/win32/rrdtool.vcproj b/program/win32/rrdtool.vcproj
--- /dev/null
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="rrdtool"
+ SccProjectName=""
+ SccLocalPath="">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory=".\toolrelease"
+ IntermediateDirectory=".\toolrelease"
+ ConfigurationType="1"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="4"
+ AdditionalIncludeDirectories="\Program Files\GnuWin32\include,\Program Files\GnuWin32\include\freetype2"
+ PreprocessorDefinitions="NDEBUG;_WINDOWS;WIN32;_CTYPE_DISABLE_MACROS"
+ RuntimeLibrary="2"
+ PrecompiledHeaderFile=".\toolrelease/rrdtool.pch"
+ AssemblerListingLocation=".\toolrelease/"
+ ObjectFile=".\toolrelease/"
+ ProgramDataBaseFileName=".\toolrelease/"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="libpng.lib libz.lib libart_lgpl.lib libfreetype.lib"
+ OutputFile=".\toolrelease/rrdtool.exe"
+ LinkIncremental="1"
+ SuppressStartupBanner="TRUE"
+ AdditionalLibraryDirectories="\Program Files\GnuWin32\lib"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile=".\toolrelease/rrdtool.pdb"
+ SubSystem="1"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ TypeLibraryName=".\toolrelease/rrdtool.tlb"
+ HeaderFileName=""/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="4108"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\tooldebug"
+ IntermediateDirectory=".\tooldebug"
+ ConfigurationType="1"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="\Program Files\GnuWin32\include\freetype2,\Program Files\GnuWin32\include"
+ PreprocessorDefinitions="_DEBUG;_CONSOLE;WIN32;_CTYPE_DISABLE_MACROS"
+ RuntimeLibrary="2"
+ PrecompiledHeaderFile=".\tooldebug/rrdtool.pch"
+ AssemblerListingLocation=".\tooldebug/"
+ ObjectFile=".\tooldebug/"
+ ProgramDataBaseFileName=".\tooldebug/"
+ BrowseInformation="1"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="4"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="libpng.lib libz.lib libart_lgpl.lib libfreetype.lib"
+ OutputFile=".\tooldebug/rrdtool.exe"
+ LinkIncremental="1"
+ SuppressStartupBanner="TRUE"
+ AdditionalLibraryDirectories="\Program Files\GnuWin32\lib"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile=".\tooldebug/rrdtool.pdb"
+ SubSystem="1"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ TypeLibraryName=".\tooldebug/rrdtool.tlb"
+ HeaderFileName=""/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="4108"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath="rrd_tool.c">
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BrowseInformation="1"/>
+ </FileConfiguration>
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>