GraphvizParser

Changeset

5:93b8e78cfcc2
2012-01-14 Paul Boddie raw files shortlog changelog graph Moved the module into the parsers directory. Expanded the documentation. Added copyright and licensing information.
README.txt (file) docs/COPYING.txt (file) docs/LICENCE.txt (file) graphviz.py parsers/graphviz.py (file)
     1.1 --- a/README.txt	Sat Jan 14 19:17:36 2012 +0100
     1.2 +++ b/README.txt	Sat Jan 14 20:40:47 2012 +0100
     1.3 @@ -1,7 +1,27 @@
     1.4 +Introduction
     1.5 +------------
     1.6 +
     1.7 +The graphviz parser module allows descriptions of graphs suitable for
     1.8 +interpretation by the Graphviz suite of tools to be embedded within Wiki
     1.9 +pages.
    1.10 +
    1.11 +Installation
    1.12 +------------
    1.13 +
    1.14 +First, make sure that the BINARY_PATH setting in the graphviz.py module (found
    1.15 +in the parsers directory) is suitable for your system. Then, copy the
    1.16 +graphviz.py module into your Wiki's parsers directory.
    1.17 +
    1.18 +With moinsetup and a suitable configuration file (see "Recommended Software"
    1.19 +below), you can perform this last step as follows, with $GVDIR referring to the
    1.20 +GraphvizParser distribution directory:
    1.21 +
    1.22 +  python moinsetup.py -f moinsetup.cfg -m install_parsers $GVDIR/parsers
    1.23 +
    1.24  Basic Usage
    1.25  -----------
    1.26  
    1.27 -Embed a visualization of a graph in a wiki page:
    1.28 +To embed a visualisation of a graph in a Wiki page, try the following:
    1.29  
    1.30  {{{#!graphviz
    1.31  digraph G {
    1.32 @@ -14,23 +34,23 @@
    1.33  
    1.34  This parser will check the first lines of the Graphviz data for C++ style
    1.35  comments instructing it to use a different filter (dot, neato, twopi, circo,
    1.36 -or fdp - see http://graphviz.org/ for more info), use a different format for
    1.37 -the output (see the FORMATS list in the Parser class), or to generate and pass
    1.38 -a client-side image map.
    1.39 +or fdp - see http://graphviz.org/ for more information), use a different
    1.40 +format for the output (see the OUTPUT_FORMATS list in the Parser class), or to
    1.41 +generate and pass a client-side imagemap.
    1.42  
    1.43  Options:
    1.44  
    1.45  filter  - the filter to use (see Parser.FILTERS)
    1.46 -format  - the output format (see Parser.FORMATS)
    1.47 -cmapx   - the map name to use for the client-side image map; this must match
    1.48 +format  - the output format (see Parser.OUTPUT_FORMATS)
    1.49 +cmapx   - the map name to use for the client-side imagemap; this must match
    1.50            the graph name in the graph definition and shouldn't conflict with
    1.51            any other graphs that are used on the same page; for SVG images, the
    1.52            cmapx option is superfluous since SVG supports linking natively and
    1.53            Graphviz converts "href" attributes appropriately
    1.54  
    1.55 -Embed a visualization of a graph in a wiki page, using the dot filter and
    1.56 -providing a client-side image map (the filter=dot and format=png options are
    1.57 -redundant since those are the defaults for this parser):
    1.58 +To embed a visualisation of a graph in a Wiki page, using the dot filter and
    1.59 +providing a client-side imagemap (the filter=dot and format=png options are
    1.60 +redundant since those are the defaults for this parser), try the following:
    1.61  
    1.62  {{{#!graphviz
    1.63  //filter=dot
    1.64 @@ -50,16 +70,73 @@
    1.65  };
    1.66  }}}
    1.67  
    1.68 -Known Bugs
    1.69 -----------
    1.70 +As noted above, for SVG output an imagemap definition need not be used, since
    1.71 +SVG as a format supports hyperlinks.
    1.72 +
    1.73 +Known Issues
    1.74 +------------
    1.75 +
    1.76 +  * The parser could benefit from being checked for potential methods of
    1.77 +    injecting arbitrary HTML into the page output, even though the formatter
    1.78 +    interface is being used to generate the output.
    1.79 +
    1.80 +  * The parser only be useful or compatible with HTML rendering, although the
    1.81 +    generic formatter interface is used to format the output and could support
    1.82 +    any page format that can present images and objects in a similar fashion.
    1.83 +
    1.84 +  * The code may not use all of the MoinMoin interfaces properly. Particularly
    1.85 +    the manipulation of attachments might be better done through any
    1.86 +    Moin-level abstractions instead of at the file level.
    1.87 +
    1.88 +  * Comments must start at the beginning of the graphviz block and at the
    1.89 +    beginning of their respective lines. They must also not contain any extra
    1.90 +    whitespace surrounding the = sign.
    1.91 +
    1.92 +  * SVG image sizing can only be done by extracting the dimensions from an
    1.93 +    uncompressed ("svg" format, not "svgz" format) output representation.
    1.94 +    Moreover, the extraction process assumes that the "width" and "height"
    1.95 +    attributes appear in an "svg" start tag of a particular form.
    1.96 +
    1.97 +  * Beyond SVG size detection there is no support for determining the size of
    1.98 +    output representations that cannot be inserted into a page as an image.
    1.99 +    Thus, most of the supported output formats may cause inappropriately sized
   1.100 +    objects to appear in a page because the browser has not been able to
   1.101 +    determine the size of the object in advance.
   1.102  
   1.103 -  - Hasn't been thoroughly checked for potential methods of injecting
   1.104 -    arbitrary HTML into the output.
   1.105 -  - Only compatible with HTML rendering
   1.106 -  - May not use all of the MoinMoin interfaces properly - this is a
   1.107 -    quick hack based on looking at an example and digging through the
   1.108 -    MoinMoin source.  The MoinMoin development docs haven't been
   1.109 -    consulted (yet).
   1.110 -  - Comments must start at the beginning of the graphviz block, and at the
   1.111 -    beginning of their respective lines.  They must also not contain
   1.112 -    any extra whitespace surrounding the = sign.
   1.113 +Recommended Software
   1.114 +--------------------
   1.115 +
   1.116 +See the "Dependencies" section below for essential software.
   1.117 +
   1.118 +The moinsetup tool is recommended for installation since it aims to support
   1.119 +all versions of MoinMoin that are supported for use with this software.
   1.120 +
   1.121 +See the following page for information on moinsetup:
   1.122 +
   1.123 +http://moinmo.in/ScriptMarket/moinsetup
   1.124 +
   1.125 +Dependencies
   1.126 +------------
   1.127 +
   1.128 +The graphviz parser has the following basic dependencies:
   1.129 +
   1.130 +Packages                    Release Information
   1.131 +--------                    -------------------
   1.132 +
   1.133 +graphviz                    Tested with 2.16
   1.134 +                            Source: http://www.graphviz.org/
   1.135 +
   1.136 +Contact, Copyright and Licence Information
   1.137 +------------------------------------------
   1.138 +
   1.139 +See the following Web page for more information about this work:
   1.140 +
   1.141 +http://moinmo.in/ParserMarket/graphviz
   1.142 +
   1.143 +The author of this packaging of the original work can be contacted at the
   1.144 +following e-mail address:
   1.145 +
   1.146 +paul@boddie.org.uk
   1.147 +
   1.148 +Copyright and licence information can be found in the docs directory - see
   1.149 +docs/COPYING.txt and docs/LICENCE.txt for more information.
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/docs/COPYING.txt	Sat Jan 14 20:40:47 2012 +0100
     2.3 @@ -0,0 +1,20 @@
     2.4 +Licence Agreement
     2.5 +-----------------
     2.6 +
     2.7 +Copyright (C) 2008 Wayne Tucker
     2.8 +Copyright (C) 2011, 2012 Paul Boddie <paul@boddie.org.uk>
     2.9 +
    2.10 +This software is free software; you can redistribute it and/or
    2.11 +modify it under the terms of the GNU General Public License as
    2.12 +published by the Free Software Foundation; either version 2 of
    2.13 +the License, or (at your option) any later version.
    2.14 +
    2.15 +This software is distributed in the hope that it will be useful,
    2.16 +but WITHOUT ANY WARRANTY; without even the implied warranty of
    2.17 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    2.18 +GNU General Public License for more details.
    2.19 +
    2.20 +You should have received a copy of the GNU General Public
    2.21 +License along with this library; see the file LICENCE.txt
    2.22 +If not, write to the Free Software Foundation, Inc.,
    2.23 +51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/docs/LICENCE.txt	Sat Jan 14 20:40:47 2012 +0100
     3.3 @@ -0,0 +1,339 @@
     3.4 +		    GNU GENERAL PUBLIC LICENSE
     3.5 +		       Version 2, June 1991
     3.6 +
     3.7 + Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
     3.8 + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
     3.9 + Everyone is permitted to copy and distribute verbatim copies
    3.10 + of this license document, but changing it is not allowed.
    3.11 +
    3.12 +			    Preamble
    3.13 +
    3.14 +  The licenses for most software are designed to take away your
    3.15 +freedom to share and change it.  By contrast, the GNU General Public
    3.16 +License is intended to guarantee your freedom to share and change free
    3.17 +software--to make sure the software is free for all its users.  This
    3.18 +General Public License applies to most of the Free Software
    3.19 +Foundation's software and to any other program whose authors commit to
    3.20 +using it.  (Some other Free Software Foundation software is covered by
    3.21 +the GNU Lesser General Public License instead.)  You can apply it to
    3.22 +your programs, too.
    3.23 +
    3.24 +  When we speak of free software, we are referring to freedom, not
    3.25 +price.  Our General Public Licenses are designed to make sure that you
    3.26 +have the freedom to distribute copies of free software (and charge for
    3.27 +this service if you wish), that you receive source code or can get it
    3.28 +if you want it, that you can change the software or use pieces of it
    3.29 +in new free programs; and that you know you can do these things.
    3.30 +
    3.31 +  To protect your rights, we need to make restrictions that forbid
    3.32 +anyone to deny you these rights or to ask you to surrender the rights.
    3.33 +These restrictions translate to certain responsibilities for you if you
    3.34 +distribute copies of the software, or if you modify it.
    3.35 +
    3.36 +  For example, if you distribute copies of such a program, whether
    3.37 +gratis or for a fee, you must give the recipients all the rights that
    3.38 +you have.  You must make sure that they, too, receive or can get the
    3.39 +source code.  And you must show them these terms so they know their
    3.40 +rights.
    3.41 +
    3.42 +  We protect your rights with two steps: (1) copyright the software, and
    3.43 +(2) offer you this license which gives you legal permission to copy,
    3.44 +distribute and/or modify the software.
    3.45 +
    3.46 +  Also, for each author's protection and ours, we want to make certain
    3.47 +that everyone understands that there is no warranty for this free
    3.48 +software.  If the software is modified by someone else and passed on, we
    3.49 +want its recipients to know that what they have is not the original, so
    3.50 +that any problems introduced by others will not reflect on the original
    3.51 +authors' reputations.
    3.52 +
    3.53 +  Finally, any free program is threatened constantly by software
    3.54 +patents.  We wish to avoid the danger that redistributors of a free
    3.55 +program will individually obtain patent licenses, in effect making the
    3.56 +program proprietary.  To prevent this, we have made it clear that any
    3.57 +patent must be licensed for everyone's free use or not licensed at all.
    3.58 +
    3.59 +  The precise terms and conditions for copying, distribution and
    3.60 +modification follow.
    3.61 +
    3.62 +		    GNU GENERAL PUBLIC LICENSE
    3.63 +   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
    3.64 +
    3.65 +  0. This License applies to any program or other work which contains
    3.66 +a notice placed by the copyright holder saying it may be distributed
    3.67 +under the terms of this General Public License.  The "Program", below,
    3.68 +refers to any such program or work, and a "work based on the Program"
    3.69 +means either the Program or any derivative work under copyright law:
    3.70 +that is to say, a work containing the Program or a portion of it,
    3.71 +either verbatim or with modifications and/or translated into another
    3.72 +language.  (Hereinafter, translation is included without limitation in
    3.73 +the term "modification".)  Each licensee is addressed as "you".
    3.74 +
    3.75 +Activities other than copying, distribution and modification are not
    3.76 +covered by this License; they are outside its scope.  The act of
    3.77 +running the Program is not restricted, and the output from the Program
    3.78 +is covered only if its contents constitute a work based on the
    3.79 +Program (independent of having been made by running the Program).
    3.80 +Whether that is true depends on what the Program does.
    3.81 +
    3.82 +  1. You may copy and distribute verbatim copies of the Program's
    3.83 +source code as you receive it, in any medium, provided that you
    3.84 +conspicuously and appropriately publish on each copy an appropriate
    3.85 +copyright notice and disclaimer of warranty; keep intact all the
    3.86 +notices that refer to this License and to the absence of any warranty;
    3.87 +and give any other recipients of the Program a copy of this License
    3.88 +along with the Program.
    3.89 +
    3.90 +You may charge a fee for the physical act of transferring a copy, and
    3.91 +you may at your option offer warranty protection in exchange for a fee.
    3.92 +
    3.93 +  2. You may modify your copy or copies of the Program or any portion
    3.94 +of it, thus forming a work based on the Program, and copy and
    3.95 +distribute such modifications or work under the terms of Section 1
    3.96 +above, provided that you also meet all of these conditions:
    3.97 +
    3.98 +    a) You must cause the modified files to carry prominent notices
    3.99 +    stating that you changed the files and the date of any change.
   3.100 +
   3.101 +    b) You must cause any work that you distribute or publish, that in
   3.102 +    whole or in part contains or is derived from the Program or any
   3.103 +    part thereof, to be licensed as a whole at no charge to all third
   3.104 +    parties under the terms of this License.
   3.105 +
   3.106 +    c) If the modified program normally reads commands interactively
   3.107 +    when run, you must cause it, when started running for such
   3.108 +    interactive use in the most ordinary way, to print or display an
   3.109 +    announcement including an appropriate copyright notice and a
   3.110 +    notice that there is no warranty (or else, saying that you provide
   3.111 +    a warranty) and that users may redistribute the program under
   3.112 +    these conditions, and telling the user how to view a copy of this
   3.113 +    License.  (Exception: if the Program itself is interactive but
   3.114 +    does not normally print such an announcement, your work based on
   3.115 +    the Program is not required to print an announcement.)
   3.116 +
   3.117 +These requirements apply to the modified work as a whole.  If
   3.118 +identifiable sections of that work are not derived from the Program,
   3.119 +and can be reasonably considered independent and separate works in
   3.120 +themselves, then this License, and its terms, do not apply to those
   3.121 +sections when you distribute them as separate works.  But when you
   3.122 +distribute the same sections as part of a whole which is a work based
   3.123 +on the Program, the distribution of the whole must be on the terms of
   3.124 +this License, whose permissions for other licensees extend to the
   3.125 +entire whole, and thus to each and every part regardless of who wrote it.
   3.126 +
   3.127 +Thus, it is not the intent of this section to claim rights or contest
   3.128 +your rights to work written entirely by you; rather, the intent is to
   3.129 +exercise the right to control the distribution of derivative or
   3.130 +collective works based on the Program.
   3.131 +
   3.132 +In addition, mere aggregation of another work not based on the Program
   3.133 +with the Program (or with a work based on the Program) on a volume of
   3.134 +a storage or distribution medium does not bring the other work under
   3.135 +the scope of this License.
   3.136 +
   3.137 +  3. You may copy and distribute the Program (or a work based on it,
   3.138 +under Section 2) in object code or executable form under the terms of
   3.139 +Sections 1 and 2 above provided that you also do one of the following:
   3.140 +
   3.141 +    a) Accompany it with the complete corresponding machine-readable
   3.142 +    source code, which must be distributed under the terms of Sections
   3.143 +    1 and 2 above on a medium customarily used for software interchange; or,
   3.144 +
   3.145 +    b) Accompany it with a written offer, valid for at least three
   3.146 +    years, to give any third party, for a charge no more than your
   3.147 +    cost of physically performing source distribution, a complete
   3.148 +    machine-readable copy of the corresponding source code, to be
   3.149 +    distributed under the terms of Sections 1 and 2 above on a medium
   3.150 +    customarily used for software interchange; or,
   3.151 +
   3.152 +    c) Accompany it with the information you received as to the offer
   3.153 +    to distribute corresponding source code.  (This alternative is
   3.154 +    allowed only for noncommercial distribution and only if you
   3.155 +    received the program in object code or executable form with such
   3.156 +    an offer, in accord with Subsection b above.)
   3.157 +
   3.158 +The source code for a work means the preferred form of the work for
   3.159 +making modifications to it.  For an executable work, complete source
   3.160 +code means all the source code for all modules it contains, plus any
   3.161 +associated interface definition files, plus the scripts used to
   3.162 +control compilation and installation of the executable.  However, as a
   3.163 +special exception, the source code distributed need not include
   3.164 +anything that is normally distributed (in either source or binary
   3.165 +form) with the major components (compiler, kernel, and so on) of the
   3.166 +operating system on which the executable runs, unless that component
   3.167 +itself accompanies the executable.
   3.168 +
   3.169 +If distribution of executable or object code is made by offering
   3.170 +access to copy from a designated place, then offering equivalent
   3.171 +access to copy the source code from the same place counts as
   3.172 +distribution of the source code, even though third parties are not
   3.173 +compelled to copy the source along with the object code.
   3.174 +
   3.175 +  4. You may not copy, modify, sublicense, or distribute the Program
   3.176 +except as expressly provided under this License.  Any attempt
   3.177 +otherwise to copy, modify, sublicense or distribute the Program is
   3.178 +void, and will automatically terminate your rights under this License.
   3.179 +However, parties who have received copies, or rights, from you under
   3.180 +this License will not have their licenses terminated so long as such
   3.181 +parties remain in full compliance.
   3.182 +
   3.183 +  5. You are not required to accept this License, since you have not
   3.184 +signed it.  However, nothing else grants you permission to modify or
   3.185 +distribute the Program or its derivative works.  These actions are
   3.186 +prohibited by law if you do not accept this License.  Therefore, by
   3.187 +modifying or distributing the Program (or any work based on the
   3.188 +Program), you indicate your acceptance of this License to do so, and
   3.189 +all its terms and conditions for copying, distributing or modifying
   3.190 +the Program or works based on it.
   3.191 +
   3.192 +  6. Each time you redistribute the Program (or any work based on the
   3.193 +Program), the recipient automatically receives a license from the
   3.194 +original licensor to copy, distribute or modify the Program subject to
   3.195 +these terms and conditions.  You may not impose any further
   3.196 +restrictions on the recipients' exercise of the rights granted herein.
   3.197 +You are not responsible for enforcing compliance by third parties to
   3.198 +this License.
   3.199 +
   3.200 +  7. If, as a consequence of a court judgment or allegation of patent
   3.201 +infringement or for any other reason (not limited to patent issues),
   3.202 +conditions are imposed on you (whether by court order, agreement or
   3.203 +otherwise) that contradict the conditions of this License, they do not
   3.204 +excuse you from the conditions of this License.  If you cannot
   3.205 +distribute so as to satisfy simultaneously your obligations under this
   3.206 +License and any other pertinent obligations, then as a consequence you
   3.207 +may not distribute the Program at all.  For example, if a patent
   3.208 +license would not permit royalty-free redistribution of the Program by
   3.209 +all those who receive copies directly or indirectly through you, then
   3.210 +the only way you could satisfy both it and this License would be to
   3.211 +refrain entirely from distribution of the Program.
   3.212 +
   3.213 +If any portion of this section is held invalid or unenforceable under
   3.214 +any particular circumstance, the balance of the section is intended to
   3.215 +apply and the section as a whole is intended to apply in other
   3.216 +circumstances.
   3.217 +
   3.218 +It is not the purpose of this section to induce you to infringe any
   3.219 +patents or other property right claims or to contest validity of any
   3.220 +such claims; this section has the sole purpose of protecting the
   3.221 +integrity of the free software distribution system, which is
   3.222 +implemented by public license practices.  Many people have made
   3.223 +generous contributions to the wide range of software distributed
   3.224 +through that system in reliance on consistent application of that
   3.225 +system; it is up to the author/donor to decide if he or she is willing
   3.226 +to distribute software through any other system and a licensee cannot
   3.227 +impose that choice.
   3.228 +
   3.229 +This section is intended to make thoroughly clear what is believed to
   3.230 +be a consequence of the rest of this License.
   3.231 +
   3.232 +  8. If the distribution and/or use of the Program is restricted in
   3.233 +certain countries either by patents or by copyrighted interfaces, the
   3.234 +original copyright holder who places the Program under this License
   3.235 +may add an explicit geographical distribution limitation excluding
   3.236 +those countries, so that distribution is permitted only in or among
   3.237 +countries not thus excluded.  In such case, this License incorporates
   3.238 +the limitation as if written in the body of this License.
   3.239 +
   3.240 +  9. The Free Software Foundation may publish revised and/or new versions
   3.241 +of the General Public License from time to time.  Such new versions will
   3.242 +be similar in spirit to the present version, but may differ in detail to
   3.243 +address new problems or concerns.
   3.244 +
   3.245 +Each version is given a distinguishing version number.  If the Program
   3.246 +specifies a version number of this License which applies to it and "any
   3.247 +later version", you have the option of following the terms and conditions
   3.248 +either of that version or of any later version published by the Free
   3.249 +Software Foundation.  If the Program does not specify a version number of
   3.250 +this License, you may choose any version ever published by the Free Software
   3.251 +Foundation.
   3.252 +
   3.253 +  10. If you wish to incorporate parts of the Program into other free
   3.254 +programs whose distribution conditions are different, write to the author
   3.255 +to ask for permission.  For software which is copyrighted by the Free
   3.256 +Software Foundation, write to the Free Software Foundation; we sometimes
   3.257 +make exceptions for this.  Our decision will be guided by the two goals
   3.258 +of preserving the free status of all derivatives of our free software and
   3.259 +of promoting the sharing and reuse of software generally.
   3.260 +
   3.261 +			    NO WARRANTY
   3.262 +
   3.263 +  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
   3.264 +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
   3.265 +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
   3.266 +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
   3.267 +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
   3.268 +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
   3.269 +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
   3.270 +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
   3.271 +REPAIR OR CORRECTION.
   3.272 +
   3.273 +  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
   3.274 +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
   3.275 +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
   3.276 +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
   3.277 +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
   3.278 +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
   3.279 +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
   3.280 +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
   3.281 +POSSIBILITY OF SUCH DAMAGES.
   3.282 +
   3.283 +		     END OF TERMS AND CONDITIONS
   3.284 +
   3.285 +	    How to Apply These Terms to Your New Programs
   3.286 +
   3.287 +  If you develop a new program, and you want it to be of the greatest
   3.288 +possible use to the public, the best way to achieve this is to make it
   3.289 +free software which everyone can redistribute and change under these terms.
   3.290 +
   3.291 +  To do so, attach the following notices to the program.  It is safest
   3.292 +to attach them to the start of each source file to most effectively
   3.293 +convey the exclusion of warranty; and each file should have at least
   3.294 +the "copyright" line and a pointer to where the full notice is found.
   3.295 +
   3.296 +    <one line to give the program's name and a brief idea of what it does.>
   3.297 +    Copyright (C) <year>  <name of author>
   3.298 +
   3.299 +    This program is free software; you can redistribute it and/or modify
   3.300 +    it under the terms of the GNU General Public License as published by
   3.301 +    the Free Software Foundation; either version 2 of the License, or
   3.302 +    (at your option) any later version.
   3.303 +
   3.304 +    This program is distributed in the hope that it will be useful,
   3.305 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
   3.306 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   3.307 +    GNU General Public License for more details.
   3.308 +
   3.309 +    You should have received a copy of the GNU General Public License along
   3.310 +    with this program; if not, write to the Free Software Foundation, Inc.,
   3.311 +    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   3.312 +
   3.313 +Also add information on how to contact you by electronic and paper mail.
   3.314 +
   3.315 +If the program is interactive, make it output a short notice like this
   3.316 +when it starts in an interactive mode:
   3.317 +
   3.318 +    Gnomovision version 69, Copyright (C) year name of author
   3.319 +    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
   3.320 +    This is free software, and you are welcome to redistribute it
   3.321 +    under certain conditions; type `show c' for details.
   3.322 +
   3.323 +The hypothetical commands `show w' and `show c' should show the appropriate
   3.324 +parts of the General Public License.  Of course, the commands you use may
   3.325 +be called something other than `show w' and `show c'; they could even be
   3.326 +mouse-clicks or menu items--whatever suits your program.
   3.327 +
   3.328 +You should also get your employer (if you work as a programmer) or your
   3.329 +school, if any, to sign a "copyright disclaimer" for the program, if
   3.330 +necessary.  Here is a sample; alter the names:
   3.331 +
   3.332 +  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
   3.333 +  `Gnomovision' (which makes passes at compilers) written by James Hacker.
   3.334 +
   3.335 +  <signature of Ty Coon>, 1 April 1989
   3.336 +  Ty Coon, President of Vice
   3.337 +
   3.338 +This General Public License does not permit incorporating your program into
   3.339 +proprietary programs.  If your program is a subroutine library, you may
   3.340 +consider it more useful to permit linking proprietary applications with the
   3.341 +library.  If this is what you want to do, use the GNU Lesser General
   3.342 +Public License instead of this License.
     4.1 --- a/graphviz.py	Sat Jan 14 19:17:36 2012 +0100
     4.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.3 @@ -1,318 +0,0 @@
     4.4 -# -*- coding: iso-8859-1 -*-
     4.5 -"""
     4.6 -    MoinMoin - Graphviz Parser
     4.7 -    Based loosely on GNUPLOT parser by MoinMoin:KwonChanYoung
     4.8 -
     4.9 -    @copyright: 2008 Wayne Tucker
    4.10 -    @copyright: 2011, 2012 Paul Boddie <paul@boddie.org.uk>
    4.11 -    @license: GNU GPL, see COPYING for details.
    4.12 -"""
    4.13 -
    4.14 -# Change this to the directory that the Graphviz binaries (dot, neato, etc.)
    4.15 -# are installed in.
    4.16 -
    4.17 -BINARY_PATH = '/usr/bin'
    4.18 -
    4.19 -from os.path import join
    4.20 -import os
    4.21 -import subprocess
    4.22 -import sha
    4.23 -import re
    4.24 -
    4.25 -from MoinMoin import config
    4.26 -from MoinMoin.action import AttachFile
    4.27 -from MoinMoin import log
    4.28 -from MoinMoin import wikiutil
    4.29 -
    4.30 -logging = log.getLogger(__name__)
    4.31 -
    4.32 -class GraphVizError(RuntimeError):
    4.33 -    pass
    4.34 -
    4.35 -Dependencies = ["pages"]
    4.36 -
    4.37 -class Parser:
    4.38 -
    4.39 -    "Uses the Graphviz programs to create a visualization of a graph."
    4.40 -
    4.41 -    extensions = []
    4.42 -    Dependencies = Dependencies
    4.43 -
    4.44 -    FILTERS = ['dot', 'neato', 'twopi', 'circo', 'fdp']
    4.45 -    IMAGE_FORMATS = ['png', 'gif']
    4.46 -    SVG_FORMATS = ['svg', 'svgz']
    4.47 -    OUTPUT_FORMATS = IMAGE_FORMATS + SVG_FORMATS + \
    4.48 -        ['ps', 'fig', 'mif', 'hpgl', 'pcl', 'dia', 'imap']
    4.49 -
    4.50 -    attach_regexp = re.compile(
    4.51 -        r"graphviz_"
    4.52 -        r"(?P<digest>.*?)"
    4.53 -        r"(?:"                              # begin optional section
    4.54 -        r"_(?P<width>.*?)_(?P<height>.*?)"  # dimensions
    4.55 -        r")?"                               # end optional section
    4.56 -        r"\.(?P<format>.*)"
    4.57 -        r"$")
    4.58 -
    4.59 -    attr_regexp = re.compile(
    4.60 -        r"(?P<attr>width|height)"
    4.61 -        r"\s*=\s*"
    4.62 -        r"""(?P<quote>['"])"""              # start quote
    4.63 -        r"(?P<value>.*?)"
    4.64 -        r"""(?P=quote)""",                  # matching quote
    4.65 -        re.UNICODE)
    4.66 -
    4.67 -    def __init__(self, raw, request, **kw):
    4.68 -        self.raw = raw
    4.69 -        self.request = request
    4.70 -
    4.71 -    def format(self, formatter):
    4.72 -
    4.73 -        "Using the 'formatter', return the formatted page output."
    4.74 -
    4.75 -        request = self.request
    4.76 -        page = request.page
    4.77 -        _ = request.getText
    4.78 -
    4.79 -        request.flush() # to identify error text
    4.80 -
    4.81 -        filter = self.FILTERS[0]
    4.82 -        format = 'png'
    4.83 -        cmapx = None
    4.84 -        width = None
    4.85 -        height = None
    4.86 -
    4.87 -        raw_lines = self.raw.splitlines()
    4.88 -        for l in raw_lines:
    4.89 -            if not l[0:2] == '//':
    4.90 -                break
    4.91 -
    4.92 -            parts = l[2:].split("=")
    4.93 -            directive = parts[0]
    4.94 -            value = "=".join(parts[1:])
    4.95 -
    4.96 -            if directive == 'filter':
    4.97 -                filter = value.lower()
    4.98 -                if filter not in self.FILTERS:
    4.99 -                    logging.warn('unknown filter %s' % filter)
   4.100 -
   4.101 -            elif directive == 'format':
   4.102 -                value = value.lower()
   4.103 -                if value in self.OUTPUT_FORMATS:
   4.104 -                    format = value
   4.105 -
   4.106 -            elif directive == 'cmapx':
   4.107 -                cmapx = wikiutil.escape(value)
   4.108 -
   4.109 -        if not format in self.OUTPUT_FORMATS:
   4.110 -            raise NotImplementedError, "only formats %s are currently supported" % \
   4.111 -                self.OUTPUT_FORMATS
   4.112 -
   4.113 -        if cmapx and not format in self.IMAGE_FORMATS:
   4.114 -            logging.warn('format %s is incompatible with cmapx option' % format)
   4.115 -            cmapx = None
   4.116 -
   4.117 -        digest = sha.new(self.raw).hexdigest()
   4.118 -
   4.119 -        # Make sure that an attachments directory exists and that old graphs are
   4.120 -        # deleted.
   4.121 -
   4.122 -        self.attach_dir = AttachFile.getAttachDir(request, page.page_name, create=1)
   4.123 -        self.delete_old_graphs(formatter)
   4.124 -
   4.125 -        # Find the details of the graph, rendering a new graph if necessary.
   4.126 -
   4.127 -        attrs = self.find_graph(digest, format)
   4.128 -        if not attrs:
   4.129 -            attrs = self.graphviz(filter, self.raw, digest, format)
   4.130 -
   4.131 -        chart = self.get_chartname(digest, format, attrs)
   4.132 -        url = AttachFile.getAttachUrl(page.page_name, chart, request)
   4.133 -
   4.134 -        # Images are displayed using the HTML "img" element (or equivalent)
   4.135 -        # and may provide an imagemap.
   4.136 -
   4.137 -        if format in self.IMAGE_FORMATS:
   4.138 -            if cmapx:
   4.139 -                request.write('\n' + self.graphviz(filter, self.raw, digest, "cmapx") + '\n')
   4.140 -                request.write(formatter.image(src="%s" % url, usemap="#%s" % cmapx, **self.get_format_attrs(attrs)))
   4.141 -            else:
   4.142 -                request.write(formatter.image(src="%s" % url, alt="graphviz image", **self.get_format_attrs(attrs)))
   4.143 -
   4.144 -        # Other objects are embedded using the HTML "object" element (or
   4.145 -        # equivalent).
   4.146 -
   4.147 -        else:
   4.148 -            request.write(formatter.transclusion(1, data=url, **self.get_format_attrs(attrs)))
   4.149 -            request.write(formatter.text(_("graphviz image")))
   4.150 -            request.write(formatter.transclusion(0))
   4.151 -
   4.152 -    def find_graph(self, digest, format):
   4.153 -
   4.154 -        "Find an existing graph using 'digest' and 'format'."
   4.155 -
   4.156 -        attach_files = AttachFile._get_files(self.request, self.request.page.page_name)
   4.157 -
   4.158 -        for chart in attach_files:
   4.159 -            match = self.attach_regexp.match(chart)
   4.160 -
   4.161 -            if match and \
   4.162 -                match.group("digest") == digest and \
   4.163 -                match.group("format") == format:
   4.164 -
   4.165 -                return match.groupdict()
   4.166 -
   4.167 -        return None
   4.168 -
   4.169 -    def get_chartname(self, digest, format, attrs=None):
   4.170 -
   4.171 -        "Return the chart name for the 'digest', 'format' and 'attrs'."
   4.172 -
   4.173 -        wh = self.get_dimensions(attrs)
   4.174 -        if wh:
   4.175 -            dimensions = "_%s_%s" % wh
   4.176 -        else:
   4.177 -            dimensions = ""
   4.178 -        return "graphviz_%s%s.%s" % (digest, dimensions, format)
   4.179 -
   4.180 -    def delete_old_graphs(self, formatter):
   4.181 -
   4.182 -        "Using the 'formatter' for page information, delete old graphs."
   4.183 -
   4.184 -        page_info = formatter.page.lastEditInfo()
   4.185 -        try:
   4.186 -            page_date = page_info['time']
   4.187 -        except KeyError, ex:
   4.188 -            return
   4.189 -
   4.190 -        attach_files = AttachFile._get_files(self.request, self.request.page.page_name)
   4.191 -
   4.192 -        for chart in attach_files:
   4.193 -            match = self.attach_regexp.match(chart)
   4.194 -
   4.195 -            if match and match.group("format") in self.OUTPUT_FORMATS:
   4.196 -                fullpath = join(self.attach_dir, chart).encode(config.charset)
   4.197 -                st = os.stat(fullpath)
   4.198 -                chart_date = self.request.user.getFormattedDateTime(st.st_mtime)
   4.199 -                if chart_date < page_date:
   4.200 -                    os.remove(fullpath)
   4.201 -
   4.202 -    def graphviz(self, filter, graph_def, digest, format):
   4.203 -
   4.204 -        """
   4.205 -        Using the 'filter' with the given 'graph_def' (and 'digest'), generate
   4.206 -        output in the given 'format'.
   4.207 -        """
   4.208 -
   4.209 -        need_output = format in ("cmapx", "svg")
   4.210 -
   4.211 -        # Either write the output straight to a file.
   4.212 -
   4.213 -        if not need_output:
   4.214 -            chart = self.get_chartname(digest, format)
   4.215 -            filename = join(self.attach_dir, chart).encode(config.charset)
   4.216 -
   4.217 -            p = subprocess.Popen([
   4.218 -                join(BINARY_PATH, filter), '-T%s' % format, '-o%s' % filename
   4.219 -                ],
   4.220 -                shell=False,
   4.221 -                stdin=subprocess.PIPE,
   4.222 -                stdout=subprocess.PIPE,
   4.223 -                stderr=subprocess.PIPE)
   4.224 -
   4.225 -        # Or intercept the output.
   4.226 -
   4.227 -        else:
   4.228 -            p = subprocess.Popen([
   4.229 -                join(BINARY_PATH, filter), '-T%s' % format
   4.230 -                ],
   4.231 -                shell=False,
   4.232 -                stdin=subprocess.PIPE,
   4.233 -                stdout=subprocess.PIPE,
   4.234 -                stderr=subprocess.PIPE)
   4.235 -
   4.236 -        p.stdin.write(graph_def)
   4.237 -        p.stdin.flush()
   4.238 -        p.stdin.close()
   4.239 -
   4.240 -        p.wait()
   4.241 -
   4.242 -        # Graph data always goes via standard output so that we can extract the
   4.243 -        # width and height if possible.
   4.244 -
   4.245 -        if need_output:
   4.246 -            output, attrs = self.process_output(p.stdout, format)
   4.247 -        else:
   4.248 -            output, attrs = None, {}
   4.249 -
   4.250 -        # Test for errors.
   4.251 -
   4.252 -        errors = p.stderr.read()
   4.253 -
   4.254 -        if len(errors) > 0:
   4.255 -            raise GraphVizError, errors
   4.256 -
   4.257 -        # Return the output for imagemaps.
   4.258 -
   4.259 -        if format == "cmapx":
   4.260 -            return output
   4.261 -
   4.262 -        # Copy to a file, if necessary.
   4.263 -
   4.264 -        elif need_output:
   4.265 -            chart = self.get_chartname(digest, format, attrs)
   4.266 -            filename = join(self.attach_dir, chart).encode(config.charset)
   4.267 -
   4.268 -            f = open(filename, "wb")
   4.269 -            try:
   4.270 -                f.write(output)
   4.271 -            finally:
   4.272 -                f.close()
   4.273 -
   4.274 -        # Return the dimensions, if defined.
   4.275 -
   4.276 -        return attrs
   4.277 -
   4.278 -    def process_output(self, output, format):
   4.279 -
   4.280 -        "Process graph 'output' in the given 'format'."
   4.281 -
   4.282 -        # Return the raw output if SVG is not being produced.
   4.283 -
   4.284 -        if format != "svg":
   4.285 -            return output.read(), {}
   4.286 -
   4.287 -        # Otherwise, return the processed SVG output.
   4.288 -
   4.289 -        processed = []
   4.290 -        found = False
   4.291 -        attrs = {}
   4.292 -
   4.293 -        for line in output.xreadlines():
   4.294 -            if not found and line.startswith("<svg "):
   4.295 -                for match in self.attr_regexp.finditer(line):
   4.296 -                    attrs[match.group("attr")] = match.group("value")
   4.297 -                found = True
   4.298 -            processed.append(line)
   4.299 -
   4.300 -        return "".join(processed), attrs
   4.301 -
   4.302 -    def get_dimensions(self, attrs):
   4.303 -
   4.304 -        "Return a (width, height) tuple using the 'attrs' dictionary."
   4.305 -
   4.306 -        if attrs and attrs.has_key("width") and attrs.has_key("height"):
   4.307 -            return attrs["width"], attrs["height"]
   4.308 -        else:
   4.309 -            return None
   4.310 -
   4.311 -    def get_format_attrs(self, attrs):
   4.312 -
   4.313 -        "Return a dictionary based on 'attrs' with only formatting attributes."
   4.314 -
   4.315 -        dattrs = {}
   4.316 -        for key in ("width", "height"):
   4.317 -            if attrs.has_key(key):
   4.318 -                dattrs[key] = attrs[key]
   4.319 -        return dattrs
   4.320 -
   4.321 -# vim: tabstop=4 expandtab shiftwidth=4
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/parsers/graphviz.py	Sat Jan 14 20:40:47 2012 +0100
     5.3 @@ -0,0 +1,318 @@
     5.4 +# -*- coding: iso-8859-1 -*-
     5.5 +"""
     5.6 +    MoinMoin - Graphviz Parser
     5.7 +    Based loosely on GNUPLOT parser by MoinMoin:KwonChanYoung
     5.8 +
     5.9 +    @copyright: 2008 Wayne Tucker
    5.10 +    @copyright: 2011, 2012 Paul Boddie <paul@boddie.org.uk>
    5.11 +    @license: GNU GPL, see COPYING for details.
    5.12 +"""
    5.13 +
    5.14 +# Change this to the directory that the Graphviz binaries (dot, neato, etc.)
    5.15 +# are installed in.
    5.16 +
    5.17 +BINARY_PATH = '/usr/bin'
    5.18 +
    5.19 +from os.path import join
    5.20 +import os
    5.21 +import subprocess
    5.22 +import sha
    5.23 +import re
    5.24 +
    5.25 +from MoinMoin import config
    5.26 +from MoinMoin.action import AttachFile
    5.27 +from MoinMoin import log
    5.28 +from MoinMoin import wikiutil
    5.29 +
    5.30 +logging = log.getLogger(__name__)
    5.31 +
    5.32 +class GraphVizError(RuntimeError):
    5.33 +    pass
    5.34 +
    5.35 +Dependencies = ["pages"]
    5.36 +
    5.37 +class Parser:
    5.38 +
    5.39 +    "Uses the Graphviz programs to create a visualization of a graph."
    5.40 +
    5.41 +    extensions = []
    5.42 +    Dependencies = Dependencies
    5.43 +
    5.44 +    FILTERS = ['dot', 'neato', 'twopi', 'circo', 'fdp']
    5.45 +    IMAGE_FORMATS = ['png', 'gif']
    5.46 +    SVG_FORMATS = ['svg', 'svgz']
    5.47 +    OUTPUT_FORMATS = IMAGE_FORMATS + SVG_FORMATS + \
    5.48 +        ['ps', 'fig', 'mif', 'hpgl', 'pcl', 'dia', 'imap']
    5.49 +
    5.50 +    attach_regexp = re.compile(
    5.51 +        r"graphviz_"
    5.52 +        r"(?P<digest>.*?)"
    5.53 +        r"(?:"                              # begin optional section
    5.54 +        r"_(?P<width>.*?)_(?P<height>.*?)"  # dimensions
    5.55 +        r")?"                               # end optional section
    5.56 +        r"\.(?P<format>.*)"
    5.57 +        r"$")
    5.58 +
    5.59 +    attr_regexp = re.compile(
    5.60 +        r"(?P<attr>width|height)"
    5.61 +        r"\s*=\s*"
    5.62 +        r"""(?P<quote>['"])"""              # start quote
    5.63 +        r"(?P<value>.*?)"
    5.64 +        r"""(?P=quote)""",                  # matching quote
    5.65 +        re.UNICODE)
    5.66 +
    5.67 +    def __init__(self, raw, request, **kw):
    5.68 +        self.raw = raw
    5.69 +        self.request = request
    5.70 +
    5.71 +    def format(self, formatter):
    5.72 +
    5.73 +        "Using the 'formatter', return the formatted page output."
    5.74 +
    5.75 +        request = self.request
    5.76 +        page = request.page
    5.77 +        _ = request.getText
    5.78 +
    5.79 +        request.flush() # to identify error text
    5.80 +
    5.81 +        filter = self.FILTERS[0]
    5.82 +        format = 'png'
    5.83 +        cmapx = None
    5.84 +        width = None
    5.85 +        height = None
    5.86 +
    5.87 +        raw_lines = self.raw.splitlines()
    5.88 +        for l in raw_lines:
    5.89 +            if not l[0:2] == '//':
    5.90 +                break
    5.91 +
    5.92 +            parts = l[2:].split("=")
    5.93 +            directive = parts[0]
    5.94 +            value = "=".join(parts[1:])
    5.95 +
    5.96 +            if directive == 'filter':
    5.97 +                filter = value.lower()
    5.98 +                if filter not in self.FILTERS:
    5.99 +                    logging.warn('unknown filter %s' % filter)
   5.100 +
   5.101 +            elif directive == 'format':
   5.102 +                value = value.lower()
   5.103 +                if value in self.OUTPUT_FORMATS:
   5.104 +                    format = value
   5.105 +
   5.106 +            elif directive == 'cmapx':
   5.107 +                cmapx = wikiutil.escape(value)
   5.108 +
   5.109 +        if not format in self.OUTPUT_FORMATS:
   5.110 +            raise NotImplementedError, "only formats %s are currently supported" % \
   5.111 +                self.OUTPUT_FORMATS
   5.112 +
   5.113 +        if cmapx and not format in self.IMAGE_FORMATS:
   5.114 +            logging.warn('format %s is incompatible with cmapx option' % format)
   5.115 +            cmapx = None
   5.116 +
   5.117 +        digest = sha.new(self.raw).hexdigest()
   5.118 +
   5.119 +        # Make sure that an attachments directory exists and that old graphs are
   5.120 +        # deleted.
   5.121 +
   5.122 +        self.attach_dir = AttachFile.getAttachDir(request, page.page_name, create=1)
   5.123 +        self.delete_old_graphs(formatter)
   5.124 +
   5.125 +        # Find the details of the graph, rendering a new graph if necessary.
   5.126 +
   5.127 +        attrs = self.find_graph(digest, format)
   5.128 +        if not attrs:
   5.129 +            attrs = self.graphviz(filter, self.raw, digest, format)
   5.130 +
   5.131 +        chart = self.get_chartname(digest, format, attrs)
   5.132 +        url = AttachFile.getAttachUrl(page.page_name, chart, request)
   5.133 +
   5.134 +        # Images are displayed using the HTML "img" element (or equivalent)
   5.135 +        # and may provide an imagemap.
   5.136 +
   5.137 +        if format in self.IMAGE_FORMATS:
   5.138 +            if cmapx:
   5.139 +                request.write('\n' + self.graphviz(filter, self.raw, digest, "cmapx") + '\n')
   5.140 +                request.write(formatter.image(src="%s" % url, usemap="#%s" % cmapx, **self.get_format_attrs(attrs)))
   5.141 +            else:
   5.142 +                request.write(formatter.image(src="%s" % url, alt="graphviz image", **self.get_format_attrs(attrs)))
   5.143 +
   5.144 +        # Other objects are embedded using the HTML "object" element (or
   5.145 +        # equivalent).
   5.146 +
   5.147 +        else:
   5.148 +            request.write(formatter.transclusion(1, data=url, **self.get_format_attrs(attrs)))
   5.149 +            request.write(formatter.text(_("graphviz image")))
   5.150 +            request.write(formatter.transclusion(0))
   5.151 +
   5.152 +    def find_graph(self, digest, format):
   5.153 +
   5.154 +        "Find an existing graph using 'digest' and 'format'."
   5.155 +
   5.156 +        attach_files = AttachFile._get_files(self.request, self.request.page.page_name)
   5.157 +
   5.158 +        for chart in attach_files:
   5.159 +            match = self.attach_regexp.match(chart)
   5.160 +
   5.161 +            if match and \
   5.162 +                match.group("digest") == digest and \
   5.163 +                match.group("format") == format:
   5.164 +
   5.165 +                return match.groupdict()
   5.166 +
   5.167 +        return None
   5.168 +
   5.169 +    def get_chartname(self, digest, format, attrs=None):
   5.170 +
   5.171 +        "Return the chart name for the 'digest', 'format' and 'attrs'."
   5.172 +
   5.173 +        wh = self.get_dimensions(attrs)
   5.174 +        if wh:
   5.175 +            dimensions = "_%s_%s" % wh
   5.176 +        else:
   5.177 +            dimensions = ""
   5.178 +        return "graphviz_%s%s.%s" % (digest, dimensions, format)
   5.179 +
   5.180 +    def delete_old_graphs(self, formatter):
   5.181 +
   5.182 +        "Using the 'formatter' for page information, delete old graphs."
   5.183 +
   5.184 +        page_info = formatter.page.lastEditInfo()
   5.185 +        try:
   5.186 +            page_date = page_info['time']
   5.187 +        except KeyError, ex:
   5.188 +            return
   5.189 +
   5.190 +        attach_files = AttachFile._get_files(self.request, self.request.page.page_name)
   5.191 +
   5.192 +        for chart in attach_files:
   5.193 +            match = self.attach_regexp.match(chart)
   5.194 +
   5.195 +            if match and match.group("format") in self.OUTPUT_FORMATS:
   5.196 +                fullpath = join(self.attach_dir, chart).encode(config.charset)
   5.197 +                st = os.stat(fullpath)
   5.198 +                chart_date = self.request.user.getFormattedDateTime(st.st_mtime)
   5.199 +                if chart_date < page_date:
   5.200 +                    os.remove(fullpath)
   5.201 +
   5.202 +    def graphviz(self, filter, graph_def, digest, format):
   5.203 +
   5.204 +        """
   5.205 +        Using the 'filter' with the given 'graph_def' (and 'digest'), generate
   5.206 +        output in the given 'format'.
   5.207 +        """
   5.208 +
   5.209 +        need_output = format in ("cmapx", "svg")
   5.210 +
   5.211 +        # Either write the output straight to a file.
   5.212 +
   5.213 +        if not need_output:
   5.214 +            chart = self.get_chartname(digest, format)
   5.215 +            filename = join(self.attach_dir, chart).encode(config.charset)
   5.216 +
   5.217 +            p = subprocess.Popen([
   5.218 +                join(BINARY_PATH, filter), '-T%s' % format, '-o%s' % filename
   5.219 +                ],
   5.220 +                shell=False,
   5.221 +                stdin=subprocess.PIPE,
   5.222 +                stdout=subprocess.PIPE,
   5.223 +                stderr=subprocess.PIPE)
   5.224 +
   5.225 +        # Or intercept the output.
   5.226 +
   5.227 +        else:
   5.228 +            p = subprocess.Popen([
   5.229 +                join(BINARY_PATH, filter), '-T%s' % format
   5.230 +                ],
   5.231 +                shell=False,
   5.232 +                stdin=subprocess.PIPE,
   5.233 +                stdout=subprocess.PIPE,
   5.234 +                stderr=subprocess.PIPE)
   5.235 +
   5.236 +        p.stdin.write(graph_def)
   5.237 +        p.stdin.flush()
   5.238 +        p.stdin.close()
   5.239 +
   5.240 +        p.wait()
   5.241 +
   5.242 +        # Graph data always goes via standard output so that we can extract the
   5.243 +        # width and height if possible.
   5.244 +
   5.245 +        if need_output:
   5.246 +            output, attrs = self.process_output(p.stdout, format)
   5.247 +        else:
   5.248 +            output, attrs = None, {}
   5.249 +
   5.250 +        # Test for errors.
   5.251 +
   5.252 +        errors = p.stderr.read()
   5.253 +
   5.254 +        if len(errors) > 0:
   5.255 +            raise GraphVizError, errors
   5.256 +
   5.257 +        # Return the output for imagemaps.
   5.258 +
   5.259 +        if format == "cmapx":
   5.260 +            return output
   5.261 +
   5.262 +        # Copy to a file, if necessary.
   5.263 +
   5.264 +        elif need_output:
   5.265 +            chart = self.get_chartname(digest, format, attrs)
   5.266 +            filename = join(self.attach_dir, chart).encode(config.charset)
   5.267 +
   5.268 +            f = open(filename, "wb")
   5.269 +            try:
   5.270 +                f.write(output)
   5.271 +            finally:
   5.272 +                f.close()
   5.273 +
   5.274 +        # Return the dimensions, if defined.
   5.275 +
   5.276 +        return attrs
   5.277 +
   5.278 +    def process_output(self, output, format):
   5.279 +
   5.280 +        "Process graph 'output' in the given 'format'."
   5.281 +
   5.282 +        # Return the raw output if SVG is not being produced.
   5.283 +
   5.284 +        if format != "svg":
   5.285 +            return output.read(), {}
   5.286 +
   5.287 +        # Otherwise, return the processed SVG output.
   5.288 +
   5.289 +        processed = []
   5.290 +        found = False
   5.291 +        attrs = {}
   5.292 +
   5.293 +        for line in output.xreadlines():
   5.294 +            if not found and line.startswith("<svg "):
   5.295 +                for match in self.attr_regexp.finditer(line):
   5.296 +                    attrs[match.group("attr")] = match.group("value")
   5.297 +                found = True
   5.298 +            processed.append(line)
   5.299 +
   5.300 +        return "".join(processed), attrs
   5.301 +
   5.302 +    def get_dimensions(self, attrs):
   5.303 +
   5.304 +        "Return a (width, height) tuple using the 'attrs' dictionary."
   5.305 +
   5.306 +        if attrs and attrs.has_key("width") and attrs.has_key("height"):
   5.307 +            return attrs["width"], attrs["height"]
   5.308 +        else:
   5.309 +            return None
   5.310 +
   5.311 +    def get_format_attrs(self, attrs):
   5.312 +
   5.313 +        "Return a dictionary based on 'attrs' with only formatting attributes."
   5.314 +
   5.315 +        dattrs = {}
   5.316 +        for key in ("width", "height"):
   5.317 +            if attrs.has_key(key):
   5.318 +                dattrs[key] = attrs[key]
   5.319 +        return dattrs
   5.320 +
   5.321 +# vim: tabstop=4 expandtab shiftwidth=4