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