logilab/doctools

changeset 0:cc367abb080e

forget the past.
forget the past.
author root
date Wed Apr 26 10:48:09 2006 +0000 (3 years ago)
parents
children dfd377175b83 5f66dad05f6b
files .hgignore ChangeLog DEPENDS MANIFEST.in README SUGGESTS __init__.py __pkginfo__.py bin/mkdoc bin/py2dbk bin/trf-session bin/xml2dbk debian/changelog debian/compat debian/control debian/copyright debian/logilab-doctools-test.dirs debian/logilab-doctools.dirs debian/logilab-doctools.docs debian/logilab-doctools.examples debian/logilab-doctools.postinst debian/logilab-doctools.prerm debian/rules debian/watch doc/makefile doc/manuel_utilisateur.txt doc/spec-mkdoc.pdf doc/user_manual.txt editor.py examples/mkdocrc mkview.py py2db.py rest_docbook.py setup.cfg setup.py test/runtests.py test/unittest_transformer.py test/validation_tests.py transform.py transformer.py xmlformat.py
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/.hgignore	Wed Apr 26 10:48:09 2006 +0000
     1.3 @@ -0,0 +1,4 @@
     1.4 +(^|/)\.svn($|/)
     1.5 +(^|/)\.hg($|/)
     1.6 +(^|/)\.hgtags($|/)
     1.7 +^log$
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/ChangeLog	Wed Apr 26 10:48:09 2006 +0000
     2.3 @@ -0,0 +1,35 @@
     2.4 +Changelog for doctools
     2.5 +-----------------------
     2.6 +
     2.7 +2005-12-05  --  0.1.6
     2.8 +    * added fragment writer
     2.9 +    * bugfixes
    2.10 +	
    2.11 +2005-07-18  --  0.1.5
    2.12 +    * remove deprecated mkview example (close #8813)
    2.13 +    * changed dependency from libfop-java to fop
    2.14 +    * print external tools output in non quiet mode
    2.15 +
    2.16 +	
    2.17 +2005-03-30  --  0.1.4
    2.18 +    * added english documentation, dropped the mkview part
    2.19 +    * packaging fixes: added missing executable script py2dbk and xml2dbk
    2.20 +
    2.21 +	
    2.22 +	
    2.23 +2005-03-29  --  0.1.3
    2.24 +    * rename xml2db and py2db scripts into xml2dbk and py2dbk
    2.25 +    * first public release
    2.26 +
    2.27 +	
    2.28 +	
    2.29 +2004-06-03  --  0.1.2
    2.30 +    * Fix bug with xmlproc_parse with UTF-8 files.
    2.31 +
    2.32 +
    2.33 +
    2.34 +2004-06-01  --  0.1.1
    2.35 +    * First release.
    2.36 +
    2.37 +
    2.38 +
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/DEPENDS	Wed Apr 26 10:48:09 2006 +0000
     3.3 @@ -0,0 +1,3 @@
     3.4 +python-xml
     3.5 +fop
     3.6 +xsltproc
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/MANIFEST.in	Wed Apr 26 10:48:09 2006 +0000
     4.3 @@ -0,0 +1,11 @@
     4.4 +include README
     4.5 +include ChangeLog
     4.6 +include DEPENDS
     4.7 +include SUGGESTS 
     4.8 +include bin/mkdoc
     4.9 +include bin/mkview
    4.10 +include bin/py2dbk
    4.11 +include bin/xml2dbk
    4.12 +
    4.13 +recursive-include doc *
    4.14 +recursive-include examples *
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/README	Wed Apr 26 10:48:09 2006 +0000
     5.3 @@ -0,0 +1,5 @@
     5.4 +logilab doctools
     5.5 +================
     5.6 +
     5.7 +
     5.8 +Outils Logilab pour la production de documents.
     5.9 \ No newline at end of file
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/SUGGESTS	Wed Apr 26 10:48:09 2006 +0000
     6.3 @@ -0,0 +1,1 @@
     6.4 +logilab-xml
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/__init__.py	Wed Apr 26 10:48:09 2006 +0000
     7.3 @@ -0,0 +1,30 @@
     7.4 +# Copyright (c) 2000-2003 LOGILAB S.A. (Paris, FRANCE).
     7.5 +# http://www.logilab.fr/ -- mailto:contact@logilab.fr
     7.6 +#
     7.7 +# This program is free software; you can redistribute it and/or modify it under
     7.8 +# the terms of the GNU General Public License as published by the Free Software
     7.9 +# Foundation; either version 2 of the License, or (at your option) any later
    7.10 +# version.
    7.11 +#
    7.12 +# This program is distributed in the hope that it will be useful, but WITHOUT
    7.13 +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    7.14 +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
    7.15 +#
    7.16 +# You should have received a copy of the GNU General Public License along with
    7.17 +# this program; if not, write to the Free Software Foundation, Inc.,
    7.18 +# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    7.19 +"""
    7.20 +doctools is a python package to transform xml files into fo, pdf or html files
    7.21 +"""
    7.22 +
    7.23 +__revision__ = "$Id: __init__.py,v 1.2 2004-05-27 13:26:45 sand Exp $"
    7.24 +
    7.25 +try:
    7.26 +    false = False
    7.27 +except NameError:
    7.28 +    false = None
    7.29 +    
    7.30 +true = (not false)
    7.31 +
    7.32 +__builtins__['false'] = false
    7.33 +__builtins__['true'] = true
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/__pkginfo__.py	Wed Apr 26 10:48:09 2006 +0000
     8.3 @@ -0,0 +1,43 @@
     8.4 +# This program is free software; you can redistribute it and/or modify it under
     8.5 +# the terms of the GNU General Public License as published by the Free Software
     8.6 +# Foundation; either version 2 of the License, or (at your option) any later
     8.7 +# version.
     8.8 +#
     8.9 +# This program is distributed in the hope that it will be useful, but WITHOUT
    8.10 +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    8.11 +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details
    8.12 +#
    8.13 +# You should have received a copy of the GNU General Public License along with
    8.14 +# this program; if not, write to the Free Software Foundation, Inc.,
    8.15 +# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    8.16 +""" Copyright (c) 2003-2005 LOGILAB S.A. (Paris, FRANCE).
    8.17 +http://www.logilab.fr/ -- mailto:contact@logilab.fr  
    8.18 +"""
    8.19 +
    8.20 +__revision__ = "$Id: __pkginfo__.py,v 1.18 2005-12-05 12:35:30 arthur Exp $"
    8.21 +
    8.22 +modname = "doctools"
    8.23 +numversion = (0, 1, 6)
    8.24 +version = '.'.join([str(num) for num in numversion])
    8.25 +
    8.26 +license = 'GPL'
    8.27 +copyright = '''Copyright (c) 2003-2005 LOGILAB S.A. (Paris, FRANCE).
    8.28 +http://www.logilab.fr/ -- mailto:contact@logilab.fr'''
    8.29 +
    8.30 +author = "Logilab"
    8.31 +author_email = "devel@logilab.fr"
    8.32 +scripts = ['bin/mkdoc', 'bin/py2dbk', 'bin/xml2dbk']
    8.33 +
    8.34 +short_desc = "tools used at Logilab to make documents"
    8.35 +long_desc = "Set of tools to help writing documents."
    8.36 +web = "http://www.logilab.org/projects/doctools"
    8.37 +ftp = "ftp://ftp.logilab.org/pub/doctools"
    8.38 +mailinglist = "mailto://management-projects@logilab.org"
    8.39 +
    8.40 +subpackage_of = 'logilab'
    8.41 +
    8.42 +debian_name = 'logilab-doctools'
    8.43 +debian_maintainer = 'Alexandre Fayolle '
    8.44 +debian_maintainer_email = 'alexandre.fayolle@logilab.fr'
    8.45 +pyversions = ["2.3", "2.4"]
    8.46 +
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/bin/mkdoc	Wed Apr 26 10:48:09 2006 +0000
     9.3 @@ -0,0 +1,7 @@
     9.4 +#!/usr/bin/env python
     9.5 +# -*- coding: ISO-8859-1 -*-
     9.6 +
     9.7 +from logilab.doctools import transform
     9.8 +import sys
     9.9 +sys.exit(transform.run(sys.argv[1:]))
    9.10 +
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/bin/py2dbk	Wed Apr 26 10:48:09 2006 +0000
    10.3 @@ -0,0 +1,6 @@
    10.4 +#!/usr/bin/env python
    10.5 +
    10.6 +from logilab.doctools import py2db
    10.7 +import sys
    10.8 +py2db.run(sys.argv[1:])
    10.9 +
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/bin/trf-session	Wed Apr 26 10:48:09 2006 +0000
    11.3 @@ -0,0 +1,58 @@
    11.4 +#!/usr/bin/env python
    11.5 +# -*- coding: ISO-8859-1 -*-
    11.6 +
    11.7 +import sys
    11.8 +from os import system
    11.9 +
   11.10 +USAGE = """
   11.11 +Produire les conventions de formation (fichiers xml-docbook) :
   11.12 +  format-session conv fichier-session.xml
   11.13 +
   11.14 +Produire la liste de présence (fichier pdf) :
   11.15 +  format-session pres fichier-session.xml
   11.16 +
   11.17 +Produire les feuilles d'évaluation (fichier pdf) :
   11.18 +  format-session eval fichier-session.xml
   11.19 +
   11.20 +Produire les certificats de formation (fichiers xml-docbook) :
   11.21 +  format-session cert fichier-session.xml
   11.22 +"""
   11.23 +
   11.24 +xslt_dir = "/usr/share/sgml/logilab-xml/stylesheet/others"
   11.25 +
   11.26 +xslts = { 'conv': "%s/session2conv-form-dcbk.xsl" %xslt_dir,
   11.27 +          'pres': "%s/session2liste-pres-fo.xsl" %xslt_dir,
   11.28 +          'eval': "%s/session2eval-fo.xsl" %xslt_dir,
   11.29 +          'cert': "%s/session2certif-form-dcbk.xsl" %xslt_dir,
   11.30 +        }
   11.31 +
   11.32 +if __name__ == '__main__' :
   11.33 +    if len(sys.argv) != 3 :
   11.34 +        print USAGE
   11.35 +        sys.exit(1)
   11.36 +
   11.37 +    if sys.argv[1] == 'conv' :
   11.38 +       	line = "xsltproc %s %s" %(xslts['conv'],sys.argv[2])
   11.39 +        system(line)
   11.40 +
   11.41 +    elif sys.argv[1] == 'pres' :
   11.42 +        line = "xsltproc --output liste-presence.fo %s %s" %(xslts['pres'],sys.argv[2])
   11.43 +        system(line)
   11.44 +        line = "/home/logilab/bin/fop liste-presence.fo liste-presence.pdf"
   11.45 +        system(line)
   11.46 +        system("rm liste-presence.fo")
   11.47 +
   11.48 +    elif sys.argv[1] == 'eval' :
   11.49 +        line = "xsltproc --output evaluations.fo %s %s" %(xslts['eval'],sys.argv[2])
   11.50 +        system(line)
   11.51 +        line = "/home/logilab/bin/fop evaluations.fo evaluations.pdf"
   11.52 +        system(line)
   11.53 +        system("rm evaluations.fo")
   11.54 +
   11.55 +    elif sys.argv[1] == 'cert' :
   11.56 +       	line = "xsltproc %s %s" %(xslts['cert'],sys.argv[2])
   11.57 +        system(line)
   11.58 +
   11.59 +    else :
   11.60 +        print USAGE
   11.61 +        sys.exit(1)
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/bin/xml2dbk	Wed Apr 26 10:48:09 2006 +0000
    12.3 @@ -0,0 +1,7 @@
    12.4 +#!/usr/bin/env python
    12.5 +
    12.6 +from logilab.doctools import xmlformat
    12.7 +import sys
    12.8 +
    12.9 +xmlformat.run(sys.argv[1:])
   12.10 +
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/debian/changelog	Wed Apr 26 10:48:09 2006 +0000
    13.3 @@ -0,0 +1,66 @@
    13.4 +logilab-doctools (0.1.6-4) unstable; urgency=low
    13.5 +
    13.6 +  * Added missing dependency on logilab-common
    13.7 +  * Updated debhelper build dep to 5.0.0 and compat mode accordingly
    13.8 +
    13.9 + -- Alexandre Fayolle <afayolle@debian.org>  Mon, 27 Feb 2006 14:32:37 +0100
   13.10 +
   13.11 +logilab-doctools (0.1.6-3) unstable; urgency=low
   13.12 +
   13.13 +  * fixed dangerous postrm script
   13.14 +
   13.15 + -- Alexandre Fayolle <afayolle@debian.org>  Thu, 16 Feb 2006 17:20:26 +0100
   13.16 +
   13.17 +logilab-doctools (0.1.6-2) unstable; urgency=low
   13.18 +
   13.19 +  * reorganization to install into site-python, removing the need for 
   13.20 +    pythonX.X- packages
   13.21 +
   13.22 + -- Sylvain Thénault <sylvain.thenault@logilab.fr>  Mon, 23 Jan 2006 16:35:02 +0100
   13.23 +
   13.24 +logilab-doctools (0.1.6-1) unstable; urgency=low
   13.25 +
   13.26 +  * new upstream release
   13.27 +
   13.28 + -- Arthur Lutz <arthur.lutz@logilab.fr>  Mon,  5 Dec 2005 13:33:28 +0100
   13.29 +
   13.30 +logilab-doctools (0.1.5-1) unstable; urgency=low
   13.31 +
   13.32 +  * new upstream release
   13.33 +  * depends on fop instead of libfop-java
   13.34 +  * python-logilab-doctools has been renamed to logilab-doctools
   13.35 +  
   13.36 + -- Sylvain Thénault <sylvain.thenault@logilab.fr>  Mon, 18 Jul 2005 15:24:17 +0200
   13.37 +
   13.38 +logilab-doctools (0.1.4-1) unstable; urgency=low
   13.39 +
   13.40 +  * new upstream release
   13.41 +  * added home page to packages description
   13.42 +  * added watch file
   13.43 +
   13.44 + -- Sylvain Thénault <sylvain.thenault@logilab.fr>  Wed, 30 Mar 2005 11:17:26 +0200
   13.45 +
   13.46 +logilab-doctools (0.1.3-1) unstable; urgency=low
   13.47 +
   13.48 +  * new upstream release
   13.49 +
   13.50 + -- Sylvain Thénault <sylvain.thenault@logilab.fr>  Tue, 29 Mar 2005 13:56:25 +0200
   13.51 +
   13.52 +logilab-doctools (0.1.2-1) unstable; urgency=low
   13.53 +
   13.54 +  * Fix bug with xmlproc_parse for UTF-8 files.
   13.55 +
   13.56 + -- Sandrine Ribeau <sand@logilab.fr>  Thu,  3 Jun 2004 15:58:08 +0200
   13.57 +
   13.58 +logilab-doctools (0.1.1-1) unstable; urgency=low
   13.59 +
   13.60 +  * Changes package name. Becomes logilab-doctools instead of doctools.
   13.61 +
   13.62 + -- Sandrine Ribeau <sand@logilab.fr>  Tue,  1 Jun 2004 13:36:57 +0200
   13.63 +
   13.64 +logilab-doctools (0.1.0-1) unstable; urgency=low
   13.65 +
   13.66 +  * First release.
   13.67 +
   13.68 + -- Sandrine Ribeau <sand@logilab.fr>  Thu, 27 May 2004 15:09:21 +0200
   13.69 +
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/debian/compat	Wed Apr 26 10:48:09 2006 +0000
    14.3 @@ -0,0 +1,1 @@
    14.4 +5
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/debian/control	Wed Apr 26 10:48:09 2006 +0000
    15.3 @@ -0,0 +1,19 @@
    15.4 +Source: logilab-doctools
    15.5 +Section: python
    15.6 +Priority: optional
    15.7 +Maintainer: Alexandre Fayolle  <alexandre.fayolle@logilab.fr> 
    15.8 +Build-Depends: debhelper (>= 5.0.0), python-dev
    15.9 +Standards-Version: 3.6.2
   15.10 +
   15.11 +Package: logilab-doctools
   15.12 +Architecture: all
   15.13 +Suggests: logilab-xml
   15.14 +Depends: python, python-xml, fop, xsltproc, python-logilab-common (>= 0.13.1-4)
   15.15 +Provides: python2.3-logilab-doctools, python2.4-logilab-doctools
   15.16 +Conflicts: python2.3-logilab-doctools, python2.4-logilab-doctools
   15.17 +Replaces: python2.3-logilab-doctools, python2.4-logilab-doctools
   15.18 +Description: tools used at Logilab to make documents
   15.19 + Set of tools to help writing documents.
   15.20 + .
   15.21 + Homepage: http://www.logilab.org/projects/doctools
   15.22 +
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/debian/copyright	Wed Apr 26 10:48:09 2006 +0000
    16.3 @@ -0,0 +1,28 @@
    16.4 +This package was debianized by Alexandre Fayolle  <alexandre.fayolle@logilab.fr>  Sat, 13 Apr 2002 19:05:23 +0200.
    16.5 +
    16.6 +It was downloaded from ftp://ftp.logilab.org/pub/doctools
    16.7 +
    16.8 +Upstream Author: 
    16.9 +
   16.10 +  Logilab <devel@logilab.fr>
   16.11 +
   16.12 +Copyright:
   16.13 +
   16.14 +Copyright (c) 2003-2005 LOGILAB S.A. (Paris, FRANCE).
   16.15 +http://www.logilab.fr/ -- mailto:contact@logilab.fr
   16.16 +
   16.17 +This program is free software; you can redistribute it and/or modify it under
   16.18 +the terms of the GNU General Public License as published by the Free Software
   16.19 +Foundation; either version 2 of the License, or (at your option) any later
   16.20 +version.
   16.21 +
   16.22 +This program is distributed in the hope that it will be useful, but WITHOUT
   16.23 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   16.24 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
   16.25 +
   16.26 +You should have received a copy of the GNU General Public License along with
   16.27 +this program; if not, write to the Free Software Foundation, Inc.,
   16.28 +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
   16.29 +
   16.30 +On Debian systems, the complete text of the GNU General Public License
   16.31 +may be found in '/usr/share/common-licenses/GPL'.
    17.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.2 +++ b/debian/logilab-doctools-test.dirs	Wed Apr 26 10:48:09 2006 +0000
    17.3 @@ -0,0 +1,2 @@
    17.4 +usr/share/doc/logilab-doctools/
    17.5 +usr/share/doc/logilab-doctools/test
    18.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.2 +++ b/debian/logilab-doctools.dirs	Wed Apr 26 10:48:09 2006 +0000
    18.3 @@ -0,0 +1,6 @@
    18.4 +usr/lib/site-python
    18.5 +usr/lib/site-python/logilab
    18.6 +usr/lib/site-python/logilab/doctools
    18.7 +usr/share/doc/logilab-doctools
    18.8 +usr/share/doc/logilab-doctools
    18.9 +usr/share/doc/logilab-doctools/test
    19.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    19.2 +++ b/debian/logilab-doctools.docs	Wed Apr 26 10:48:09 2006 +0000
    19.3 @@ -0,0 +1,5 @@
    19.4 +doc/user_manual.html
    19.5 +doc/manuel_utilisateur.html
    19.6 +doc/manuel_utilisateur.txt
    19.7 +doc/user_manual.txt
    19.8 +doc/spec-mkdoc.pdf
    20.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.2 +++ b/debian/logilab-doctools.examples	Wed Apr 26 10:48:09 2006 +0000
    20.3 @@ -0,0 +1,1 @@
    20.4 +examples/*
    21.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.2 +++ b/debian/logilab-doctools.postinst	Wed Apr 26 10:48:09 2006 +0000
    21.3 @@ -0,0 +1,26 @@
    21.4 +#! /bin/sh -e
    21.5 +#
    21.6 +
    21.7 +
    21.8 +touch /usr/lib/site-python/logilab/__init__.py
    21.9 +
   21.10 +
   21.11 +# precompile python files
   21.12 +VERSION=2.3
   21.13 +PACKAGEDIR=/usr/lib/site-python/logilab/doctools
   21.14 +case "$1" in
   21.15 +    configure|abort-upgrade|abort-remove|abort-deconfigure)
   21.16 +	python$VERSION -O /usr/lib/python$VERSION/compileall.py -q $PACKAGEDIR
   21.17 +	python$VERSION /usr/lib/python$VERSION/compileall.py -q $PACKAGEDIR
   21.18 +    ;;
   21.19 +
   21.20 +    *)
   21.21 +        echo "postinst called with unknown argument \`$1'" >&2
   21.22 +        exit 1
   21.23 +    ;;
   21.24 +esac
   21.25 +
   21.26 +
   21.27 +#DEBHELPER#
   21.28 +
   21.29 +exit 0
    22.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2 +++ b/debian/logilab-doctools.prerm	Wed Apr 26 10:48:09 2006 +0000
    22.3 @@ -0,0 +1,14 @@
    22.4 +#! /bin/sh -e
    22.5 +#
    22.6 +
    22.7 +# remove .pyc and .pyo files
    22.8 +dpkg --listfiles logilab-doctools |
    22.9 +	awk '$0~/\.py$/ {print $0"c\n" $0"o"}' |
   22.10 +	xargs rm -f >&2
   22.11 +
   22.12 +
   22.13 +
   22.14 +
   22.15 +#DEBHELPER#
   22.16 +
   22.17 +exit 0
    23.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.2 +++ b/debian/rules	Wed Apr 26 10:48:09 2006 +0000
    23.3 @@ -0,0 +1,79 @@
    23.4 +#!/usr/bin/make -f
    23.5 +# Sample debian/rules that uses debhelper.
    23.6 +# GNU copyright 1997 to 1999 by Joey Hess.
    23.7 +#
    23.8 +# adapted by Logilab for automatic generation by debianize
    23.9 +# (part of the devtools project, http://www.logilab.org/projects/devtools)
   23.10 +#
   23.11 +# Copyright (c) 2003-2005 LOGILAB S.A. (Paris, FRANCE).
   23.12 +# http://www.logilab.fr/ -- mailto:contact@logilab.fr
   23.13 +
   23.14 +# Uncomment this to turn on verbose mode.
   23.15 +#export DH_VERBOSE=1
   23.16 +
   23.17 +build: build-stamp
   23.18 +build-stamp: 
   23.19 +	dh_testdir
   23.20 +	python setup.py -q build
   23.21 +	touch build-stamp
   23.22 +
   23.23 +clean: 
   23.24 +	dh_testdir
   23.25 +	dh_testroot
   23.26 +	rm -f build-stamp configure-stamp
   23.27 +	rm -rf build
   23.28 +	find . -name "*.pyc" | xargs rm -f
   23.29 +	rm -f changelog.gz
   23.30 +	dh_clean
   23.31 +
   23.32 +install: build
   23.33 +	dh_testdir
   23.34 +	dh_testroot
   23.35 +	dh_clean -k
   23.36 +	dh_installdirs
   23.37 +	python setup.py -q install_lib --no-compile --install-dir=debian/logilab-doctools/usr/lib/site-python
   23.38 +	python setup.py -q install_headers --install-dir=debian/logilab-doctools/usr/include/
   23.39 +	python setup.py -q install_scripts --install-dir=debian/logilab-doctools/usr/bin/
   23.40 +	# remove sub-package __init__ file (created in postinst)
   23.41 +	rm debian/logilab-doctools/usr/lib/site-python/logilab/__init__.py
   23.42 +	# remove test directory (installed in a separated package)
   23.43 +	rm -rf debian/logilab-doctools/usr/lib/site-python/logilab/doctools/test
   23.44 +	if head -1 debian/logilab-doctools/usr/bin/mkdoc | grep "^#! */usr/bin" | grep "python" >/dev/null ; then \
   23.45 +		sed -i "s@^#! */usr/bin/env \+python\$$@#!/usr/bin/python@" debian/logilab-doctools/usr/bin/mkdoc; \
   23.46 +	fi
   23.47 +	chmod a+x debian/logilab-doctools/usr/bin/mkdoc
   23.48 +	if head -1 debian/logilab-doctools/usr/bin/py2dbk | grep "^#! */usr/bin" | grep "python" >/dev/null ; then \
   23.49 +		sed -i "s@^#! */usr/bin/env \+python\$$@#!/usr/bin/python@" debian/logilab-doctools/usr/bin/py2dbk; \
   23.50 +	fi
   23.51 +	chmod a+x debian/logilab-doctools/usr/bin/py2dbk
   23.52 +	if head -1 debian/logilab-doctools/usr/bin/xml2dbk | grep "^#! */usr/bin" | grep "python" >/dev/null ; then \
   23.53 +		sed -i "s@^#! */usr/bin/env \+python\$$@#!/usr/bin/python@" debian/logilab-doctools/usr/bin/xml2dbk; \
   23.54 +	fi
   23.55 +	chmod a+x debian/logilab-doctools/usr/bin/xml2dbk
   23.56 +	# install tests
   23.57 +	(cd test && find . -type f -not \( -path '*/CVS/*' -or -name '*.pyc' \) -exec install -D --mode=644 {} ../debian/logilab-doctools/usr/share/doc/logilab-doctools/test/{} \;)
   23.58 +
   23.59 +
   23.60 +# Build architecture-independent files here.
   23.61 +binary-indep: build install
   23.62 +	dh_testdir 
   23.63 +	dh_testroot 
   23.64 +	dh_install -i
   23.65 +	gzip -9 -c ChangeLog > changelog.gz
   23.66 +	dh_installchangelogs -i
   23.67 +	dh_installexamples -i
   23.68 +	dh_installdocs -i README changelog.gz
   23.69 +	dh_installman -i
   23.70 +	dh_link -i
   23.71 +	dh_compress -i -X.py -X.ini -X.xml -Xtest
   23.72 +	dh_fixperms -i
   23.73 +	dh_installdeb -i
   23.74 +	dh_gencontrol -i 
   23.75 +	dh_md5sums -i
   23.76 +	dh_builddeb -i
   23.77 +
   23.78 +
   23.79 +
   23.80 +binary: binary-indep
   23.81 +.PHONY: build clean binary binary-indep
   23.82 +
    24.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.2 +++ b/debian/watch	Wed Apr 26 10:48:09 2006 +0000
    24.3 @@ -0,0 +1,3 @@
    24.4 +version=2
    24.5 +ftp://ftp.logilab.org/pub/doctools/doctools-(.*)\.tar\.gz debian uupdate
    24.6 +
    25.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    25.2 +++ b/doc/makefile	Wed Apr 26 10:48:09 2006 +0000
    25.3 @@ -0,0 +1,15 @@
    25.4 +MKHTML=mkdoc --target=html --stylesheet=single-file
    25.5 +MKHTML_OPT=--param toc.section.depth=1
    25.6 +
    25.7 +SRC=.
    25.8 +
    25.9 +TXTFILES:= $(wildcard *.txt)
   25.10 +TARGET := $(TXTFILES:.txt=.html)
   25.11 +
   25.12 +all: ${TARGET}
   25.13 +
   25.14 +%.html: %.txt
   25.15 +	${MKHTML} ${MKHTMLOPTS} $<
   25.16 +
   25.17 +clean:
   25.18 +	rm -f *.html
    26.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    26.2 +++ b/doc/manuel_utilisateur.txt	Wed Apr 26 10:48:09 2006 +0000
    26.3 @@ -0,0 +1,133 @@
    26.4 +Manuel utilisateur
    26.5 +==================
    26.6 +
    26.7 +:Author: Sylvain Thénault
    26.8 +:Organization: Logilab
    26.9 +:Version: $Revision: 1.1 $
   26.10 +:Date: $Date: 2005-03-29 14:24:04 $
   26.11 +:Abstract:
   26.12 +    Manuel pour l'utilisateur des outils de documentations de
   26.13 +    Logilab.
   26.14 +
   26.15 +
   26.16 +py2dbk
   26.17 +------
   26.18 +
   26.19 +Description
   26.20 +```````````
   26.21 +Formate un script Python afin de pouvoir l'inclure dans un document Docbook.
   26.22 +
   26.23 +Synopsis
   26.24 +````````
   26.25 +::
   26.26 +
   26.27 +    USAGE: py2dbk [OPTIONS] <input.py>...
   26.28 +
   26.29 +    OPTIONS:
   26.30 +      -h / --help
   26.31 +	   display this help message and exit
   26.32 +
   26.33 +      -r / --root "rootstring"
   26.34 +	   insert "rootstring" as root
   26.35 +
   26.36 +      -f / --format <OUTPUT_FORMAT>
   26.37 +	   set output format. Default to docbook.
   26.38 +	   Available formats are docbook, extended-docbook.
   26.39 +
   26.40 +      -s / --stdout
   26.41 +	   write results to standard output
   26.42 +
   26.43 +Exemple
   26.44 +```````
   26.45 +::
   26.46 +
   26.47 +    py2dbk --format extended-docbook monfichier.py
   26.48 +
   26.49 +Cet exemple produira en sortie un fichier monfichier.xml.
   26.50 +
   26.51 +
   26.52 +xml2dbk
   26.53 +-------
   26.54 +
   26.55 +Description
   26.56 +```````````
   26.57 +Formatte un fichier XML afin de pouvoir l'inclure dans un document Docbook. Il
   26.58 +est également possible d'obtenir une sortie HTML (colorisée)
   26.59 +
   26.60 +Synopsis
   26.61 +````````
   26.62 +::
   26.63 +
   26.64 +    USAGE: xml2dbk [OPTIONS] <input.xml>...
   26.65 +
   26.66 +    OPTIONS:
   26.67 +      -h / --help
   26.68 +	   display this help message and exit
   26.69 +
   26.70 +      -o / --output <OUTPUT_FILE>
   26.71 +	   write results in file <OUTPUT_FILE>.
   26.72 +      -s / --stdout
   26.73 +	   write results to standard output.
   26.74 +      -e / --encoding iso-8859-1
   26.75 +	   specify encoding to use in outputs.
   26.76 +
   26.77 +      -n / --no-head
   26.78 +	   do not insert output headers.
   26.79 +
   26.80 +      -f / --format <OUTPUT_FORMAT>
   26.81 +	   set output format. Default to docbook.
   26.82 +	   Available formats are docbook, extended-docbook, html.
   26.83 +
   26.84 +Exemple
   26.85 +```````
   26.86 +::
   26.87 +
   26.88 +    xml2dbk --format extended-docbook monfichier.xml
   26.89 +
   26.90 +Cet exemple produira en sortie un fichier monfichier_dcbk.xml.
   26.91 +
   26.92 +
   26.93 +mkdoc
   26.94 +-----
   26.95 +
   26.96 +Description
   26.97 +```````````
   26.98 +Convertit des fichiers au format ReST_ (Restructured Text) ou Docbook_ dans
   26.99 +divers formats tels que html ou pdf.
  26.100 +
  26.101 +Synopsis
  26.102 +````````
  26.103 +::
  26.104 +
  26.105 +    USAGE: mkdoc [OPTIONS] <input file>...
  26.106 +
  26.107 +    OPTIONS:
  26.108 +      -h / --help
  26.109 +	   display this help message and exit
  26.110 +
  26.111 +      -f / --format <OUTPUT_FORMAT>
  26.112 +	   set output format. Default to html.
  26.113 +	   Available formats are docbook, html, multi_html, pdf, pdf_ao, pdf_iup, pdf_manual, pdf_psyc, site_html.
  26.114 +
  26.115 +      -n / --noverif
  26.116 +	   doesn't verify XML correctness.
  26.117 +      -k / --keep-tmp
  26.118 +	   doesn't remove temporary directory where transforms are done.
  26.119 +
  26.120 +      -p / --parameter <NAME>:<VALUE>
  26.121 +	   sets the <NAME> stylesheet parameter to <VALUE>. You may set this option
  26.122 +	   multiple times. Parameters are given to the xslt processor.
  26.123 +
  26.124 +Exemple
  26.125 +```````
  26.126 +::
  26.127 +
  26.128 +    mkdoc --format pdf monfichier.rst unautrefichier.xml
  26.129 +
  26.130 +Cet exemple produira en sortie 2 fichiers : monfichier.pdf et
  26.131 +unautrefichier.pdf.
  26.132 +
  26.133 +
  26.134 +
  26.135 +.. _ReST: http://docutils.sourceforge.net/rst.html
  26.136 +.. _Docbook: http://www.docbook.org
  26.137 \ No newline at end of file
    27.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    27.2 +++ b/doc/spec-mkdoc.pdf	Wed Apr 26 10:48:09 2006 +0000
    27.3 @@ -0,0 +1,299 @@
    27.4 +%PDF-1.3
    27.5 +%ª«¬­
    27.6 +4 0 obj
    27.7 +<< /Type /Info
    27.8 +/Producer (FOP 0.20.5) >>
    27.9 +endobj
   27.10 +5 0 obj
   27.11 +<< /Length 620 /Filter [ /ASCII85Decode /FlateDecode ]
   27.12 + >>
   27.13 +stream
   27.14 +GasJPhf$st&BE]"=87bUd$r?G@XCmmK;"u2K/E_(Q%ptd-Ypg*-)LW*cC36X8egrlZ$"sMS\Jgp<>&TmE.SKH8[]55s3c^dV[*TO59q]_aTXse^_J(c5oqA2o#^%[a!mE2V4D/&KK2`J:Qg*K3'?0r$n=QP%&E@)I^dZ$@SuR\+2@_cdrj3VHU,Jr9fFjS.8$f@1;CS02S+r@o\5r%;q))<K%LA;@Q6'ag3$.=BW9((/>KDr<RT4V^o.$m'pk<^q-BTPf0VXApUX+idi"KDN7QP%K"<SJ"09OJc<!rZJc8RN:3#F2.1a;KJ:4[&Hn?!\](<r/`gD7\o_nWX4ne,'>)a=@"WPAbb"c?H<6c+>>1cC`<#.H<3#j&*??Xuh#2G<IBc9n7?u`e5k(Z:][J;.2]1Y:lc0(`W)rq7H:>jM0L3g54P7[b@U!iM.>,TpJ1J>S&d]s#9TNhjkem31ng2,r/fCL*YGDktO-kI)*Y7T;#=l0JYb[Y9)QQV51Bb=Y+(.^WRa5ZFGXHsp;HU+iVadKb=k*USmX+%CSh:<7YQ7p(9f`^moJ$Z=pDQC2$AU^T:=Ur7V(ld>k`Ug/iX-aZ1Qln^G&@)VsPc6n>5B6BIc2~>
   27.15 +endstream
   27.16 +endobj
   27.17 +6 0 obj
   27.18 +<</Type /XObject
   27.19 +/Subtype /Image
   27.20 +/Name /Im1
   27.21 +/Length 21819
   27.22 +/Width 357
   27.23 +/Height 165
   27.24 +/BitsPerComponent 8
   27.25 +/ColorSpace /DeviceRGB
   27.26 +/Filter [ /ASCII85Decode /FlateDecode ]
   27.27 +>>
   27.28 +stream
   27.29 +Gb"/L$(Ou6[5Znte$HnBbQ>us+:9#Ug]1h\FtkU_@at0@N'>#&-%:`G0n^sb4bt!bU5AF9N64ML-ZOu9?As]=Lk7l'&Ju8q(^:23IAUY'X7+hJpRZ_Lh5.`=T"WFQIQ_mmhl^2,3WD/'<7NG;,kW"3(k3bBm2pe.s0?T38ba,d!8k!dU^3/08Q%Jp7I!lM7hFUsoP=Cc%9<1o0[mbrE"ULdFAO8/8WqSPKX-ZuY<iJhG?G&KNY),L8k"DD_@0\3,Y:nC,qf(g82qS!7`*D$d`B97/9$P**+TNU=adjOa\u+A$QN$X$IuFQ!1nu'd&\Kt8<VJ^M457meN_I0gq\MAbl9gf\q+^FU\q<:%__kBb1C:r[!2&R[K$.,g\E`hr$cF6_8<URE-!_c!f/"Z&P8.3#cVu9LkQaMdm7$Fn_aY%h"B19CdG=)WaB_f_12B6<NKgbLKUPce-tI%OsEWahP@Y.<:p<oO8A6<?bRNTr<Y)A+f'jRT4a/@EPnTICEc?5Uc'R=B@3G]7GY,hIk6O!-np)LjqFh6VkR\*?q49fOsEV98;a#PCllZBMY@po!\8MqI!Zp8hlSk/,G@r$;<OkLBh4E:>mOt&^mYe=:!6_"'g1#onL=oGY`PKpKs9Q0OsEV)Q#M":,bVXfWja#s<^<ZeE%X6G'arNE,%8q501h<PF_O1P#l@cJ_..9\qtT,:8`-[YTk\`?L,c@dd6j(m/l.T6k23TDXa(5(MsC'!Zc7X6?s"<lOsG0?Wb3npqu6:k6K$kMo(uR"Kear)=)CNle6RmOE['K%^oK7orVf@/cn6SW8<VKQM/5%d@*Q:sAW5t_p++^=J7F<*G"RqI>pud$J;\$jZN&9!0.$fS)HSS$c%c9FUimnQ;E#d?/m*XXW`d<PKYRaAs+giZj[?*o8_oG,Gn)?\iBM;>onSP@.sAZ&#]&-3$.O^H9YuK/8_o>a:c?-NG%CQsVgmE8X9^Bpa:>CdiFChC1P^mKXdUM,P1kMPr>ji4--!q'[4MdGfSbUnIML`52e=:\\=$tX"O)uR8Q,:*eSYYHJh8NMaUW^2mEbek0!)#JrL%KbqlFKb$i?o$+dZn7P12&i,WZbP.Cqh7(ou>5FOd!n`/91s?bh\<@/`MEeAUBQ=M[;?Kd95qRcr,%b^4r?#K+B>lCSL8Z)0\Ni$U+K5I^+7WO=4[lT];LqYkkR*i*d7$Q]V0RTfh:lJ@a:+\>S@,esb0lB>q?\&0dK<6na.1&kZ_]8I^+(G#M@Yk+X*p(&=On%W7qhlur?XRREK5/MpEMJk9bU:ptk'p9hb91"=4f3p/m&_:Sg^qL(5-k"!]<@h%CXO'e\Er+=b=j#_2IThke#>())!nfU^$)u(%!Y,5HBi.&oT[9$M)*a?(Kd95QM6%g2ich+LkE^'18Q(Tm'e#dr58MheKko&_?:C&'8Q$?JU,*@jOQRNSOsJ/7,5gM4(3R0"/C-kM,cM08PKG!fa<$1e<1/CI8WqeoUr)#!.!@8KoI!;;-OCUFP_>IjGS*1F6'YtJ%WGjfm0G/]@1G\\.5>JW]s/6Z(#?6TE=16,:X!ViG#\8`F]+)c]BB7)E6DC%(l<s"`Z\1:"r@n#,]uti+pu)+I*CAu*2LV(ob'),`Cc]CVdV?uO#OE5%i;mh^]k+D'FY39;l,SEXOeAu['[UrFa$C3O;&@r5snoh_=r710pu'Y-M3b5CY6aoU+Ycs7+g),KOWfje9oNes)%>fad6:Vfu4b7W[:J9f*Yf(a^nu&NdpE#GqCU6M%Z!2,NP1^LkYcN?c<onZA&I_Z!9jp#F2)imq5@0MZ$^Om`utO;W@f/Yb0J5@uZ7H9783&#b0D"9=V4(nq2^FWTcuG8]f)r8P(?n_6qm1r(Bp#?GcjK"[rk09ph3a%8!ggi`_DMYa-O5*NR=hrU%EI1Cl(NYO]*u%kkQHP[9iIW9_T;pK"<GLR-b;B_d(3QJ]ISL"gf&H\Bps*tC.u+sDsW@=;@m^H&Okg[/porQ`G*IhFW@?N',g<NGrgb*B$9Dr6Sq)[R8JMIM0K5$2ea.G<nS,Uo^Z!WmU`P<;ECO&(&@S(u2Ne@0?^I=5U,XM1J?,g?Z#d0qn<d%MIu0CV24R[A^oi_TP$#-s,90kS3)#10<OW>d[=87lK&\5Gdc(46T015&F\$4@S0VSs3=LF\F;G+kU_NX<.5!C24.]m=r)0oCkbqu&)e>m!ZhPh_OL`E*Xf@)2r#5)M5)Am;*h%8%<T2--VPp]r;)Po6"CDPO,IO:!Em1UA$NK9[pI*:p"=niCe>E,Qge]C>S\[/d]b..#R4kjR^R8Euub'eU3f,']c,9k',64?g9R"1SnC#TTnFbNi@H,@'m+Q)oD6cPI<2BoUKQ5bmm@Fc%gSZirn7E7HQh@K9Xu.P0j\iM)gLN2/?XF^h(rL%eYudEuoQGVdl&ptf$G"M!Zbhp50ik1;G0h&D)SDfT8p+^G2l\r1Y1>_%"6gOpfI$XB>f_R0aP/gO#GS^`@DN@_H?lH>3BMaRMBi?CZh$UsfhWijUg0B1u0,9#iq;c4?KojKMhKim$2:D'a8Uh@[+]+Eo;Wa//Yj@jY=P8:5hJI=/$^3HX;[>RkpH(C3)(YO3EUM(HefBT/?JT^'qQNI\:ag^9pX@c6'NUNU.=2KZefZAXO*_!CK_1f$tQqPV_*3Bj%(GT8qA#)0u)C&MI6=5_9C6@RO<EqQt/gNk5\#%Kp6n5u@GlP$%ri>)<6Id[knNKuRUVK=bF!<aO/f^S5aUbHl,P>T8Q6]%6M?tmrL$ul"0'X]e#UJ@q;S*O3>1I4S3f,R8i?C1CE$;4:a9!0,(+QLkq\KPT?%jP/88<>U1N&/)Roo1,Z*fK9>]^^DE$;2J86lAT`'T_H?bIfFU8sB/$VB_TVCgdI/cpg/eh35,d8``SKqo2%Aa*a(R>#AcXa<^X79?1#%i7"&BEqA@a>2*_C06<(@t@J$JYqm#$h!hPIJ4?.o2"appIaOg&,h0b5#e]D!Tf`7OkoMjO$dJ#"h&mtgpT!-H#%%p,[A8]h&\0OC0`4@`]91B8?Qo?`4OB>NjQ#]/Rq:Rp]q6LAYC>P$<F0NoRH[Zpm@N"lUV`uf<AWlCT1eBA#sjSKT!:9Sd#u^2T1^Pcp"6`Z_Cq`k#X_kIO2722$IG\8g(3S1P=3HSV2p0VB-!ZOua-<C5BA#/bU09iD:qs5$u'#_F7[-[tb@G?AkgGi,L7-R'n\?P2[ebG1/K=7?,m?=")9Z&bSsP#o@GSh&\21'%)+?/m:u0mA@^Rq2PI,!kcuD_,(7G6\1/Mr=Ua:R^seM#cC\<kgRoBYHFk*X_Xl7XfndDKETo88bi!]<K5nn,0s.Y&/cCT*cjD2c&bJ$:oqeu(aE5ND@%+r:Yca<J;*d?&WiF`/JBL;R$68`YAZ^U5uHP;6;-UA-Lj8^ga>2XJHt=j6J\m34+^.F_7^Tr4cGY8<Np^CkZ/eTHl%m:1e-2j\g:SeeD/ha;DQTd@Oc]rn/\rcm8;jSH9LOJ$;!f<'.-D9E7&Ak0h+--8F</=WJA8XC$?`L"Pht1QPM]C5JX#u!F,b[dU1Bnaaf;hmj25G2>%Nj(cZIgY87C*GC^-_*n?[r`PdUs-!YOqP&Sg?e3i/'d%D$T\8^nSWbG.qFC?:'MZ+Mc*C1;jg66h$>XY]_[4*A+FCp\Td@Sj_i5-02hR0t@^iKeuSY%04ZmYN3\.H:gg5.Tu0"NB$\7e\';/h(_i/-U]UdqRY\EM-^iM&K7#Jrogr@YL9/jU+&S>\"H+Q5Z(7@0km%MuR/.^n06-!X!NkgItuiIW0?DC+W''2fkX(D8@32"bgt5f,+ai]5'd<B2=08@[m3e2-%bOb'50^6.j[:M_a2>Y)BgdaU)3_FfDp@0u@d(m8iH8No"'\EFhViBhc"s0n!E0&].67@0mC!#J6AThl#^Ydld@eC7`1hg"KiF[a:.IJXLjfs5bLVTk2\CtSCQfADgZ<ua8#=^>=hf\"j)dLlrmSU@-5,7,.m0h.B-Eb89pRc-;&/kWW2W`#4>WM+7(?SN2$7QCc8bX/!/0VdOoDcS9ZVWL,d&.TY3?*mdjRDU-a[=U6QXpZ&sLk=:)n,Y*'pUAZ%e8<sKA''n1pYS4$k<K!U$SE9?o#X*54F"qt!@:0'r<ir>EcZ=!oZ>5#cHT4cYco5)0.A=f2Jh$UE>*6'p%IR@It$FtNIW_0?VK'7.#)+a6D<V5'a63@o#:?Fo2J[kgt:g,hWt*^0OUFYP(=C&Jrnn/mdRNGhhcT&SDN)1L'qdnnR(-+0#\=$;/%3RJ4:]i8OD]jA0+V9d$Jl;bsG&hKA`HV9>?1D6!qP)p"F3]o@RREJ.F<IpsScZ)tUrhrX_96N4E?opk^reI.QBZ5Pa8n+4\rn6g"f^?nEtC!_,C\G@1\_Ip`n*#G.#Wpp[nUY[mmPE#439B'D:R(EbEq%#\IqHHRnLN>QZt^sXBO\V9;4R40Sa*Xp\Ql]]$(r/S3'9#!nh9'HLdW'ZrARL5,X#Qnc3lDr+.X)"C<Kef=ZWbCj%m!.K`=0H-c)o@<2H^:XU"UJ72<O7#El=Z]6k.q9E]^)`4DS5L/&AWA54`BW8/"fUcorDF_qs9s+XBAb/f>%EK=n'8O]l"9s9C;!/Up<6(KO8Mi7RKNV.#f<so^Uq[97HWI-gnS3mci2M0l>XOl-lQJa#2IRHeWDPI.GA_s7K`KH2$.hp[$Q@6N[k/6,<f\KL3iOi.`iY5S)Gseh1=Dr;,`_5N.8/oCDJ$dP?uLN>;"H+Fq=u.TaDeR@1a^[4f,Tih]:TK0H,(QV]d]3q'T=c7?*2$>L%7]'HMgVU0FO":kT@dG2;6>eh`.dFu!8cC-Wf[*qc'aR>bdbOh1>2";>Nd1[Bt\01"/rC[#(@&LNEo[[5brV"ua"+b&`n7$%%fSgE7aeIrunG#99Bi'd$!@<8S_89BdEn)gmq>[,aTK#4t;cD-GoP!C"&`BP4Q^3oeBAgSAkFXsS,RCnNf/Gr=Jh#7'lH,'1"DWFr&4@&i\uDZCkK_ne]_q'0>s&#WiFhg6.S3G%%2Dr4:Fhf!fY;AI=q!g&TV,uNYZ%^9lC+&GH=d!Im0sL6((E19eo39#rZ$Fi0^Z?aI;m8hPL@q/H.cjM@f(+V5f?KS2WZtE,O;.hL->S7$dbTUAUa[Xs+NLsZ-mE9$Y=e4s3R%P%uPgF"Q.tg[GZW=5)f.b<il)4%o2HH3u^4dl$]B_F^uUH5S\Y3N[OdcO2D?9"W`0?.iGAc`GbJ;gD=i)]C3JoRKcbph9-cS>^/E2=>2-'^]eHhfE0URe^$DQr:&YiJCSp&Oc_JDnf!qS9ghb7+5ho9n!jER.d%/?M_0_)6LA6@fGe:=j5@a@TV@Je3P'.d!aeQS21JUq\>4@H[kttskf!aBaj'4"`YiDPo?8:,.Lq)CDHi8@gVfp@n-R[aYCHM?If0IIeQ78,NulK/DV='@X!*3),B+*\nFG\ElaF,#V]U<UU!ErS(pDl8#PO;2)BOd78Yr893+Hq$m`N6OjV%i!kZ5s00_5K*"cBr_g_slTGU>s)>9eD'<4F@A_In=Hi=f#b*XY/Zhd+]eh:9V+H?8<1,q1D37su3ChLu=R6#WVGVdhotr9t@0HH:F=OuuNDG8O?K^3_2?_MlR2>n*MMr6]E9"iW<+l$`I&h0\Uo5j8!AJPb$XTVXgfH`;0SL29dAo8\R\5c)-RN]R^l4;cNF;q*^>"_^^OC<MpFTG?DN360IM7-.HZ7?:%-o]Qc;b/Q+Bgi1/jF`hj!?(ZQ=3S.KB;:V[_S$2Xs6\h7lei225PiGfV+C(j>_*C62aiU<[WXm-Vg?CF$SGIce(Kq5^6r^KV7Yb^<JcqBmQCg9b$Bu]'QAKBh"LoI0,q(.k/mu=22<nEUc-1Rs1$b$fa"Z'IQCg>L\H-Vbhd0o;j:\0M'Q=X]Vn2X\'Ga1$3HH5C345d;<3j#k82Y\hJ;F`V@"AXTJ+C\bp%=<q5p7;f:ct/YKnI52/$Jk0WeG+<g9N>B;5'M3pn6l"lD_Z3+$P90roB9h5\1SF+Z]fCcoiX%SiDrY,X97A3CKO@)'b[$0_H_Y,Si!BU10e/JggG,kbb(EFVR:fP9E:S'Vl-G``PhPmmJXT,Nme^"%khR!&7WkIQ^pD0PK"8BfUm?'7AH?![Asee_Y`KI9d]-Toc"%a#0@A5&e>\A,+i`@1s`Rd;_kq.KM9f]7c:bRh"7ZFKe"tDqM9m[VXWK""o@h=P?9\>Hi#-c2/OU^jB%Y,ql\-ATY_.cnnE:9tO^bF8u6YQ'IWI!391NWqe7Q97*`@Mp-tXD2YeXq(B7=Fs2+.LCP=sa&W4i@tY5i3Da$KYG:V&eH#9O!+NaR':!4iJd^d4c9,?Y"M4cCMtc+Y+&kjrQ"8:&CgiD4$aFd^&$V4pqHI@-A]pA?5=b@Vl+$BQp+]$eFdnj&0>-d`H<P5qLpRm=@1"PXd]o#UFB$qDS*B1*@"eCT-9kBZKImKgc([YQU=&$FQD6/1>K^hLGV^<1kG_@tfnPQ!@#UoX1?k`-g"o:;.9pt/hZ[&3_hDrg@s`6'&Cnc(;+230<C*`M'l5T96f^Uq00(J@?_;Q!(kL#8K'!OZ'^)S:1A!XV_\j=p*o4oV+\C$89Lb3U']Mr@^G>,g4@;AmQX>30O.l(r<,2lO&3L.^%1Nb2rp_9=HJ?F*69C3:4*>0BaoaO'FY+NW"MJf"SekWc%a)SP*'0E3'U[`CGK8^^9+2%uP3GE?^kH4!#[M049e?+u_*_C0JmWr6&YKl2G>b6K.pQJCS931N<kZmkZ0K#Qdfc*WX#ikOG%X@9`l>!"AlC+U2GEH85'uu*:7T3N0($N[*RoQ$;t-$VOiJ"A9qZ,>C)rt1k/IX)YHm(3rPK.1jK-d,`eYaT\7DP3U$:?RcSMi>Es#St&T+,_r##/B+i[utgF+=nMh,.E3_%'#,f>qpFFqj;qtEHZdS;W\MEnaO`sIHq&0W=!#1&\nBB@:$TGMlQ_hZhF-P5\3&RTa6$gMV]?DG93ar$Hc5s=h&M,=W'MD0>bH*f.E2QWlRFf<F+m(IiEG*K1T[0p2na$)A#Lj9.?l4Ua/WYL_VSiuq8d*BKKPB,YSb0'!J?="4*:iL/2\DZ(9;cD/40c(s#UR4.9'SQh/Rpe_o6kV&3Tl&dsUueeaf#WuK>?Y3t*EH9B$]]HZS=h_Ln=,MbgqlB=cBOB->,(8DQBjF;_?A-6WC)k__ffCsTH+'EQA9,ZI$Cu^#4tY_8h0kZ$YhIqjd^!#U-$F'`J@'bNd:_3^`\_#>R3GCb_uhoc+c"Rl#uVAN20LLh?H4Nf0R][SiRk0e#>C:e4..[?@ZE;r9s[bIm!QAVp^!$$jrc3-"j8lR#DL^,@2@/Fg[DUZUn`Y-gRtMqWG^0M#\7>*W"Q4c_[G5+H$S:R?KTi?":S3W2E\4!cdKt&s:_lfBk,Jh$bod:-SJa"BG)-E?*o;og&8pc0\_5;?CIRkg;TtL7CAO$IC'.](!Sq_hT$E(M@;jq0YC*'2LG:Qh$goe>-"u19rfUr0NUo0FmK%k`][IWJo!e,Q#<r6fudXo=`l#@7RHAjSR@c&1q8IE3"]S6>bnt!5<mQc+!K[aeaqLON*+u;f8a4MTpbLUo'soNJVO]o>E0JG<dWq[Cu4\/%4/cJ0'8,TPr`%>aD@eLpiSI#?q$<Jh:^#'r9lC264B`W&Fh9A]hc#^r;@`oHOA_TEbi4qgj#SDr0rsrq[SJ5Jh!.,$_o#qX(cLhK.#V!sY!'LnV,-92X(X&M2^Oe#"Jq/s)^-Jm]=1KLbePf:%@1I$*=MW+r5ETN;7I/7Dhh4!bk!E4Ke-FY.\c*B/rih`R)tOUt1&CBR?7-9Rq^HpZBYINh`$0ln;#KoiMJeHOS7qIX0II8&S+KaS[ueu:<#^fabt6cs=,,m6c4RPU[hC=Mm]WY><>kfYa6M2i_+KFp]+D?C]&VXOHScc=&LcS)rqVOMbsgrV.jBYUsc"%0l@Ko:H^9PCN8Xi]Nn2-B2ipdn'!5ek'f=nX8[kE2MsY&If."euI/0^p64!!d+KkCO:&B?m#YbEi1g+9,TQB[NZC:5*/-//Z)t11S1b@J*jqUi6>Fgj\"KTodNr2Hso@hZ^CK2:l)k*Gr.fFl(h;\ecHC5/$O0X$6CD@mE9B+@o4$bqPB`?b^%7e15LK'Tl!_@cI`K%"*ZBK%<O`+4K@_A,tP&5sq:h9^6(s)<"\JSR,,hFQT!(6clOFp=_62`^$"\'`e(=mt>SG;`;X>79,qk.pRT^!Ne;.pkZ,fg;s"$`pWT*hVgDoWbOM&\;2p]SHX@Nc'l&KYDB&]fY`Ccn&PLE5G0MnX_i8=5UV18gi_6)g^e>IH4^+S[JX-<r\36*OIk%8U>iDga0T(1K(dBb9q.h#??PDabj&0`9#Xgg2FOLO\E&grlK[Z9qg*L@TWRMF)m?mAqUF"oC+Sj'-Po7k":FP$A(A!r`l/Bh!hO+[99ZB<WpQ#HPFqb`f</D*3!,)t?dOlnDVTL$V3gc+G02:0%^$Z,U72uEkX>:>dndGjh$/j/BBPh8*dnn^cXc4ah27kn43gq=kmYka5n?-N#^Mt,Yo=qIcq2pr60XY9rgVN.P-p.hbKE3#T\^Mq!m%o)8kk-t4DWO7W7-HjNZTmTQk-BT@8ph8ABRpX>t;r+_o@Qb4qOo"nV40c_UQgW4NIpVd%H[W?(.(FAl<$,$>c;mJ73leaO/R?@&Jr&2Z_NL`[)2sO1Nfp$^NR?VS7Z-AnNWd56@kp?AF%alqQoQE"P"L\ARZTB(Bl-l\\`;rMou0]A\J`S`]1l38^26]Zi=+<ksE$;47r)%k7\HIDC^L)`COq_6uCL%7B<7&SGQ"FN6#bc?@24[@2RC#"7:lbpcND7>*57.P!"O9cI(WpE$#[%$Io<l"bCc[Zuc4OdM"IM\e%dNS`1MO34iol01&_27@?'4Tu%bLFc8_>o_2rFi"G4K(UBS"Q`ka7Y$^NT3M@mBl[YTCq.9pAqiZ(*3mD)Jd7=iL!]D[$0nRgf>UmAI32g3i'J,%ZSZsX7ED*M\jkKq)<#of.7cKc7t#eKPfg@Eg\&7%@0r:4[;3OaZDEhH+"hO(2/7t(B?nH774o7N#T#Il??kEt>ZYq(,5ajI\DVsY=FQs;/>-`cGY1TNXBDlGhZC5<LS/nN!k49`mpFl.*.$!>)N&25_t3%UN`K-jLC:`VdR#';%(2%CjjN;`>2/3WBDKs0Gs$$hfKSN`]'#Z3;-g)*)7>=pm5r,(BOc1`h0\BZNFh_<j?)e>1DE30_1]E]>?Y3*55A_b%("\la`Tg"<#.;7pPp#,nc1ruh0F;A=T7OLV67pj%`Z^(UgC:EMjN@D<R`o=QH/IqipVBsLTP=PoP[-%V@l#gm'DZg(Hi]ikb'%H)q\*3VjdCqO?hA^Q9d_?'+YqTWk,pE]!bd:gh0A,qHe3aAJgWL,iDFUmFD29<HfDj$lpQ.\=i<-jZp>`)02m^X_20MZ'B0r#\<nb>Am#M\]@"bs/uM.?g,SG"_F(@kA^R[KYtdc]mDOj12s6-N=fTl8!=B^MeUY;a3lN'SH(9e_qoLac+9+cB$O8Dh:<t$5_\,n-3Tbmji?RVi,'*T&sKQK=_F:8?&!8Z\fmbV#(R"\;Cc"Rh%),kf!G>Y(iTHRneY[0bQ#3?:>eLDhb-TIp$GtFPEXV1GBRSQOtneX"="LS?t0rd'U4oLSp&h%5Ps-["q2oRE']Hbqt=++c-J&EX2Ap/5.I7W(bQuFpu4s1W3N@qmd7o^[:3Vu:\X>JeWRr+C"6X4nDL"=Y=bJVJnOkF^\e7m)qB9*&CX3*%=Dm?QK9<F7;D#GU'SDGe#)brXBmqQ3Cu0@"`SbVOsNc>Q=m:pF:`=9MqTMp1+d25:HnTmg<3H"G%(P[Q`I=(3c:Q]GD?PqP`>8)o(hn?NuBk_`aX'>:3p)I)D6WlG%qu<=F3<"k/,%*PEb-M3g?"N_kI"D63RN]>R02Xef)%Pmk[D/[(<i;ff=2GRS3G,n+QndL_I`'[C@GNIkNiF5p^^69Vd>;Ue[OG*@Ef9@COWec)5K/@5nn#hF3ol2_6f8#t%I.Rn0AMV[jlb1LnF`&Xk*^B%^o%-S%eZ`skhm^juT->6.a%UK.N`;HmA+c/H[FeT<%GYkf[@:!gKnIItp\6\jgbj8_@h-eg2rUH5NB=1r/#/EM%`K9Flb2WaCOSD&\24suc$)BChYZu)1afYb&2kMqUfkl_eDo@>qfWj'$T+6M$,/efC4;#c':FocdF)/#JU"1&@Y=DdZgf0J@D9PO`9d"_Dm[Fb(,Y8VPe"Wq;@0,6872[;TFM3<pr*PD6OfVqfI7P$b$GjE4PE0Rh+I,;!aJlrLp(s6d-jgO1.;AamT[-&]=AUl85*ig>W2P(`c@U=@$[2&oG(O"2HimfY<Dl;_I8RAXZ&$8#B$/948Nh*]+`P!$fO3QdQ?Z*J$N)HD.Jl>S]j>KQV0$L@<D4:nh"F>4TCN2q4eJ7So5Q*T6heE`l.j4bRQ^9(9YrQj3.TKcij'I1E'Z2h+(QCQUo[>es+'2b3o+t\SZ"_+3b2bl#_X7XFL4Z>t82(lcY[G:t!@;uR#ShW3E$1VW"g#N5'Xo[7\b%'hE+-C\^]&W*\el=)KkbBAdo*M7mVu+r7)S7S)DP\(<Gu^N.'[`bmLq$KU[c>^3bo+1PP0I?T,'%KpC;W:j40d!klE!&GYZ=t4rCMCFTeUZgWOuDkoS%538="nY#WN]B:jU,$>Sp&.`"`*p=V\#m4'/uFGoPr(V%CM2p1D=\6p9eqLST64o_l2.@AJr'C\D\$W-T1P6G,1!#J%2Z(*namuC`u'cF!J\j;1Q'TZaTQP5f%Qj9CK40`\(iIXG(H"M`shd6lR[BdP&)I-,+_WUM>Ia4>DN0p<B1AjSocJ<o5[8gc#LjL`G7AR_b!aMPrdO=[uZib1ck$qPd4um1cASO,Ca!mbJpZo$_Q,A;0);W!bJe>\*qt;dQ9,ba-T%/FmQ_8t.j$Mrk)IEPYd%#X'h.;g9\3O0fV.>%i\%L71J`^0BD%kMRie7cD)T3KN_Hd?ZX'P&pRI/HnO]PUqhM_7me-AlM;]DC"n\>=7=Le8%QY5LBi>QW7Hae)XEU<0V+I+e/1ZYu^rJ"j*kEjB9Lt?t(M-Ag'm+f:$pG2=h2D.oO6?VehSTir.))7?P0"UMGX)9Ye?!1K?;,l&;#)An1qF"Z?JHG"&W7lc;)r_MQf9BCIi&m=jb6#0bQm$Xik5d3"E4Dd9[P@F_NsdNj]s,4!Z#HsjlaEmr/FL.>\7ugO8Ob^=e'jug6h.XKd9MXt5B&<gDn_"HB6,Ic$9Qa(KF>e#Ie7VXC78k\ZAq3rJ!q<i(OPoEA!T.4=Fc"Ohu<=%T,]TZ^D"(Y[tG-d@k?8eK*MoAL"*HqWo+>h"dU=ZE]eYa3n$d9^(^o!:gn7M^.ig,B.o;I(GuX.1Ie`#HZ*ZPN]f*PN!gF&>UU2Xj'B-hQ;`;A>=p#C^e;+QgQ@nlagDM'\0-)B7NV/tV/j*(>gnRbK'JUC4Oba^^sgI]^7W2]S9ZMfn9hrTpJl7&Bk2F%#HT/r3@h/VNZC1b[7BFWgRH&]cThF2L)$GH,L1_e*J3bMRm6_jPod9X_)dL*ZGuK8HtC6Q!P2igH6tn$@:?q!dQo0*"8V,1rW<Q!q\Kq\jl[HRm[Xdn*^dgj`fq=MTYlk?^#?$:k58]B>q7a>`PEeHLD>U8E+?i/NKdTU(L5AlmGbp4K=SP'!i4JcL"9MIGG0(g/*E(=n(F2$PAf_h-T4e.aqqM&+C8>Z9Cqt*bLRB"Z-1\2]+(*q3o4F[p82&:>-0>]H0&$q6iuApEum3M"'bingUBt8H_?D-2EYIeqK3(o@Z`tR%i]]7/[sO/ieoIl?b`;/ic&aOI=6Mg:XIMHJ%m)T^KnJFI.>0:kc(Vc\qZ"<[H?h=&jDH#RHK8??8QR5M$#AlhnY1fNU`j2iJ]S<deCTqGmY6=>R37"!#I=eCMO-u*bfuRdH:aC37tQMY>YaE>DAPo(\"Dq&t%q39t3ris3&diIY&qSo5)UmG57lD3g!^m.p`4'(,'"R>S/n!TQT2uY^/$/UpK27Zt`Zt$Y:QklD`?LmAf"$dM0Zl9hc]mqM-/Br,H4V,OT5gk8tgq,_X/J0a,*Z(gL/ZS9pFE3G0&GST[n-$#(@H-pbhM9q*1G:_;m$kb=""=hV8.=L\?B"r':,?0Mh8f\MWqI<Cs)3NXR6l8-caOo%M9QDrLX>Eb87LO3Ddl`[IB_[`Uek\@F1(I7$thZsCqk*g(bi!WiW'^-&l,O4hg>h'_hg7`Ba(JRWKWcau,m3RdU4rWSqj-=o?]g%QAqB*u,!HX`#hor6;EFtGK+1Ug%$%8D<mab<tSL).MG(;J5gnf?7;A3T'lhg.CKj,93?CCl(!-*q\+\)O'c8t82S^gXB3>M*N:BF)?"W*,=.\Sr_MX.6?YMe/h\:WnNUX5c_dCn86c9XQ7Ca,fQ<`U<fYr7PUf@1k9?jmlKU98VF$aZf"CeTVI47A\jg]-:K`\%]:J,S]&JqM[8-T.jCE-!HW.5PV^N4RW1!]'c0^OJ!/F6oq\NY=$YcsXR('ogHuEurb3,TMgR1eb=3[V_M<2:B09?9BH./]lac^$"7,/90AI^&fiUO1%V<$JXK>(FX$AG-2`1/(WAVJNuPe""C0olf+Yg'=@/ZZ/_tT.Os`PU!>'aTWE;sKB^'Y`<p4F*$K54iIX5tGY?@&mnnF$TJ&"'^W4a0CVU*\M:'(fJ%c[174]ptTGGT"JAk`N6tmH^7Oea)8-.V\(5XEk"Xb-G7uS:9>&Mi9$*.A5W5%)n5O)I5#;4rg]#>*F5rW=1n+YcCb->I,:0?f"#'g0+0@jjR=rduhC)f@]B$$2^<hUQpEb(F)h$c@L9W?P[1?9^,plRr&m8`8[m^h/#[R`Lg%_F]*'Gm,,#K4^9G57dWEr"g`;eE7ND;/L_f>%;Zla4/))C$:9nQr?'Eq0fd+Ai'2Km,kEGMdfj_u.*`Kc9U34F=$pRu?C\kZ5S)Y:RNo2b&=@n,?(=l4XUZ4Ckc/Dl`Eg-Sc]D?,O,i9rIIOAJ6Fd2B<9@o4m9re<paWeM\PrTDnI1/Pr?l46X=/5cE"qp>hDFGknnP'FY0tSK>Z#:ak6@2f>FB"9l=!QEEu_DtiL[6c%PkA@m6!6pN[oqWO\*g/Jd*D1:<je-rO=H[S4Ii<$R;Ma8R&c:jfkpL,!?ee[S)RT<KkkN1g#=V1h7<4FVRMC3,\PTNieO&J[mV_KrPne?`bcDWG$nl)mN:sM0[o7g0+1oml=U*9ebrN9FQ0UR"tLDB%l-lWS-&Tc0jM!"*+1W"YMjX4W:M$gX\4\tZ*Ab-1;(-C$g_!6WZ+>uaI3,aE1fQT/oA[h`KiJ0_WGqNF8W(J-&`[H@q\T?rFYd_D%fHV2>Hta!ODf::UC5\KM%#(UuSH'=;rqsgIn-[r'^]jb>W&QnYi*_4%bA^$giT"[]Y^?WR"f$VE3SPH)kZ]*Un3P);@-"eK1#gT5VXdDi;)"@U'_h7KcTiJ+,%:\G5f*5Vs-W7cj(KKGf-"\4$3FHtb0.t4[r.a0QsrsId&L*H[CD@ebJUnE>Vn;q+m*:jWJB@L+cWhQ\8gP^XK9+WbkV,0gMcu]V)@ZFCg4f2a#kT!qDO:5f3NS`=9l?m`n&!.mTQr/c%#g@`if6l(6AfC,&+#Z7up\*ZHF[OE+'=cDN"joGPLZ9*_Dj$Ph'u'[,fm:1M5Ep1Rh6O'`qF[Qn^rigkW=j^1u/b-*\jS)&X<*M-+-TMdP7-%j83\Ke46?a+!L6fag2!Vlm0g%g%e4XaIu[BY(m(pYC;q*@=?D:P^_#iZ;[0MnfMgK`DqAQ!3g)ZE)KtgF0i#i;o4FWiAP(+"*Y2[e\j6&!cB_o`Q7>JF+OT[W#*EL;sn_d^j*q`kT*%MpBYsdQpYkn\gP77mKUjLf#;igX6^ND"MZ&]]Ij_Y>`_%dt@Eak>.$6nlH>D4ou7LF0^!L,s53Un#YiY^OaD.j2,RcJ0%DjX+os?!VYupEsD&kZ[OZ>F$\8l\)$3<q6[h+`7_1#ds@T,inO+j-P(56R1iq-=4_2)F5e3??fW!+R%EI)dG6R1#7l+HZ$C-:IL098d*UkJ\('![n(P>HJ!Rihjcrm^(%nKfSP_ihp-*_q:6]j3\:BLmgqGE/9eoA,_O't);kJ6el-QZCH")?`)I!-Z(LKLq3*c_8N4//ET3kDo>A*!eM@2$NG2eX,c"].J#/:#:?b_'QT7?jA=gO]Io#ZRn(XATK0X)".1uMo-G$$4#VIsHW;?kU94ae0$*0r6bmZ%os!>fjS6"T.TmdT:QXg&4&:]cPuJQ2nL(cL`F^]46S9hikd"QML1&bZ&W\-)]DHM+3!S23cs^nqZJckS4`T0VcaYJ,]n4XS\r+DdP2o[?SPN"P=n'(/]lTJ2'ZPEjZ'PXakJTL"27jl[G#NIjJ"D$j4/Mk71IlbjHlWX$V.P@E=3H&kI&]`Z"f1#gF'5Q:e>>orp8.WV%f55OL'Z"(ga'G2;7--?@nOT7!HZDnSa_tBCF%cXBVF3\Rk7<P,r3MS4,J#6Do-j,pZ7XhjG:f(+uYKbon2?2FjcpJO/fUOr4ENGgUTgEV>#08JP\=[c\=hjdWDca#bA)4`"[Vmg'^OF()DuFP0]\e\eFflcC`LEZeWM>/Y>.U0C"W:W$IJPUaK<^&g[X\=+@9u-t<G5Pq9$2d6W*X^f9,&/#O#?\>YPES8=P%_>EOZ"NdKjAL'PpXLFj\5!Pk,Uu:ZKJkr;H)V>.J\([V\*Lm+Aj[b>p;[J.G/1*,R[Qc(hKSG:UgpN1/*13?G[A!Qp?6Pas3UGtB8fC4KN5_dTAQg!#@Ao*[4aaE]rbDdd,6K"__R"jk0#_?nV%$f+u,[,!Ee4pIO=Q^85Td-8K[nY/C9/r_[`h10JsGQlF(?bZV$B4TOFW/buVH"4L1iDs4]p65kDT(<6<!d((WQkj!n4Ki<,AiG5JW0%`uJ:QZBZF6l;DfZ^FN,!m)3Uf9&#q5((3B9*a@6@RLZItufI0*J/7R4Si<oJ;sN$*o_NYJ2];$1eFmh=5V7?18W_#kT/b!IZFgtLs_AcC,`Z8-;IRYOsD!:1MWUBKgOLCcEkrQE.kImW%EFeK]oIi-<1=phV-M-9d&e^_(@HY4QRP#9#MB1&/h@,0@04>C;]ib8R@JYf)5&Zoc.JdYs34C1Y9YS4u(n;rbK\"oFCf-<O;lE9c\;@4*bd'f.S7)9%UMk_,3U^1nG>,G8o&@;g(L=$GbhDf4`Bj;FP34MbEZk6R4$[D?=7>pq$l!>!Ef+G^hcJk>4KJnBN1QBnt:sDP.9jn1C1OCggl?VT@RU1M%>MS-nHhM7aCdg6DgXq6%\fF%Q+2\_hRL+,?R@2`$2RB=u?B6I*hun79kgFFde/\\%>gm_&LmY]QORA1f$E#&F\uA#CJR6.(p];D45-dmC\UDX3*jQR\aO-GW(aHbZN3(5CD0F7,?<Z=2MA>ijl0st@oQo%TqET9QR1G/7*l@X%/r0KW9:]_>R,RWSa,:m=C9<r2WM9.!1#GAB6Kf?$5ti-tA))CrXXoq0f3/R4Vs6'EWksIRen=QIRCs@_)d.C_@e%gFJt&E+6YUB>DRc=k@KRET<Rr,_GCI&PH[^H;;,Mi.QZ0bJKEuAJU8C4^kVs2`_i>RRk)^`6Be+F<(A%0WXKT*L[?dG#(VO997X1n1I@5S4pA+Xa6Bqci(TsFrGY$n:J\c#W&MZDZ``l?XpHlkiD$1Z6%,%=NkjlnMLQS,3h'J(%+5N;8L_p1$D.MV$BuE^1B/'2c4Ri':rq*,$<5M3-;+V-#B=DDBFCp]X5#XZQ_h>RkZ)ITT['Yd9hKam['D%T8(HMj7l;ms%3_Cp&SH]mLUXa<\TtlAfADQ#f*%k\TP_;ul4M%TBf#%pg`Z&ilVl/#C2%9lqLX@J#8e<jcXV7RXP<q@_f%/]#f3Ll0G[YR#MS+8+RFur)L/#>)9&&W-$JIph=sbXW]nH,`-&Y:pQsIamf"UXjMd4TE+FtFV>.Mi3)BOgZ+UgpJ37R#?.TBDJ0oK?YQ.j4p0ei[+>AgtTfRO=:&ZGs\Obf:QBo1/l$6';eDYK);,9u6/479>\4/,V@>8H(gS>W>VWhEOWUdq((c;fe%@`:Tq%iSSU"a^%0BAedJqXrn&Pa8l9"KpB+hIKp(ckHn=MdT)XDsOfhbq.WmBGV6q/opMmJd\WH6p52DlDq,^q9jSW(Y>'C_c?FB36/)'QDED)42:F9/+B]KrplDtqXHBs_?H1kXge:A["VhO_j+4/>U63I@L+9bKaZbP^f4OIDI_tj17CUp=W=AK#^*UWhfR=Xn9QL9An5GS>PAlT6<s'GgK;&nnajCQ-buD.Vb`=-nKINgR3!Q`#AJ@^LW9f8JnNi=qT6(MZXLW^(c!`'^T+94.5!6L,8'#ie(A;94$cMnlO).QUs_^4*e*148,,^b$4;Ht$eMgM6B)RgNJa0ho%+ncmt.uSgVF%72)8mo,ZPjB\Jb_r)m)iD?!-pCK[P/sJ\4?3o.nO[3>4dM#X'[T20c5E54o?%[a;'QH/r'7TNkC#1+Q+Z9u<MDD1M,MaMZcIpd.!&-kHZ_>G3ViHcta^`<7j>"dh/Ob#e@ZJ4.a7kS_+Ok/XFM%WF]oXgY\;bVRT-L,n^<>?"*ehR)@cJc5AO0E]M3=Qc56VS9d9:3Nj$6%_AVf[tH:\`_oX+?732I%'(%$Pk&bmVl_IAU(@`!9Pq?qAL+aA.%X65LS5m`XdN;RN=+Q.<&+'FK.+KYda:E!SW5Rc<KYT86g+"5B"=8G%H@`R:YOJL(.p=/<^g-Hrbr]7(BN+rWO6OhRn+pIn#_U>GcM@.`1g`:>,kqOPXutSQZb%g;VIh[#A<4YD5B,Z2TLgO[Qg3gi.Q@H>;`pWOTX(J"lLD".2Jqo[ZQ%XPgBOp:C*XMY\F%F4/''D$LlA%HsC*pft@MAT;]E5;eo?+/TcjFB$fR,IF]EHD>18]RGjb!]bo#nBZU`e!:_OS;9JoUZ)'KVk2&TbY1S_=OFLJNjPJr_piHt-?8J`S_rC4g!\;7'buM/?m,#cH$PqC9p(PhVO9VRX1`)gQj[S=6+$uQeRac]1Ge#!(\(pf)(o`R(IIT'rqbr/qt;m=Z"gj2D@Np'NUr5<G<WILpYL8hNGlQ,-HCURrWm8@;uXT+bVUFl@j8]WE_<p`lVg^2Be4\(!l/[%VTlJ*Q7Z=4Q.6-6ZTuAWGXu4*"*X%TG)CY9>C[jS%I9/$n`nlr"rGdM_'S*3GMX9$GR.9%05rf"-Wmh$k_V^a2S4Ak0G3Ah_?hU(rUr<p.popnRS\7Q":JR<=*N%\(\*mC<8"#XB6&+X?5,R^MOb#<!S5rZ\IW1@<LuEg6l?j[<me.Zfr8_4rqU&<YlatAa#PqrGV]^c-6+Y*Ki+E1*&P5GDRbfNVMrCn<T\!?nGB_="gq&gJrC^3?=3LMiZ&5Bfs5/sO"+X[LH6T5rYY,A*`e:NZtbPMG+fm.&-[65TaG)<ET=g$))*!KrStpo<,.tg7SO2oKN<=CZAB6W,p`ZNJ,b"t8($&i0iC*K"_@uiFSp4SmbPA/H>?qt5Am0^QE6G_'9gKK$0C(OMUp$2hcsJoJTJl_2;YHK,)S:q#ZtA<4)N^DS=D\M1[?bIX>4C8F<o?GAE;8l-n(NIHZi>OYr<"ED8V);MWIT$N2kG18l3%-pu4q\YncX-\6<$ZdH[,3"YOV(7WS)-A)e?RbSO6PTV-g:QR&'W+ag#oRd[lW5/8%Y%?G`l]i@aJrEPa#!20MlpZo#tdC=cWCJhr`X/Jd\RZ,JI.D@X1I&WO,+o"sanIDGWPZ3+j9hHF&>Zt'1+#0,X@)_*#j>:FO`-)Tr3RNK;f;r,u#[lZEqV?AQN?8$cIMdsN#fl"$\!_6R$SE,@SM]\c%mEs40nPoH846]LdFN8&7qZEO6k%'F74KgK55k+0'.6NZ3s#h9D)VnaAr(4C>h7g`Gc#uj@Mhn5Hg^\sLFNCWJ@XqlXGC/oYdPTYaWQfi^1hHpXBN%.Va(,_^O?9pX_g-LWPo(Z3N]g$'L69tg+PaeTKPi*[CLAAC2:_CG(FLJ*MKi[.OjHq^UT,dgi.E4/'D7K(rX2gWStYs-aYEAMQ-D#-,G<8WDh^V^282]P_Q)j_o!Fc^PC?ea26*[E%oL0blUe/S!U<uXNRMQs.)Z>"N!ZPGIV$mAqp#1/hSY)RlPQP.Ar]!SXg-lJRR+A2[*h)YWIRL4aI&)a(oGToNd>hCGRh:1p98>+LHLAhJ]i?g\U;\88nTDb;\>,o_"NPGR`!7^B_n3i.jX=)]J\,9q+?3gsHc^9q3nG&u>%99iR<CG^mS`$#gQfpThspkOCR7>fpgmh`gu-_h/1)eiYs)2^X5$Y9$sSCth&?S5E=q$hD1d"\H(<n>I$/>R5+Ad7\:=QDi>ekQd%L)>a:`0QsX,^L>6IV9>4?^1bp#ZY%Gd=Fbo$qU[YlH)msFJS0L_i;"2ll*LVf)?]=J%#4*6r+.t`]_:(Z4S$R'1M5E`hgP7VF3hO]^t`&)Gf_o%I,!#pKOnc,6^"kX^P;/Gju8>MU.4)fbEGn\s'rEDW^&%TW@ZO-Lr&BN.V_HO5/m+"#qhHX2=YJW<r)IiX5C*SVDONiC(UpZX:'[Z/^5q%IcZl"DM(4s.X:^DGH3U7a7;fc7PDNh3"fY>=bi6Ze^ap7nH09fgpnBRCrl+&-[n:,p&fgDo7+LE`Z&^FY-"k0#.7/1-(B9qS%"EZ-01<mq_+l^0NO'BVZqMVCf=]uq"a)[4Rg.VaciWmHKoH'Ld67Dl&7FDRQC,!LE:d&b:hJ"-Dh<EM&2%o[0p`1-P.o#Lr1J#&3[ZJY3Tc5f=I3eFB5^C?gTW^iLt/k*(G24!urVeW$(HBkMEn!d5Og%^tK_M7*,[Vl^`7V'=*<N_`/J=C(87o(9Ss#?rn4O72AVP;9L$gE]9uZGj)Bc/J5rTU/P=F6&3*RpB.iac],CoQA7A=q[=eK4cp#@.Z*K#-5<a;1U7d<$kJMR8KEC,HA#)7*0-Xp!>e_E9K69Z,?n@OD1@"RGm,dMmIhNN#9r;94SlP,bgE@dUPsWV%bcWE0>)%).:\f?k8>m8^doUIW]W##WCTR"@FAqE$RP%04.hMOCot]q>RfmR7RnJ*D4e;G/O%0-/G_fP."V1`S2ZLuCX`Ar'<$&f$Y7YA'.50M(pX%kbTS-EkI,]B,Ml>/pSQKt,R^FW#H?X*2)B_a%1KE;]70^)o]B#3Y.[_!r(-<NEH*H"Kb14c?hgG-pe5U-FU[>$[-k=F>D3IQ/>[=E>`2]U!7c;pLRGupl)%kV[Iop8\u$$fDU$!I#Zf6Ebh:R%H0^D$>S7-N'Sb14#Z2@PlRn3OYTKtsNA*H\?<UPq%#9IWeLMK7q>#`S)qPB<K_r9ci64"'F@'9fIq,[rrh&5ENH&NCagRHTm?0hLj.[A>?HdD8^?nD<Gi:tk0/m=VUkJUt,*kst*S]WPm425=\DiAr2K.I=nt]2C=j+"pL'7$Sgrdd7V58gO$/(\/87)7V8,2'ApYToMn#X*`16Pds==nD(B#aJ$Fs5VWFK,t^.CEASUVhfM^<\e=8@>P&^A@*t(.JN6iu7r/*RstB=4=$@"#D)VZhHKCN*<Z+3?A:]U-Q;O!%]e<]U/r429#AG02XO:_lR6b\B)b4g9aH/-:u-?L&oKb>^3qODF8].<>I?#UhsW=H1Z,\MprIA,:lOb>H.5P?7^%]ia.dR;<ma_`[I<3r-j-Z6HthG7.*R[L3smY)r[NrO^BrM:=/%6Z_gEa_RKR7r>hO;giCSTCY#T+F]PaUE.ZGj6D7D[<h4U*Go0-$[.D8d&Tfh7q8<^W>De>3C$^%0]5Zl@eqbIo_B/SmUH\,R6uM+&#=hfoC9@@W^@q"tEe=`]!26GD!CkMddg$9u:o[Db8P,mIK@CUm[@W"k?!LY4UEBF?c\q-<6GLIKET>Qaj%7lY5$qK:G4*.sK;VE=nP`48gq7o*?!ROqY0N>`Y5o0CDVb$U_iG9HFp\3Q4fK)0$l;W6ZNlI9HqGORRr?$/=_(ZuUr6/9E+/=P)/I@h4pV\p4J%5r'tmaQ=WgifgHI$0kF?URZ:E-O/_Zu'bYLr-ccO9J0VILF'8MShrPQf2]DRC&DO`leJ_>]AU*i^+Gj;9G!=X7d/CSce1>h.&(YmQB06L<o:S0gq?6k_:+fW^7/m"rnhHpWli6`QeED=DUAAP;g>f_+k!*fdW7CGFSkRAIh9E@U4[WZ)7E>o;Cmfj\dRM=cO!/9P5?4uA;6Ol$&>LM$ZVF-L<9H`*UY.Mt?GLJhe-E0(*.je/RUD[6p%"eB%N#6f1S"&0kg'&jc`'K"[#n`0DeKCGg5[4X!'9!L[_WG;n].iu5%:"/&a3&ou`)R2PSNF`2P,)tenK+ZGn^ra3Y&'K_oih6L"YS`-:S/WA?ZFuBZEU#s.1cc9Tg<c#&sl^aH@5so&NS*OBO(HQEsNf#G/_67ZMVP]%(%L/r*X2R'1o9ee/!-gE8_KE+>11.?DA?hJ^<;L_[23_T3o[Y?T`n+=;IdRZ#$M=NJ?@VN=e9W9'?ZKW=ud>DWJmd_@]F3&htXXSDmdt6,kuL(mOgM`5)VUfpr6ulK]CqZML6hdtCVXAq_ZfR_<-=+*SWoW4$P\>%jVl5B,Q=lKVPZ63/Y`<_-s5C)pQ8C@cF1KFn.4UJDSUi,H2^6oulp*=/H=3?]<%9=BN82"1OR#X'N40U(KB#9T=nb9<95cA[j[lG5G00<a<Q':-"JG]QIAY11nJ!eZ_;QVO2n&_Am0UZ-7?JZ_c50W_?<Wi`'P)]D[sYg.78?):MM:k9fn^`3B@dHL(]T^a'd#i":R6hofm%eo6Zl;<5gL$A@O$6&:A(FVJ<_&2-oq"<7rR'[5/>[XiU+JQ;0+;&6I&2*IdLU^5>GYp7g:X:"FIUZSg"-\YK,df^bFocQuYsLQiN<Ea0F1AFf2k1c5W<8I0E_1ZOeRdI^GWM'<PGe7+iEkJ>RqOu'O1p^F`jKc;+O&O%9C(2J^iRgbf8?p",Y:T6EObM"=JOHpYW^rsO5PE9@U<Ai-\72@.p)8kg:UhCkWlWhN"P%&Wa-G["T\kEdD>]+2)RN]^Mq2OHh]mLN%p8/eTEcJ8a=0Gl73\rb&Mn,5n%k"[lM.2]q>HBDrSCmQu+h<1B=QLhL"]cSN=0SA0Bf">$=BfMD9f70'"A$a[]bp2[I'(<lrJ+Tk<esTgMVI487^6WZ&I*MXQ$*#(s!um&lN$6O:%,*bGG;D&r[!:Zb8E8lLa<8P7HpriP!6-_K]\DV*U#BF`-J0Le)70!$QK"+`^C?,PC(?U",/6Y",p))uSS<`U;m%Xu-TZ;\u+'o)BI0Yp!k)R_u_:S8(T6kg=LS9jJ"^]*nEf!!uXf]]f1MWmu(jB;p_m7AE%$Pr[!r:@s(QnW7&U^Q.&il&qP'Ugk-guJe%0BAhP8Xa%24i-*+N7Xs)-Vp<hG3o"/A&mGMhgJ>6Nc$!`Sr>uYPlS/`oo2%WmXFK([iTqd_94ICr7"Z#KC2T=fWTMfCs\)*l9Y>J0_)t,huHPeC!%2(G^r#BSXitae0"%nZd1i4WiN:k5QC9(X$6bn!cZQp"h&(ePm^1N7M-pnH)RPcf\@GhZ%Y2\^br(hGYPH;A&h=Bm>f#gK#',HGYLn<7E+5f>IU_;ZhDX&hd3\RfgUQ0/qg%IJcuNgIei+"hYTAs<`ZZpaH9u?[C(Iu+&?4$Td1OJXH4^,#EkSlQ.3o)d`JG_gER1@at/dl'm5@(\<,4NW?WP%NB4X6iZOr=6MXh_Q\^;&8Wk@<7M7=!8Wk>j;V.cF&'+95(B~>
   27.30 +endstream
   27.31 +endobj
   27.32 +7 0 obj
   27.33 +<< /Type /Page
   27.34 +/Parent 1 0 R
   27.35 +/MediaBox [ 0 0 595 842 ]
   27.36 +/Resources 3 0 R
   27.37 +/Contents 5 0 R
   27.38 +>>
   27.39 +endobj
   27.40 +8 0 obj
   27.41 +<< /Length 430 /Filter [ /ASCII85Decode /FlateDecode ]
   27.42 + >>
   27.43 +stream
   27.44 +Gas1Zd8%P4'RfFOgc$sMdbCqlq?SPWL'34L`/"qF%+;XG*J<gi@mDt0VTF/Q8^BQ_SrFpL(^n-015OM7_1j'dG7^REb&G]iTY_P%Ed6t'^6V)p@b3Mp.pIC=YG'9bkfCG;lKdeO=4teuYq7!m#18'%5s3>\,^=i=?rnQP=;ArG,QY0n6*@2O+_W4%$,_KSbO")_AaMagg;31GANlS)a4s2GRqPNUjG+mF;8ts/30e/BCUY]a89-JQj9Tr5Gp9"E(BrcO?d5P(hbc^t;Z;kOj.aYkWOnRgd*$9#=&:qrbc$/e<6&RP4VN9)7)`iT*DoY97.WSlG&:#d@B3F9f'^"]\Nejj?]'BAgrI.c(5].8=7sVOdpcgnptZ#[3i'LUq\%SADs"[=4_&,/*hM[gru<c]CWTD!i,L`K/?&U-5B?Rs7f~>
   27.45 +endstream
   27.46 +endobj
   27.47 +9 0 obj
   27.48 +<< /Type /Page
   27.49 +/Parent 1 0 R
   27.50 +/MediaBox [ 0 0 595 842 ]
   27.51 +/Resources 3 0 R
   27.52 +/Contents 8 0 R
   27.53 +>>
   27.54 +endobj
   27.55 +10 0 obj
   27.56 +<< /Length 2747 /Filter [ /ASCII85Decode /FlateDecode ]
   27.57 + >>
   27.58 +stream
   27.59 +GauHNh/D(,&qBX_Tf5&`QPN.i++9':9@!hZmD[(NJSlQ?/_AQR.rP"<VZ6US,YB%s-.R?5%L*QjLbg%M3+&KTp@7/),PN$"^A#.Pn@dumlM9p>4V<i2@9u>h&e!Y]*kYj`*faGWBU8,Y/8]E-f'g6\Mdd73g.Cm@>dd@5j@u(E_#;N)r!1G4kqpXE"V3l-+51cUIc%+u9E"8Ji\MGE_o8goJ\Z0/alaq0Km3$>?mHBD`$meU;uGT;>l=3QG$#]Clq+UF0_@r$kW((H`rG]'+5*aA*stW7&5B>G-aO2A;LA*_+UjG<=\OF/&$t0>_3LOHdNBk\$#Eb'f%bX0%S2aN[a7lPLMi(jmO*[C4;?Z&Q3Y.K3Mof?=#Ou@fp4N$>i?uqcEOZu#-$mL3YQs=fr\/egWX#R]XJd$dI^!o1p0F8WKF#ml(X_3<$ON1+?kk"&0-dmkk)kNiHNR$)6R[`Zt=j)f7mHBrV=?$1fC`"PPY1:O8!2=haMH!&LTk$f[#X8ho]-a?'qe*ZNpXGE#_UtB.f^+Gltjs=H\UYi.-+amKVM_[>+A0V3/(\Vb2j^n(#f0r.+8rf$P\RXMa(S,^r(nX/8M4H@S_&,kGp*>,:jEo/_$6(XaLegDUZN7'.S$Ct?&LBoAa&/%R=g^:SR\PkI6:1iJn<b0hL2S5H2E>TrEY(Cq-JfSJKm[X$72h68.O0nm@sKc@U@r[]&?0+[f<lU(>Z4+W64P<h$[9\?6?oY]M`qqDi-?ibo>OP!ILT(K1iC&''Y)oo0g^>JZ+-d0M8q>ZCDh<(oY(*KJ9W9U7^gS.91J[K6"@%Y'_c@Wc;gr@toFD`%QF>9.F4AY*S&j81=C0tP/nr+'48IZlZb'MO?!<Tf/:o*SO!'2d>#-km`/>n_"oEJV51*T3:[u?0O>/u4nRD$22S$^tIeS_MK6.1S!,PiEe43eWZY#4l53NBNno(USLJd>sNF(&/T(08O%,VQ]"eQO@t7M>+p^L:h4$^lKUq9lM/SQ1K6[U0Sc4N[^e_kl)UV]Ot'-4YTSmf$^a%ORPU;XPmf*V+4[.?Nkd!6]jRU^!kR9V$SC)AVJn]:h=f6K:8,cN46m!$K>UWQ7#Xb[/HiF":>`W#2$0C\VM'oQHoqJt?JZEtK&Kj6=*<Jn69"13GRt.a-:M'blcAarMN&B2F\d9<^\Q6hm(@@`\U$Kl!2m%1=)06uYZcRpe%=K,@emU&kg8*%4:EgqHtoU[*'KoFX_#^rW2+WmYLeN:>[[/BMgbL:1`7s$]VuF8$d"FA#AuZr#)Ng`WYa"!_:5"HJeO]6q!^dBr^="ul;Jn-#7LkB[8sH"dc->C)44!*d<ITss6G"?7$c;J>I"*\9Mq_-rX>!fgX0\IO4&"pFMf\H)ZNf5mglXd8n<Y$itDl^NkJGGHh1:UUGtLY8I#"@[N$4kCRU9Fo)Cg@`*KU4*Ia=T7VRE@fg)QYf`p@'C=WX?/G7kms(R&&KFZBiF3>W\[I;BZ+cH%Y6la+p&>Z1FSo?)@=@0BHRdS"H+I'&DJ=;ktm/1W_[`DN]Cd\-h+nhRuT#s[sW4Nl)P.,#,]$/T^N\Mkk.GR`_2a02cGGi*4?Dgk.l+VO!mnqed"KBYG5-j#;ODqLNB/ak%#(,(-<T#>V,+,Q(l&`Vc'N<?g>&r+5nSDI"a/]]oqHEXNW"6.[,D;;G[DW:Xfd_hbtF4\rrOAR+>QESqq=BOTi4E>T&TtqV)?PPL8W*h:R\cAh9-g4kp>'=1cYggiXWZAb3t.>Jm^!iUIkKf$7`LaFhiM%R#*!@HGd<Ft.\O1Ybp>p?K0...$[`CJGX'\@S@EH0ptAbCtqP+[0@:#MECgE*r7(P5DmKeF'V0r"Xgu6;2*bpM&IE430<I<S#r!*HN6j"%1Zh9bg2bW=DI?MaU<l*NQ5=6qnb1])N6>0B42U-VPXZ/O80SgIE7^^pJXJ?0:-78_eMYQG?^#ls2^[=*QHkBM@Bd18`aJZu$$&_PjZ)C"6H-Eq%K83N]=Z'b/u-0dnH))Qm<-hUL$9R:^ha0C^%ZF:>"&`n%P"EPM)aW0"4u#>8bo"NWi1lHC'2J^Ih?cU@gC+aoA!N=cE;EW!7S^7,u%0U&3XB,\[@"o!]jZ!M"rfUSr-bDOqjoG,)KSA='q+0W]o7q[bZB%>:o3\+r<\R8ON=hma"johc4(@(e1JnV^u8";ah+hFkP6aM?R\rpN_F;XUnXd6mtr:)0Y<!_7X@jO(S8p07VAgREXn.d!C>P;p570KqQ/qFrIdjauk5u+N_A9P&G2jnE]oo@eqV(F9IRb4j+&O5fLnT2K6%e\B!4&onE(8oO@'?QK,Wh_f#/^$[0Y0)KIU=H6bk/C;_%;YUa,oAX$mAT9NI86Fb$Ec0(U?>D`HL6UUD\J-D8EN@U:1Eh0LVCkJg*)X(fMt#n\h@>I8%K=3iCdIW$V/lHAhaY[ag11X6lAG<WDeqWU\S7""GKT0%[aBb/-c^%)HVKhK.ER%]4I?_YkTa<*Su[aheT%Mfee::"/epe"$-5P5ab*fHu+fe8.Vf2PD@(hYno)<B];Gm(eo^N*A26t%b1BK`_XgP(V<aG5(UrYOgr_TbJ-G*]fL09DW&)jRReD\459Vr7&W%p:8:V/US1Hbet"i-)h13DW*4"6"\l9!@0M1-*NRfU>T-@SH>9$eQ1[jYUH:]b^/rX+OZjZ&"*mZN[5!`l8&%4^KJ2'I<LY3PrmfsfX5`.d&j@%bnalWRG7VXYp4!++Ga5u~>
   27.60 +endstream
   27.61 +endobj
   27.62 +11 0 obj
   27.63 +<< /Type /Page
   27.64 +/Parent 1 0 R
   27.65 +/MediaBox [ 0 0 595 842 ]
   27.66 +/Resources 3 0 R
   27.67 +/Contents 10 0 R
   27.68 +>>
   27.69 +endobj
   27.70 +12 0 obj
   27.71 +<< /Length 3758 /Filter [ /ASCII85Decode /FlateDecode ]
   27.72 + >>
   27.73 +stream
   27.74 +Gau0FlYkP!&c_:66HBs)J@4A(neq(G88q:m_X60u^*5hu&%"_b`)38GLLL5W*HGLC/!6!uB_'!.+#uEh]<h*4I_<:;pF09bc@X$kjs!ni?LUA`s7mk^R,E@SaFjkf0(-0+Ip[WVPe4D_V2,a"7UZuaY1<cQpN?L8QOO+"or]m8Vo")`S]CZ\j?%k,[bU;OE`I;W1?+nlbJ#L-ZH^"e5+u#R?L@<hJLCJ#h#/8US_8:X3JSd^Ng*:bqtms2=f1DE.1Vce.*qil_&h:"Ubl\'S/mm%N!#pOibD]dQ?j7_R78_-L."Fg)obU_qGn@@HeqdZMK2g[fOqmIHT`u]b!9@%^etn!"ZAeV<X5R0@8")c9BAJ61(5S*p.O.k@kF-qf28X)Xip23YiiaYIGXafZp&E_2>q9oMA&U+9jW"^[d^,s_+[m80t$XYNdoTN)<Lo7qeD[;OX$\=AGiR=a8oZJEPoHZooY0]N+HW!c]quhT$+Q0A$1ahfhpSK^-KQeQNuiLR1Kass+nA_Pi=B$R>-VZ&PQB/&L&,%e]`(S5NPg*i1;@]Ni!^S]\O*F^FD0RmQ[^l8o+<oQXe:h6,l#-K$Dc<i6j_u>BF02NE5C.Xm0Na^#CR3;n_`RN/LLaFrWKNn?K-G#>Q]obCYZldm:cD:<6H20ZnFWRZa^k_]8(2Q[;Q9h2E#2i(0"VU=rZ@@gR$hWE4K$D=WL>0Vuu^8&SK#T'L^3ZTc4AX`>LsIe@kk\)trq.ibG3#somT[kEcchQ4HQg\o@GH'3ai&9O9lD]XgSB$N^G/;b_7b$VN446acG*:f=q`IL\O71c[KoB*GfDe?A=6F)D;Pbmg2KKumOAP>+uFgFq8`fd/^cS_8!iQ_Dqq[pu4"[&VZT%e!t2aYkbb)GmVY0n&sK,mg%f?q3Vl`",_)4HLiZr"J07g/"a%R%-+V^bg(hV@qS_E);F),FlbZOG;;SWJU3h*"UV_U6:V,M[Lekg;@M7(WcNa>">sOEShR6&lhVqNPB`)3e>Th7[I8'QBc\(d.T`*=O\=Qj%s(eMo9V6o*!LZ@2e+.3"0<KEP(hS@HOs'=WL4ig-rHg^QkImDLT)Yd`c2]Ftg0?p%*H/<(>#2oWM15uKdcNhS4<.2R(/lNdC=N3\g^<fWUNU_[oCi/i1)hVlJn4,!HT15>?@#jHPp9GS]L$'-:JLrHSN7,JY!74'ajAO:[H7mZ\r:ES[qn2.coeF@Q93aU9/3!pbo6jig'ZomJZF?UV^S0d=2?i-RL[6t=4nQ\>9pA[fL#6XQ[giQp^PJ]M4qU*JYTYs`3N"PrB2\7Cp3-<g7+5VP4%s@1nYM]9QkIIn@BIP]$5sDCsjuKF&&8;[)h"F`L/iP*C^mTN>OG0lD:!DYi`9r1keH,rE0D<ZNEWppgKPoMl_Mj+,/`rpJ>-PC"0s_P5#Ne'721RaQQ?6](_naaL6->(H08.IS[BKG8KZo>]4iirnERC)j7R!$I$l&f6qmf:?,R/e'k"isR-o(LV@qBJD^nMdN/N?Y57H`,FS;&pNl!\.Mb)m#@8T,rS][Ni8%*j:(CF&Rn$u8lePZWK:DG-?mclapTpEai_QrPFK@j%1YJ70-R?UDsLFA-t;bgHLF/.IH\dU:WW%A:toO=u:0#t<+e%cEhQ<NdP+fk)[bOA>dtH,6`!rWq*h`X)"djUN"3-Z+iq7.EAdTRDTa6b!Mt2g'nTHqXs$WL*2tb'=u)BG]j;DV!I)`oPGCY#kS+ON)>?Ed.'P!XdX,%HT923.j>N(Q7EqE=Dj;f>Rn1\;W5b_\*^iHo1TuSh56-38dN9Ge!PAioAk]fFrlG3s#M#c57[(!t;;%W+uXXTrq3!SA?:9O@Y;nNt5W1T_XYpi8#ae1!,g9j+.r]1X9KA7K9I$K'l5L^qYU!bK[10^j&q4R&I?E>UYWo.sJrOjOLjd6cfu94"s"KWYKctY5I5ZoF)0.bcVqr6I<=g;deUIg6F>12K)PM/qtF)aU6Se/Q@"U;EZo1].WrXAhFd4?S,b9[tPUkS%9@8K?8^=h^&s>b'/.(d[E^>d+](<$#S[U*N7k3b#UG-/8@PJ!?1(!`eq;UkVM7L%"43lRg3`/Yk-\0i&@!K.^KdkpW)*nN$QTjWYqF`g(.\qCpSJ52GO_W`-p$L$eu$.WPgdrE^SR<httS0\$g9EIONallTRnr%+tqW_1/t(RV83j1l!O3=D3V5J;9l$b8o!G?.aZjLL)ZmTc+UQ$"bQ1U)$?Tade'6hQ=td]o1=Ih7MR^4S%pMh7%OUQ"O4.8AR@lg;3`>P5t%)/u'Bs<#L4]NBiB#)O7'-$Y8c<\k%&7HV3QuM?@t4j-l^Wr:J)ulCQFR]@/nT#Mg*7-MRAINOMBt/70Y7P@d88K:Qj%*LJNSQi_?YDod7E'S>%T5&hRDHM6Q1Gs`WJL2m_^HNU9eP)lC1[jiJD'8L'tDn#FteNTO!!>Ft!ED5!Je9,F]FFeh:M8X^Z9Z!6>hK7nc7jpm&]"j1i(MF0&q6Qs^%Bi'Uc)+Tc4[R@ueO\342<XD%8uSR<T0O(fB!-'dNFo?XB?*H@g>1AaUM&Y`qmI>.L2Lk5(q,=iq,Du7#f._&84r_hZD8j1l3KnRd.bhg@Hk#jF4D2=9:=0^eOhoP0>b7)j750mFG:e%2kJh=?P)H'&*)$WpK)D_&!iVP_OHCbH7&K%6\0$Dnl_4ic[nO0qmWa]H2`Rc`A\s374a=&B0q7VI,j.*_1ZAN@khmTOC,;PB648UV4h=<rB1I=oVpDS"$J@4*>TJul12LuYNjrDBM;4QiT<nN!]Z?'<.29.!&1fdX%p>8s6,lb$D9/7g(@+,$dE.j'.>UrFE0cc&n4@\]tIQtqUp3c"Y9mpL1<IlF7np^l7t$EhHN1GJd`RM?!pA_fo%BaObi/kCQlc<+jVkr9HEr.F350;h9>_ek^N"4Wp_4,4Kn%!L]e&6Dh,5Uf&K3l=9diN2RbjURe9`[69UdV<6WJHRY8/(c_^T$Q2<E#6.;"5;hh81_>+K%O:T#eO+gH1\#(^\aR8VQ!oB*.cc@Za'pZOg`.F!g7g-EVI8]5i)_ZD&*0RJh"4MrlOM1n+1qj.$TJ(ihH&qSo_,!ZPWKo-39C'5ncY[dB=dGkJ:UNNOoRf0NQ7.3&j!ek/&AiHiD,8$90(h`1*oDl0@GDd-nZ=V__A/dP4Z7lB?_-GKB`+;J?@/l_bWF89rq,emKTs>"[-'nLbVPJBUS-SWFDSn@L<)>_@VFHQ+\3j,%[5XC/hKIY,^Nkp%Mli''#>h],5eK+#8A?oC(@'D%RMFeVXkKOj1&ZV^_I:A<aA2W9@%';Bt`a*%;"o:0EDePjq@lQ=@5EB:TL7O#'NLdR`U!YJY7VqM>>Cr"&b*_a;JD6!b@D(qW!M7R04SW.g%&0WUNC(&Q/g\0Ett:e<9O+#`G2YO"`B3WJiXM,6SLKCCH8!m"spC"qpK^Qk",q9;ta<DtJ;lOfG1O"&s5]1gM](W*7nC7Yb[#RBhW"Aq=5GF)GXYo$=q@46OebR1J*d(.kO[3KZ"]/F[9DW]S&%9:dD=FZ9diI+BTAfJF@0+d40+Mg;[3D(jaohX\W][:tW@MtA\XKRn[X[Q2c!f3<U_=8C'A'7Q98TmfW]o/\XZDPO]iei.E^^3uXqO.dm8^a10%8G2gbh.)/-]'KtkQJ9:^ro;nX8;mtMX]M95lY&u,!V]IQ%h?*\+&K^hl$i1'=k]4V^gm!_dc"QNH6+*ddkZa]*hb.E_jA]YBXe!L.ct==~>
   27.75 +endstream
   27.76 +endobj
   27.77 +13 0 obj
   27.78 +<< /Type /Page
   27.79 +/Parent 1 0 R
   27.80 +/MediaBox [ 0 0 595 842 ]
   27.81 +/Resources 3 0 R
   27.82 +/Contents 12 0 R
   27.83 +>>
   27.84 +endobj
   27.85 +14 0 obj
   27.86 +<< /Length 2533 /Filter [ /ASCII85Decode /FlateDecode ]
   27.87 + >>
   27.88 +stream
   27.89 +GauGbfl#h>o%dZH2qu-P9sWG7p2),i#IEo/Z=@ea+NaeAC>"8c-p3IA^Y`e3>HDRsP_B8B5F>_e*F3lS5!<3-h2@9U0mE5r^N)9Z=5LrN5Au`2bkJbE`$=F9Gk4mPrj[6PS+a$lhJO>$$cYOTMTnk(SbIF'P08R]GLf9r/Jm7eD)r#iXT_7BMg4'BWV+Lpq:L+gTY@.5i09PCdd7r5Hdh(Z?d'deM/l/A<_6#Z>9:;?5-Fc80Cga?D-h'tnS$D+c(CK'Iiajr$g*&H,P]258h]buC*;R`9EZOFN&IST<gRB$m3JIb#>:6Ec!*fdB`XNrTWUke<iOj/H$WIme<%Ws?s_r#Se`"g.sh3n?Omk?\^!ChorBCp>?Z08#-R':0=.f_Ceq(Xj2:moKi24%(G<5n9mohsC=4`<.g-nU12HFjJ",QF::>##5fGG^$iiFdRP7^;`BjC6#)uAihY5JT.#-uEh;.[^(Q8B0/h'+&-N5?N,3C5oGY+M2b5Qi(.g#aeQIi?,qteujPs4W_ZgfBdhkU)oQc7mu@1Y0]nqk,:,l">k%s?@C;@^^qd'a&)BsTl6(Gk&3o7VS.\>P2`4GY%F=N"P4:LlXcYSDNKc1^;@0q!%Rh,&Mi=dH8'3a33FkK+]XHV<$5UW=(G\*ekgB*++@JH/%q`)n?*,1s)kmhpgAN#k+_Ae?`1dSZjt/$)[3'm`'r8)O&;pW2)F*"'[ij0>_ec3f9E?t0s^m1:HGKcA$4DUTj8=M#-7LSi@I^;LZarqPuijqqM8[LFYN+)TCS=t@>'I1?9eiQ:Mp>b`$j=SH0(KF6M%Gu6nbn*@lK4FnTZ`("1u"'gL3oDEh?E$#8Yf7S7>F-/#HLH(U+0?$9S44J,:J&dG_ag3"82Tth?]oHN3S?Ds35_,je^LQHZ?.T<3ktC[E0dFu3RuE.Ws'b^OBU/*)/?W`P\a.i@`JPg%PrWY#8#[bUO_:aDfS`,4ZmaH&C8u?C5ND!MC%El/iQPTD@[dkP#,b0Rp\Y2I6g_tBH$aVbm6=;-Q`_mdEL2*'h;3T_ZAKU*0YYm`(rp@AX:Vn#EslL.$D)d"53(TY6U33o8O=3FI,:Xi$i6-(W7K4TrYNF.m?d4<47SL]#U&fY(Bt)[ESa_j!"+EJ753:e&:,m`I#EdOQ599+ZEa"r/Co\C,Y%3U'O.%n5bT0uktV_&K*$sq*&U+)9,+c]a0?@EYDJespi_WZ[>WEeoT53G6$mFKBu>;/8^1R@ehI'Yr;FjOW36]jBDpJAckM$Jk#L.nI:d]h83lCDo!-GE:&mTD6b9kUq_4%oYBlhegk\mf1C515,4mH.%[<WtOj7BpREm&QKh#"JAlWX)->l.TDZ]34FTP\D1.&_s7A9LO@A^AB)pFim-OUN*/!kVr$lbV8F\U]X!RMZJ`NFa_RI6GT\STYF\7!FT8I=E:8rujp$jM"f'7H.3Em7H*Y[%f<)XECb$-!^o`RS\7s+2t3jB/bGc!OIfe^"kCg3:&RUgd=9oZ!so-A]A'5]9PH*d_`4fi]0RfXiBL\1j;qAsAa@FP;dT)fDgXB*4B2[^18,[!Pf>=#^G.Fl1;hSKkVmRP6%.g.%UV@Y_c1SLG-(Q!=InP$FHV0TL2k)S'N`Q5H5Z!2p5&R[:g3#P0(97+]%CQnM]E%Z)%JY]fpDADof_PtY#sa#Xj$I?7XX8IK/,ok<'*'ej4,2Psd\WqBb0USRjLg#o?@i,0P2ZXZH_lc()&-9bSgU%8g@kcBDI"[`M$Jdaqsc6c'nBKON%R*6-$BpH1$\(.naOggkb"V"Z9?#n:tm)u!8N/ifBKuO.r"6[ii&!MqC3Pf]Ach.%MpF+&75!KU``PZtdo&/m#eIW-lPR*.[gcg\:(?elZ28l;dnMK!S78r5'5$-rn1p9).2G5i"\NjN]<Rh'LUE&QRZI,/ABJrN/Zk&jQ9[RVB0dUH<f9GV(qC77lLA%+DV8Qa@qY>TZ;4qF21rg4KkN*]d+i/ET5Y-/[0="\al^36b?r;nk00*%B:056eFh1Xt?J;fOZ>J''MNA@`5FgIVEOEs/.q;=T`mm8&mMe9lXR)5M"Q8SR5r-#`R_8Vgj2;F@='$,N24)lVI`,B.>l"s0")Z]7#=3tg?:j+\V1:Ba:-dY!k0);?a34p$b4H]Y83_)s@cK%/KJ-f%8H/#r@A`?@9N?]Y)eJOp!GBf*o*h40OA/g&qEIRMJ2D=og#iNiON!K"c)9S(\eDG+Upj45Z\/\_op7<B+$Zkb6p`R>fiPhKm$Xt(SAUR57ioX\H`9Y`=4GPV,hfjSs*gl9C5'-IIbM3/Ci9dCT:`:c\cP>-bUX$OLZ;,6WkFa8U*sOqFE1N0Fn_a)2P9<9Ip[V%5<t!nfWm@[$a:&4J=?g!np*>--/%Xo4*`HPfg"UEf#<ki"$7+[1s1kdcc#EAQNb+WCGXjmi_thd,ht8"hR/ZE6%f"lr*R[e.Is2o;]Vjhd:Do()J]C:ZB&f_3RabdU@%/'4Noq;4W\-_;u/UJinfNFlNpNg5CWM#LogP>.6Dcl~>
   27.90 +endstream
   27.91 +endobj
   27.92 +15 0 obj
   27.93 +<< /Type /Page
   27.94 +/Parent 1 0 R
   27.95 +/MediaBox [ 0 0 595 842 ]
   27.96 +/Resources 3 0 R
   27.97 +/Contents 14 0 R
   27.98 +>>
   27.99 +endobj
  27.100 +18 0 obj
  27.101 +<<
  27.102 + /Title (\376\377\0\61\0\56\0\240\0\40\0\117\0\142\0\152\0\145\0\164)
  27.103 + /Parent 16 0 R
  27.104 + /Next 20 0 R
  27.105 + /A 17 0 R
  27.106 +>> endobj
  27.107 +20 0 obj
  27.108 +<<
  27.109 + /Title (\376\377\0\62\0\56\0\240\0\40\0\102\0\145\0\163\0\157\0\151\0\156\0\163)
  27.110 + /Parent 16 0 R
  27.111 + /First 22 0 R
  27.112 + /Last 28 0 R
  27.113 + /Prev 18 0 R
  27.114 + /Count -4
  27.115 + /A 19 0 R
  27.116 +>> endobj
  27.117 +22 0 obj
  27.118 +<<
  27.119 + /Title (\376\377\0\62\0\56\0\61\0\56\0\240\0\40\0\101\0\160\0\145\0\162\0\347\0\165\0\40\0\147\0\351\0\156\0\351\0\162\0\141\0\154)
  27.120 + /Parent 20 0 R
  27.121 + /Next 24 0 R
  27.122 + /A 21 0 R
  27.123 +>> endobj
  27.124 +24 0 obj
  27.125 +<<
  27.126 + /Title (\376\377\0\62\0\56\0\62\0\56\0\240\0\40\0\111\0\156\0\144\0\151\0\143\0\141\0\164\0\151\0\157\0\156\0\40\0\144\0\145\0\40\0\154\0\141\0\40\0\146\0\145\0\165\0\151\0\154\0\154\0\145\0\40\0\144\0\145\0\40\0\163\0\164\0\171\0\154\0\145\0\40\0\340\0\40\0\165\0\164\0\151\0\154\0\151\0\163\0\145\0\162)
  27.127 + /Parent 20 0 R
  27.128 + /Prev 22 0 R
  27.129 + /Next 26 0 R
  27.130 + /A 23 0 R
  27.131 +>> endobj
  27.132 +26 0 obj
  27.133 +<<
  27.134 + /Title (\376\377\0\62\0\56\0\63\0\56\0\240\0\40\0\111\0\156\0\144\0\151\0\143\0\141\0\164\0\151\0\157\0\156\0\40\0\144\0\165\0\40\0\146\0\157\0\162\0\155\0\141\0\164\0\40\0\144\0\145\0\40\0\163\0\157\0\162\0\164\0\151\0\145)
  27.135 + /Parent 20 0 R
  27.136 + /Prev 24 0 R
  27.137 + /Next 28 0 R
  27.138 + /A 25 0 R
  27.139 +>> endobj
  27.140 +28 0 obj
  27.141 +<<
  27.142 + /Title (\376\377\0\62\0\56\0\64\0\56\0\240\0\40\0\125\0\164\0\151\0\154\0\151\0\164\0\141\0\151\0\162\0\145\0\40\0\145\0\156\0\40\0\154\0\151\0\147\0\156\0\145\0\40\0\144\0\145\0\40\0\143\0\157\0\155\0\155\0\141\0\156\0\144\0\145)
  27.143 + /Parent 20 0 R
  27.144 + /Prev 26 0 R
  27.145 + /A 27 0 R
  27.146 +>> endobj
  27.147 +29 0 obj
  27.148 +<< /Type /Font
  27.149 +/Subtype /Type1
  27.150 +/Name /F3
  27.151 +/BaseFont /Helvetica-Bold
  27.152 +/Encoding /WinAnsiEncoding >>
  27.153 +endobj
  27.154 +30 0 obj
  27.155 +<< /Type /Font
  27.156 +/Subtype /Type1
  27.157 +/Name /F1
  27.158 +/BaseFont /Helvetica
  27.159 +/Encoding /WinAnsiEncoding >>
  27.160 +endobj
  27.161 +31 0 obj
  27.162 +<< /Type /Font
  27.163 +/Subtype /Type1
  27.164 +/Name /F9
  27.165 +/BaseFont /Courier
  27.166 +/Encoding /WinAnsiEncoding >>
  27.167 +endobj
  27.168 +32 0 obj
  27.169 +<< /Type /Font
  27.170 +/Subtype /Type1
  27.171 +/Name /F7
  27.172 +/BaseFont /Times-Bold
  27.173 +/Encoding /WinAnsiEncoding >>
  27.174 +endobj
  27.175 +33 0 obj
  27.176 +<< /Type /Font
  27.177 +/Subtype /Type1
  27.178 +/Name /F6
  27.179 +/BaseFont /Times-Italic
  27.180 +/Encoding /WinAnsiEncoding >>
  27.181 +endobj
  27.182 +34 0 obj
  27.183 +<< /Type /Font
  27.184 +/Subtype /Type1
  27.185 +/Name /F5
  27.186 +/BaseFont /Times-Roman
  27.187 +/Encoding /WinAnsiEncoding >>
  27.188 +endobj
  27.189 +35 0 obj
  27.190 +<< /Type /Font
  27.191 +/Subtype /Type1
  27.192 +/Name /F11
  27.193 +/BaseFont /Courier-Bold
  27.194 +/Encoding /WinAnsiEncoding >>
  27.195 +endobj
  27.196 +1 0 obj
  27.197 +<< /Type /Pages
  27.198 +/Count 5
  27.199 +/Kids [7 0 R 9 0 R 11 0 R 13 0 R 15 0 R ] >>
  27.200 +endobj
  27.201 +2 0 obj
  27.202 +<< /Type /Catalog
  27.203 +/Pages 1 0 R
  27.204 + /Outlines 16 0 R
  27.205 + /PageMode /UseOutlines
  27.206 + >>
  27.207 +endobj
  27.208 +3 0 obj
  27.209 +<< 
  27.210 +/Font << /F3 29 0 R /F1 30 0 R /F9 31 0 R /F7 32 0 R /F6 33 0 R /F5 34 0 R /F11 35 0 R >> 
  27.211 +/ProcSet [ /PDF /ImageC /Text ] /XObject <</Im1 6 0 R 
  27.212 + >>
  27.213 +>> 
  27.214 +endobj
  27.215 +16 0 obj
  27.216 +<<
  27.217 + /First 18 0 R
  27.218 + /Last 20 0 R
  27.219 +>> endobj
  27.220 +17 0 obj
  27.221 +<<
  27.222 +/S /GoTo
  27.223 +/D [11 0 R /XYZ 51.692 679.701 null]
  27.224 +>>
  27.225 +endobj
  27.226 +19 0 obj
  27.227 +<<
  27.228 +/S /GoTo
  27.229 +/D [11 0 R /XYZ 51.692 592.376 null]
  27.230 +>>
  27.231 +endobj
  27.232 +21 0 obj
  27.233 +<<
  27.234 +/S /GoTo
  27.235 +/D [11 0 R /XYZ 51.692 544.751 null]
  27.236 +>>
  27.237 +endobj
  27.238 +23 0 obj
  27.239 +<<
  27.240 +/S /GoTo
  27.241 +/D [11 0 R /XYZ 51.692 101.551 null]
  27.242 +>>
  27.243 +endobj
  27.244 +25 0 obj
  27.245 +<<
  27.246 +/S /GoTo
  27.247 +/D [13 0 R /XYZ 51.692 327.769 null]
  27.248 +>>
  27.249 +endobj
  27.250 +27 0 obj
  27.251 +<<
  27.252 +/S /GoTo
  27.253 +/D [15 0 R /XYZ 51.692 693.551 null]
  27.254 +>>
  27.255 +endobj
  27.256 +xref
  27.257 +0 36
  27.258 +0000000000 65535 f 
  27.259 +0000035440 00000 n 
  27.260 +0000035525 00000 n 
  27.261 +0000035617 00000 n 
  27.262 +0000000015 00000 n 
  27.263 +0000000071 00000 n 
  27.264 +0000000782 00000 n 
  27.265 +0000022801 00000 n 
  27.266 +0000022907 00000 n 
  27.267 +0000023428 00000 n 
  27.268 +0000023534 00000 n 
  27.269 +0000026374 00000 n 
  27.270 +0000026482 00000 n 
  27.271 +0000030333 00000 n 
  27.272 +0000030441 00000 n 
  27.273 +0000033067 00000 n 
  27.274 +0000035790 00000 n 
  27.275 +0000035841 00000 n 
  27.276 +0000033175 00000 n 
  27.277 +0000035909 00000 n 
  27.278 +0000033308 00000 n 
  27.279 +0000035977 00000 n 
  27.280 +0000033493 00000 n 
  27.281 +0000036045 00000 n 
  27.282 +0000033689 00000 n 
  27.283 +0000036113 00000 n 
  27.284 +0000034073 00000 n 
  27.285 +0000036181 00000 n 
  27.286 +0000034376 00000 n 
  27.287 +0000034671 00000 n 
  27.288 +0000034784 00000 n 
  27.289 +0000034892 00000 n 
  27.290 +0000034998 00000 n 
  27.291 +0000035107 00000 n 
  27.292 +0000035218 00000 n 
  27.293 +0000035328 00000 n 
  27.294 +trailer
  27.295 +<<
  27.296 +/Size 36
  27.297 +/Root 2 0 R
  27.298 +/Info 4 0 R
  27.299 +>>
  27.300 +startxref
  27.301 +36249
  27.302 +%%EOF
    28.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    28.2 +++ b/doc/user_manual.txt	Wed Apr 26 10:48:09 2006 +0000
    28.3 @@ -0,0 +1,146 @@
    28.4 +Manuel utilisateur
    28.5 +==================
    28.6 +
    28.7 +:Author: Sylvain Thénault
    28.8 +:Organization: Logilab
    28.9 +:Version: $Revision: 1.7 $
   28.10 +:Date: $Date: 2005-03-29 14:24:04 $
   28.11 +:Abstract:
   28.12 +    Logilab documentation's tools user manual.
   28.13 +
   28.14 +
   28.15 +py2dbk
   28.16 +------
   28.17 +
   28.18 +Description
   28.19 +```````````
   28.20 +Transform a Python_ module into a Docbook_ document to get it nicely
   28.21 +formated (and usually to include it from another Docbook document). 
   28.22 +
   28.23 +Synopsis
   28.24 +````````
   28.25 +::
   28.26 +
   28.27 +    USAGE: py2dbk [OPTIONS] <input.py>...
   28.28 +
   28.29 +    OPTIONS:
   28.30 +      -h / --help
   28.31 +	   display this help message and exit
   28.32 +
   28.33 +      -r / --root "rootstring"
   28.34 +	   insert "rootstring" as root
   28.35 +
   28.36 +      -f / --format <OUTPUT_FORMAT>
   28.37 +	   set output format. Default to docbook.
   28.38 +	   Available formats are docbook, extended-docbook.
   28.39 +
   28.40 +      -s / --stdout
   28.41 +	   write results to standard output
   28.42 +
   28.43 +Example
   28.44 +```````
   28.45 +::
   28.46 +
   28.47 +    py2dbk --format extended-docbook myfile.py
   28.48 +
   28.49 +This example should produce a **myfile.xml** file containing the Python
   28.50 +source code from *myfile.py* formatted as a XML Docbook document. The
   28.51 +extended-docbook format will use special roles that should be handled
   28.52 +by specifics FO (PDF) or CSS (HTML) stylesheets.
   28.53 +
   28.54 +
   28.55 +
   28.56 +xml2dbk
   28.57 +-------
   28.58 +
   28.59 +Description
   28.60 +```````````
   28.61 +Transform any XML file into a Docbook_ or HTML document to get it nicely
   28.62 +formated (and usually to include it from another Docbook document).
   28.63 +
   28.64 +Synopsis
   28.65 +````````
   28.66 +::
   28.67 +
   28.68 +    USAGE: xml2dbk [OPTIONS] <input.xml>...
   28.69 +
   28.70 +    OPTIONS:
   28.71 +      -h / --help
   28.72 +	   display this help message and exit
   28.73 +
   28.74 +      -o / --output <OUTPUT_FILE>
   28.75 +	   write results in file <OUTPUT_FILE>.
   28.76 +      -s / --stdout
   28.77 +	   write results to standard output.
   28.78 +      -e / --encoding iso-8859-1
   28.79 +	   specify encoding to use in outputs.
   28.80 +
   28.81 +      -n / --no-head
   28.82 +	   do not insert output headers.
   28.83 +
   28.84 +      -f / --format <OUTPUT_FORMAT>
   28.85 +	   set output format. Default to docbook.
   28.86 +	   Available formats are docbook, extended-docbook, html.
   28.87 +
   28.88 +Example
   28.89 +```````
   28.90 +::
   28.91 +
   28.92 +    xml2dbk --format extended-docbook myfile.xml
   28.93 +
   28.94 +This example should produce a **myfile_dcbk.xml** file containing the
   28.95 +original XML document from *myfile.xml* formatted as a XML Docbook
   28.96 +document. The extended-docbook format will use special roles that
   28.97 +should be handled by specifics FO (PDF) or CSS (HTML) stylesheets.
   28.98 +
   28.99 +
  28.100 +
  28.101 +mkdoc
  28.102 +-----
  28.103 +
  28.104 +Description
  28.105 +```````````
  28.106 +Transform ReST_ (Restructured Text) or Docbook_ files to html ou
  28.107 +pdf. You will need some external stylesheets not included in this
  28.108 +package to get the Docbook_ to HTML or PDF transformation done.
  28.109 +
  28.110 +Available format depends on the installed transformation. FIXME: write
  28.111 +doc about this...
  28.112 +
  28.113 +Synopsis
  28.114 +````````
  28.115 +::
  28.116 +
  28.117 +    USAGE: mkdoc [OPTIONS] <input file>...
  28.118 +
  28.119 +    OPTIONS:
  28.120 +      -h / --help
  28.121 +	   display this help message and exit
  28.122 +
  28.123 +      -f / --format <OUTPUT_FORMAT>
  28.124 +	   set output format. Default to html.
  28.125 +	   Available formats are docbook, html, multi_html, pdf, pdf_ao, pdf_iup, pdf_manual, pdf_psyc, site_html.
  28.126 +
  28.127 +      -n / --noverif
  28.128 +	   doesn't verify XML correctness.
  28.129 +      -k / --keep-tmp
  28.130 +	   doesn't remove temporary directory where transforms are done.
  28.131 +
  28.132 +      -p / --parameter <NAME>:<VALUE>
  28.133 +	   sets the <NAME> stylesheet parameter to <VALUE>. You may set this option
  28.134 +	   multiple times. Parameters are given to the xslt processor.
  28.135 +
  28.136 +Example
  28.137 +```````
  28.138 +::
  28.139 +
  28.140 +    mkdoc --format pdf myfile.rst unautrefichier.xml
  28.141 +
  28.142 +This example should produce **myfile.pdf** and **anotherfile.pdf**
  28.143 +files containing each original document (one in ReST and the other in
  28.144 +Docbook) formatted as a PDF file.
  28.145 +
  28.146 +
  28.147 +.. _ReST: http://docutils.sourceforge.net/rst.html
  28.148 +.. _Docbook: http://www.docbook.org
  28.149 +.. _Python: http://www.python.org
  28.150 \ No newline at end of file
    29.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    29.2 +++ b/editor.py	Wed Apr 26 10:48:09 2006 +0000
    29.3 @@ -0,0 +1,48 @@
    29.4 +"""
    29.5 +Main script to launch editor's IHM (depends on PyGantt)
    29.6 +USAGE: python editor.py path/to/taskeditor.glade
    29.7 +
    29.8 +(Note: taskeditor.glade is part of Pygantt)
    29.9 +"""
   29.10 +
   29.11 +__revision__ = "$Id: editor.py,v 1.4 2004-11-02 07:50:52 adim Exp $"
   29.12 +
   29.13 +import locale, gettext
   29.14 +import pygtk
   29.15 +pygtk.require("2.0")
   29.16 +import gtk
   29.17 +import gtk.glade
   29.18 +
   29.19 +def run(glade_file):
   29.20 +
   29.21 +
   29.22 +    import sys
   29.23 +    if '-h' in sys.argv or '--help' in sys.argv :
   29.24 +        print __doc__
   29.25 +        sys.exit(0)
   29.26 +    APP = 'task_editor'
   29.27 +    DIR = 'ihm/i18n'
   29.28 +    # GLADE_FILE = 'ihm/taskeditor.glade'
   29.29 +
   29.30 +    from logilab.pygantt.ihm.taskeditor import TaskEditor, \
   29.31 +         TaskEditorController
   29.32 +
   29.33 +
   29.34 +    gettext.bindtextdomain (APP, DIR)
   29.35 +    gettext.textdomain (APP)
   29.36 +    gettext.install (APP, DIR, unicode=1)
   29.37 +
   29.38 +    gtk.glade.bindtextdomain (APP, DIR)
   29.39 +    gtk.glade.textdomain (APP)
   29.40 +
   29.41 +
   29.42 +    editor = TaskEditor(glade_file)
   29.43 +    ctrl = TaskEditorController(editor)
   29.44 +    editor.set_controller(ctrl)
   29.45 +    editor.show()
   29.46 +
   29.47 +    gtk.main()
   29.48 +
   29.49 +import sys
   29.50 +if __name__ == '__main__':
   29.51 +    run(sys.argv[1])
    30.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    30.2 +++ b/examples/mkdocrc	Wed Apr 26 10:48:09 2006 +0000
    30.3 @@ -0,0 +1,18 @@
    30.4 +# sample configuration file for mkdoc
    30.5 +#
    30.6 +# Copyright (c) 2000-2003 LOGILAB S.A. (Paris, FRANCE).
    30.7 +# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    30.8 +
    30.9 +[MAIN]
   30.10 +
   30.11 +# default target
   30.12 +target=html
   30.13 +
   30.14 +# path of the fop executable
   30.15 +fop=/usr/bin/fop
   30.16 +
   30.17 +# path of the xsltproc executable
   30.18 +xsltproc=/usr/bin/xsltproc
   30.19 +
   30.20 +# directory where logilab's stylesheets are located
   30.21 +xsltroot=/home/logilab/lib/xslt/
    31.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    31.2 +++ b/mkview.py	Wed Apr 26 10:48:09 2006 +0000
    31.3 @@ -0,0 +1,249 @@
    31.4 +#!/usr/bin/env python2.2
    31.5 +# Copyright (c) 2000-2002 LOGILAB S.A. (Paris, FRANCE).
    31.6 +# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    31.7 +#
    31.8 +# This program is free software; you can redistribute it and/or modify it under
    31.9 +# the terms of the GNU General Public License as published by the Free Software
   31.10 +# Foundation; either version 2 of the License, or (at your option) any later
   31.11 +# version.
   31.12 +#
   31.13 +# This program is distributed in the hope that it will be useful, but WITHOUT
   31.14 +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   31.15 +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
   31.16 +#
   31.17 +# You should have received a copy of the GNU General Public License along with
   31.18 +# this program; if not, write to the Free Software Foundation, Inc.,
   31.19 +# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   31.20 +"""USAGE:  mkview [OPTIONS] <input.xml> [output_file.xml]
   31.21 +
   31.22 +  Will create a table in which the project's tasks are listed with all
   31.23 +  the main informations (duration, cost, resources)
   31.24 +  If output_file is omitted, it will be automatically generated
   31.25 +
   31.26 +OPTIONS:
   31.27 +  --h / -help
   31.28 +          display this help message and exit.
   31.29 +  --verbose
   31.30 +          Will output a lot of messages to stdout.
   31.31 +
   31.32 +  --format=<OUPUT_FORMAT>
   31.33 +	  set output format. Default to docbook.
   31.34 +	  Available formats are docbook, csv, html.
   31.35 +          Defaults to docbook.
   31.36 +          
   31.37 +  --group-class=<group_class>
   31.38 +          Will process only tasks which match this group-class
   31.39 +          (This option can be combined with --group-name).
   31.40 +  --group-name=<group_name>
   31.41 +          Will process only tasks which match this group-name
   31.42 +          (This option can be combined with --group-class).
   31.43 +
   31.44 +  --dump-groups=<groups_table.xxx>
   31.45 +          Creates a table with tasks in rows, classes in columns
   31.46 +          and the list of class-groups in the table body.
   31.47 +  --dump-vcg=<file.vcg>
   31.48 +          Will dump a vcg file which will contain the task dependencies.
   31.49 +  --dump-tasks=<a_file.xxx>
   31.50 +          Will dump tasks information. If --group-class and / or --group-name
   31.51 +          are specified, then only tasks matching these groups will be processed.
   31.52 +          The file a_file.xml will be used for the output.
   31.53 +
   31.54 +  --resources-columns=(on|off)
   31.55 +          Add / remove resources columns in the tasks-table.
   31.56 +          Default is 'off'. 
   31.57 +"""
   31.58 +
   31.59 +import sys
   31.60 +import getopt
   31.61 +
   31.62 +__revision__ = '$Id: mkview.py,v 1.13 2004-10-31 02:18:06 nico Exp $'
   31.63 +
   31.64 +
   31.65 +def build_filename(base, group_name, group_class):
   31.66 +    """Returns a filename for the tasks table
   31.67 +    """
   31.68 +    
   31.69 +    table_filename = base
   31.70 +    if group_name :
   31.71 +        if group_class :
   31.72 +            table_filename = "%s_%s_%s" % (base, group_name,group_class)
   31.73 +        else:
   31.74 +            table_filename = "%s_%s" % (base,group_name)
   31.75 +    else:
   31.76 +        if group_class :
   31.77 +            table_filename = "%s_%s" % (base,group_class)
   31.78 +
   31.79 +    return table_filename
   31.80 +
   31.81 +def run(*args):
   31.82 +    # Long options list
   31.83 +    l_opt = ['group-class=', 'group-name=', 'format=',
   31.84 +             'dump-vcg=', 'dump-tasks=',
   31.85 +             'resources-columns=',
   31.86 +             'dump-groups=', 'help', 'verbose']
   31.87 +    try:
   31.88 +        (optlist,args) = getopt.getopt(args, 'o:hv', l_opt)
   31.89 +    except getopt.GetoptError, e :
   31.90 +        print e
   31.91 +        print __doc__
   31.92 +        sys.exit(1)
   31.93 +
   31.94 +    group_class = None
   31.95 +    group_name = None
   31.96 +    format = "docbook"
   31.97 +    vcg_filename = None
   31.98 +    tasks_file = None
   31.99 +    verbose = 0
  31.100 +    check_integrity = 0
  31.101 +    list_ref = 0
  31.102 +    group_table_name = None
  31.103 +    resource_cols = False
  31.104 +    output_file = None
  31.105 +
  31.106 +    ## Browse command line
  31.107 +    for opt, val in optlist:
  31.108 +
  31.109 +        if opt == '--group-class':
  31.110 +            group_class = val
  31.111 +        elif opt == '--group-name':
  31.112 +            group_name = val
  31.113 +        elif opt == '--format':
  31.114 +            format = val
  31.115 +            if format not in ("docbook","csv","html"):
  31.116 +                print "[mkview] ERROR : unknown format : ",format
  31.117 +                sys.exit(1)
  31.118 +        elif opt == '--dump-vcg':
  31.119 +            vcg_filename = val
  31.120 +        elif opt == '--dump-tasks':
  31.121 +            tasks_file = val
  31.122 +        elif opt == '--resources-columns':
  31.123 +            if val == 'on':
  31.124 +                resource_cols = True
  31.125 +            else:
  31.126 +                resource_cols = False
  31.127 +        elif opt == '--check-integrity':
  31.128 +            check_integrity = 1
  31.129 +        elif opt == '--list-references':
  31.130 +            list_ref = 1
  31.131 +        elif opt == "--dump-groups":
  31.132 +            group_table_name = val
  31.133 +        elif opt in ('--help','-h'):
  31.134 +            print __doc__
  31.135 +            sys.exit(0)
  31.136 +        elif opt in ('--verbose','-v'):
  31.137 +            verbose = 1
  31.138 +        else:
  31.139 +            print __doc__
  31.140 +            sys.exit(1)
  31.141 +
  31.142 +    # if len(args) == 0 or len(args) > 1:
  31.143 +    if len(args) < 1 or len(args) > 2:
  31.144 +        print __doc__
  31.145 +        sys.exit(1)
  31.146 +
  31.147 +    project_file = args[0]
  31.148 +    if len(args) == 2:
  31.149 +        output_file = args[1].split('.')[0]
  31.150 +
  31.151 +    if verbose:
  31.152 +        print "[mkview] File to parse : ",project_file
  31.153 +
  31.154 +
  31.155 +    from logilab.pygantt.lib.readers import ExtProjectXMLReader
  31.156 +    from logilab.pygantt.lib import writers
  31.157 +    parser = ExtProjectXMLReader()
  31.158 +    # Start the parse.
  31.159 +    if verbose:
  31.160 +        print "[mkview] starting parse ..."
  31.161 +    project = parser.fromFile(project_file)
  31.162 +
  31.163 +    from logilab.pygantt.schedule import schedule
  31.164 +    schedule(project, csp = 0)
  31.165 +    # project.schedule_reset()
  31.166 +
  31.167 +    if verbose:
  31.168 +        print "[mkview] parsing done  "
  31.169 +
  31.170 +
  31.171 +    # Find appropriate writer object
  31.172 +    dumper = None
  31.173 +    if output_file is None:
  31.174 +        table_file_name = build_filename("tasks_table",group_name,group_class)
  31.175 +    else:
  31.176 +        table_file_name = output_file
  31.177 +
  31.178 +    if format == "docbook":
  31.179 +        table_file_name += ".xml"
  31.180 +        dumper = writers.DocbookDumper(project)
  31.181 +    elif format == "csv":
  31.182 +        table_file_name += ".csv"
  31.183 +        dumper = writers.CsvDumper(project)
  31.184 +    else:
  31.185 +        table_file_name += ".html"
  31.186 +        dumper = writers.HtmlDumper(project)
  31.187 +
  31.188 +
  31.189 +    # XXX for tests ...
  31.190 +    dumper.dump_gantt_xml("fichier_gantt.xml")
  31.191 +
  31.192 +
  31.193 +    if tasks_file :
  31.194 +        if verbose:
  31.195 +            print "[mkview] generating tasks description file ..."
  31.196 +        try:
  31.197 +            dumper.dump(tasks_file, group_name, group_class)
  31.198 +            # p.dumpDocbook(open(tasks_file,"w"))
  31.199 +            if verbose:
  31.200 +                print "[mkview] tasks description file generated"
  31.201 +        except NotImplementedError, e:
  31.202 +            print "[mkview] ERROR : ", e
  31.203 +
  31.204 +    ## Task Table
  31.205 +    if verbose:
  31.206 +        print "[mkview] generating task informations table ..."
  31.207 +    try:
  31.208 +        dumper.dump_task_table(table_file_name, group_name, group_class,
  31.209 +                                       resource_cols = resource_cols)
  31.210 +        if verbose:
  31.211 +            print "[mkview] task informations table generated (%s)" % \
  31.212 +                  table_file_name
  31.213 +    except NotImplementedError, e:
  31.214 +        print "[mkview] ERROR : ", e
  31.215 +
  31.216 +##     ## Integrity (Class / Group check)
  31.217 +##     if check_integrity:
  31.218 +##         if verbose: print "[mkview] Checking classes / groups integrity ..."
  31.219 +##         project.checkGroupsIntegrity()
  31.220 +##         if verbose: print "[mkview] Checking integrity done"
  31.221 +
  31.222 +    ## VCG generation
  31.223 +    if vcg_filename:
  31.224 +        if verbose:
  31.225 +            print "[mkview] generating vcg file"
  31.226 +        dumper.dump_vcg(vcg_filename)
  31.227 +        if verbose:
  31.228 +            print "[mkview] vcg file generated (%s)" % vcg_filename
  31.229 +
  31.230 +##     ## External Refs
  31.231 +##     if list_ref:
  31.232 +##         if verbose: print "[mkview] checking all external references"
  31.233 +##         ref_dict = project.getExternalReferences()
  31.234 +##         for ref, taskid_list in ref_dict.items():
  31.235 +##             print ref.encode('iso-8859-1')," : ",taskid_list
  31.236 +
  31.237 +    ## group_table
  31.238 +    if group_table_name:
  31.239 +        if verbose:
  31.240 +            print "[mkview] generating group_table file"
  31.241 +        try:
  31.242 +            dumper.dump_group_dependencies(group_table_name)
  31.243 +            if verbose:
  31.244 +                print "[mkview] group_table file generated (%s)" \
  31.245 +                      % group_table_name
  31.246 +        except NotImplementedError, e:
  31.247 +            print "[mkview] ERROR : ", e
  31.248 +
  31.249 +    
  31.250 +
  31.251 +if __name__ == '__main__':
  31.252 +    run(*sys.argv[1:])
    32.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    32.2 +++ b/py2db.py	Wed Apr 26 10:48:09 2006 +0000
    32.3 @@ -0,0 +1,204 @@
    32.4 +#!/usr/bin/python
    32.5 +# -*- coding: ISO-8859-1 -*-
    32.6 +"""%(PROG)s: format Python source code to xml docbook using roles
    32.7 +
    32.8 +USAGE: %(PROG)s [OPTIONS] <input.py>...
    32.9 +
   32.10 +OPTIONS:
   32.11 +  -h / --help
   32.12 +       display this help message and exit
   32.13 +       
   32.14 +  -r / --root "rootstring"
   32.15 +       insert "rootstring" as root
   32.16 +       
   32.17 +  -f / --format <OUTPUT_FORMAT>
   32.18 +       set output format. Default to %(DEFAULT_FORMAT)s.
   32.19 +       Available formats are %(FORMATS)s.
   32.20 +       
   32.21 +  -s / --stdout
   32.22 +       write results to standard output
   32.23 +"""
   32.24 +## Original code from active state recipe
   32.25 +##         'Colorize Python source using the built-in tokenizer'
   32.26 +## posted by Jürgen Hermann and modified to obtain docbook instead of colored
   32.27 +## html
   32.28 +
   32.29 +## ----------------------------------------------------------------------------
   32.30 +##      MoinMoin - Python Source Parser
   32.31 +
   32.32 +##  This code is part of MoinMoin (http://moin.sourceforge.net/) and converts
   32.33 +##  Python source code to HTML markup, rendering comments, keywords, operators,
   32.34 +##  numeric and string literals in different colors.
   32.35 +
   32.36 +##  It shows how to use the built-in keyword, token and tokenize modules
   32.37 +##  to scan Python source code and re-emit it with no changes to its
   32.38 +##  original formatting (which is the hard part).
   32.39 +
   32.40 +__revision__ = '$Id: py2db.py,v 1.6 2004-10-31 02:18:06 nico Exp $'
   32.41 +
   32.42 +import sys, cStringIO
   32.43 +import keyword, token, tokenize
   32.44 +from xml.sax.saxutils import escape
   32.45 +from os.path import basename
   32.46 +
   32.47 +
   32.48 +## Python Source Parser #####################################################
   32.49 +
   32.50 +_KEYWORD = token.NT_OFFSET + 1
   32.51 +_TEXT    = token.NT_OFFSET + 2
   32.52 +
   32.53 +class Parser:
   32.54 +    """
   32.55 +    Send colored python source.
   32.56 +    """
   32.57 +
   32.58 +    def __init__(self, raw, tags, out = sys.stdout):
   32.59 +        """
   32.60 +        Store the source text.
   32.61 +        """
   32.62 +        self.raw = raw.expandtabs().strip()
   32.63 +        self.out = out
   32.64 +        self.tags = tags
   32.65 +        
   32.66 +    def format(self, root=''):
   32.67 +        """
   32.68 +        Parse and send the colored source.
   32.69 +        """
   32.70 +        # store line offsets in self.lines
   32.71 +        self.lines = [0, 0]
   32.72 +        pos = 0
   32.73 +        while 1:
   32.74 +            pos = self.raw.find('\n', pos) + 1
   32.75 +            if not pos: break
   32.76 +            self.lines.append(pos)
   32.77 +        self.lines.append(len(self.raw))
   32.78 +
   32.79 +        # parse the source and write it
   32.80 +        self.pos = 0
   32.81 +        text = cStringIO.StringIO(self.raw)
   32.82 +        if root:
   32.83 +            self.out.write('<%s>\n'%root)
   32.84 +        self.out.write('  <programlisting role="python">\n')
   32.85 +        try:
   32.86 +            tokenize.tokenize(text.readline, self)
   32.87 +        except tokenize.TokenError, ex:
   32.88 +            msg = ex[0]
   32.89 +            line = ex[1][0]
   32.90 +            print "ERROR: %s%s\n" % (msg, self.raw[self.lines[line]:])
   32.91 +        self.out.write('\n  </programlisting>\n')
   32.92 +        if root:
   32.93 +            self.out.write('</%s>\n'%root)
   32.94 +
   32.95 +    def __call__(self, toktype, toktext, (srow, scol), (erow, ecol), line):
   32.96 +        """
   32.97 +        Token handler.
   32.98 +        """
   32.99 +        #print "type", toktype, token.tok_name[toktype], "text", toktext,
  32.100 +        #print "start", srow,scol, "end", erow,ecol, "<br>"
  32.101 +
  32.102 +        ## calculate new positions
  32.103 +        oldpos = self.pos
  32.104 +        newpos = self.lines[srow] + scol
  32.105 +        self.pos = newpos + len(toktext)
  32.106 +
  32.107 +        ## handle newlines
  32.108 +        if toktype in [token.NEWLINE, tokenize.NL]:
  32.109 +            self.out.write('\n')
  32.110 +            return
  32.111 +
  32.112 +        ## send the original whitespace, if needed
  32.113 +        if newpos > oldpos:
  32.114 +            self.out.write(self.raw[oldpos:newpos])
  32.115 +
  32.116 +        ## skip indenting tokens
  32.117 +        if toktype in [token.INDENT, token.DEDENT]:
  32.118 +            self.pos = newpos
  32.119 +            return
  32.120 +
  32.121 +        ## map token type to a group
  32.122 +        if token.LPAR <= toktype and toktype <= token.OP:
  32.123 +            toktype = token.OP
  32.124 +        elif toktype == token.NAME and keyword.iskeyword(toktext):
  32.125 +            toktype = _KEYWORD
  32.126 +
  32.127 +        t_tags = self.tags.get(toktype, self.tags[_TEXT])
  32.128 +
  32.129 +        ## send text
  32.130 +        self.out.write(t_tags[0])
  32.131 +        self.out.write(escape(toktext))
  32.132 +        self.out.write(t_tags[1])
  32.133 +
  32.134 +
  32.135 +## Command line ###############################################################
  32.136 +_TAGS = {
  32.137 +    token.NUMBER:       ('<emphasis role="number">', '</emphasis>'),
  32.138 +    token.OP:           ('<emphasis role="op">', '</emphasis>'),
  32.139 +    token.STRING:       ('<emphasis role="string">', '</emphasis>'),
  32.140 +    tokenize.COMMENT:   ('<emphasis role="comment">', '</emphasis>'),
  32.141 +    token.NAME:         ('<emphasis role="name">', '</emphasis>'),
  32.142 +    token.ERRORTOKEN:   ('<emphasis role="error">', '</emphasis>'), # ?
  32.143 +    _KEYWORD:           ('<emphasis role="keyword">', '</emphasis>'),
  32.144 +    _TEXT:              ('', '')
  32.145 +    }
  32.146 +_STANDARDS_TAGS = {
  32.147 +    token.NUMBER:       ('', ''),
  32.148 +    token.OP:           ('', ''),
  32.149 +    token.STRING:       ('<emphasis>', '</emphasis>'),
  32.150 +    tokenize.COMMENT:   ('<emphasis>', '</emphasis>'),
  32.151 +    token.NAME:         ('', ''),
  32.152 +    token.ERRORTOKEN:   ('', ''), # ?
  32.153 +    _KEYWORD:           ('<emphasis role="bold">', '</emphasis>'),
  32.154 +    _TEXT:              ('', '')
  32.155 +    }
  32.156 +
  32.157 +PROG = basename(sys.argv[0])
  32.158 +FORMATS = ('docbook', 'extended-docbook')
  32.159 +DEFAULT_FORMAT = 'docbook'
  32.160 +
  32.161 +def run(args):
  32.162 +    import getopt
  32.163 +    
  32.164 +    ## get options
  32.165 +    (opt, args) = getopt.getopt(args,
  32.166 +                                'hr:f:s',
  32.167 +                                ['help', 'root=', 'format=', 'stdout'])
  32.168 +    root, ext, stdout = '', 0, 0
  32.169 +    for o in opt:
  32.170 +        if o[0] == '-h' or o[0] == '--help':
  32.171 +            print __doc__ % globals()
  32.172 +            return
  32.173 +        elif o[0] == '-r' or o[0] == '--root':
  32.174 +            root = o[1]
  32.175 +        elif o[0] == '-f' or o[0] == '--format':
  32.176 +            val = o[1].lower()
  32.177 +            if not val in FORMATS:
  32.178 +                raise 'Unknown format %s' % val
  32.179 +            if val == 'extended-docbook':
  32.180 +                ext = 1
  32.181 +        elif o[0] == '-s' or o[0] == '--stdout':
  32.182 +            stdout = 1
  32.183 +            
  32.184 +    ## transforms source files
  32.185 +    for file in args:
  32.186 +        if file[-3:] != '.py':
  32.187 +            sys.stderr.write('Unknown extension, ignored file %s\n' % file)
  32.188 +            continue
  32.189 +        source = open(file, 'r')
  32.190 +        if not stdout:
  32.191 +            output = '%s.xml' % file[:-3]
  32.192 +            dest = open(output, 'w+')
  32.193 +        else:
  32.194 +            dest = sys.stdout
  32.195 +        sys.stderr.write("Formatting...\n")
  32.196 +        ## write colorized version to "python.html"
  32.197 +        if not ext:
  32.198 +            Parser(source.read(), _STANDARDS_TAGS, dest).format(root)
  32.199 +        else:
  32.200 +            Parser(source.read(), _TAGS, dest).format(root)
  32.201 +        source.close()
  32.202 +        dest.close()
  32.203 +
  32.204 +    
  32.205 +if __name__ == "__main__":
  32.206 +    run(sys.argv[1:])
  32.207 +
    33.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    33.2 +++ b/rest_docbook.py	Wed Apr 26 10:48:09 2006 +0000
    33.3 @@ -0,0 +1,1054 @@
    33.4 +#!/usr/bin/env python
    33.5 +# pylint: disable-msg=W0131
    33.6 +
    33.7 +"""
    33.8 +:Author: Ollie Rutherfurd
    33.9 +:Contact: oliver@rutherfurd.net
   33.10 +:Revision: $Revision: 1.7 $
   33.11 +:Date: $Date: 2006-02-08 15:21:17 $
   33.12 +:Copyright: This module has been placed in the public domain.
   33.13 +
   33.14 +DocBook document tree Writer.
   33.15 +
   33.16 +This Writer converts a reST document tree to a subset
   33.17 +of DocBook.
   33.18 +
   33.19 +.. Note:: This is an unfinished work in progress.
   33.20 +
   33.21 +Document Types
   33.22 +==============
   33.23 +
   33.24 +This writer can create 3 types of DocBook documents:
   33.25 +
   33.26 +1. "article" *(default)*
   33.27 +2. "book"
   33.28 +3. "chapter"
   33.29 +
   33.30 +.. Note:: When creating a "book" document, all first-level
   33.31 +    sections are output as "chapter" elements instead
   33.32 +    of "section" as in "article" and "chapter".
   33.33 +
   33.34 +Mappings
   33.35 +========
   33.36 +
   33.37 +Option List
   33.38 +-----------
   33.39 +
   33.40 +As there is no direct equivlent for a listing of program options
   33.41 +in DocBook_, as defined in reST_, a table containing the
   33.42 +option list contents is generated.
   33.43 +
   33.44 +Field List
   33.45 +----------
   33.46 +
   33.47 +Like `Option List`_, there is not direct equivlent for
   33.48 +a Field List in DocBook, so this is done using a
   33.49 +"variablelist".
   33.50 +
   33.51 +.. NOTE:: It might be better to switch Definition List
   33.52 +    to glossary or something similar, so Field List
   33.53 +    and Definition List are generating the same type
   33.54 +    of output.
   33.55 +
   33.56 +Bibliography Elements
   33.57 +---------------------
   33.58 +
   33.59 +Here's how reST's bibliography elements are mapped
   33.60 +to DocBook elements:
   33.61 +
   33.62 ++--------------+---------------------------------------------+
   33.63 +| reST Element | DocBook Element                             |
   33.64 ++==============+=============================================+
   33.65 +| author       | {doctype}info/author/othername              |
   33.66 +|              | or                                          |
   33.67 +|              | {doctype}info/authorgroup/author/othername  |
   33.68 +|              | if nested under ``authors``                 |
   33.69 ++--------------+---------------------------------------------+
   33.70 +| authors      | {doctype}info/authorgroup/                  |
   33.71 ++--------------+---------------------------------------------+
   33.72 +| contact      | {doctype}info/author/email                  |
   33.73 ++--------------+---------------------------------------------+
   33.74 +| copyright    | {doctype}info/legalnotice                   |
   33.75 ++--------------+---------------------------------------------+
   33.76 +| date         | {doctype}info/date                          |
   33.77 ++--------------+---------------------------------------------+
   33.78 +| organization | {doctype}info/orgname                       |
   33.79 ++--------------+---------------------------------------------+
   33.80 +| revision     | concatenated with ``version`` into          |
   33.81 +|              | {doctype}info/edition                       |
   33.82 ++--------------+---------------------------------------------+
   33.83 +| status       | {doctype}info/releaseinfo                   |
   33.84 ++--------------+---------------------------------------------+
   33.85 +| version      | concatenated with ``revision`` into         |
   33.86 +|              | {doctype}info/edition                       |
   33.87 ++--------------+---------------------------------------------+
   33.88 +
   33.89 +Note: ``{doctype}`` is the type of the DocBook document 
   33.90 +being generated, one of the following: ``article``, 
   33.91 +``book``, or ``chapter``.
   33.92 +
   33.93 +Todo
   33.94 +====
   33.95 +
   33.96 +- Inline images -- need to figure out how to identify an inline image
   33.97 +- list item marks are not guarenteed to be what was specified (if they
   33.98 +  are it is be coincidence, however unless one starts out of order
   33.99 +  they should match most of the time).
  33.100 +- sidebar subtitle needs to go into sidebarinfo
  33.101 +
  33.102 +Should para, note, etc... not in a section at the start
  33.103 +of the document be stuffed into an untitled ``section``?
  33.104 +
  33.105 +"""
  33.106 +
  33.107 +__docformat__ = 'reStructuredText'
  33.108 +
  33.109 +import re
  33.110 +import string
  33.111 +from docutils import writers, nodes, languages
  33.112 +from types import ListType
  33.113 +
  33.114 +class Writer(writers.Writer):
  33.115 +
  33.116 +    settings_spec = (
  33.117 +        'DocBook-Specific Options',
  33.118 +        None,
  33.119 +        (('Set DocBook document type. '
  33.120 +            'Choices are "article", "book", and "chapter". '
  33.121 +            'Default is "article".',
  33.122 +            ['--doctype'],
  33.123 +            {'default': 'article', 
  33.124 +             'metavar': '<name>',
  33.125 +             'type': 'choice', 
  33.126 +             'choices': ('article', 'book', 'chapter',)
  33.127 +            }
  33.128 +         ),
  33.129 +        )
  33.130 +    )
  33.131 +
  33.132 +    output = None
  33.133 +    """Final translated form of `document`."""
  33.134 +
  33.135 +    def translate(self):
  33.136 +        visitor = DocBookTranslator(self.document)
  33.137 +        self.document.walkabout(visitor)
  33.138 +        self.output = visitor.astext()
  33.139 +
  33.140 +
  33.141 +class FragmentWriter(Writer):
  33.142 +    
  33.143 +    def translate(self):
  33.144 +        visitor = DocBookTranslator(self.document)
  33.145 +        self.document.walkabout(visitor)
  33.146 +        self.output = u''.join(visitor.body)
  33.147 +
  33.148 +class DocBookTranslator(nodes.NodeVisitor):
  33.149 +
  33.150 +    XML_DECL = '<?xml version="1.0" encoding="%s"?>\n'
  33.151 +
  33.152 +    DOCTYPE_DECL = """<!DOCTYPE %s 
  33.153 +        PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
  33.154 +        "http://www.oasis-open.org/docbook/xml/4.0/docbookx.dtd">\n"""
  33.155 +
  33.156 +    def __init__(self, document):
  33.157 +        nodes.NodeVisitor.__init__(self, document)
  33.158 +        self.language = languages.get_language(
  33.159 +            document.settings.language_code)
  33.160 +        self.doctype = document.settings.doctype
  33.161 +        self.doc_header = [
  33.162 +            self.XML_DECL % (document.settings.output_encoding,),
  33.163 +            self.DOCTYPE_DECL % (self.doctype,),
  33.164 +            '<%s>\n' % (self.doctype,),
  33.165 +        ]
  33.166 +        self.doc_footer = [
  33.167 +            '</%s>\n' % (self.doctype,)
  33.168 +        ]
  33.169 +        self.body = []
  33.170 +        self.section = 0
  33.171 +        self.context = []
  33.172 +        self.colnames = []
  33.173 +        self.footnotes = {}
  33.174 +        self.footnote_map = {}
  33.175 +        self.docinfo = []
  33.176 +
  33.177 +    def astext(self):
  33.178 +        return ''.join(self.doc_header
  33.179 +                    + self.docinfo
  33.180 +                    + self.body
  33.181 +                    + self.doc_footer)
  33.182 +
  33.183 +    def encode(self, text):
  33.184 +        """Encode special characters in `text` & return."""
  33.185 +        # @@@ A codec to do these and all other 
  33.186 +        # HTML entities would be nice.
  33.187 +        text = text.replace("&", "&amp;")
  33.188 +        text = text.replace("<", "&lt;")
  33.189 +        text = text.replace('"', "&quot;")
  33.190 +        text = text.replace(">", "&gt;")
  33.191 +        return text
  33.192 +
  33.193 +    def rearrange_footnotes(self):
  33.194 +        """
  33.195 +        Replaces ``foonote_reference`` placeholders with
  33.196 +        ``footnote`` element content as DocBook and reST
  33.197 +        handle footnotes differently.
  33.198 +
  33.199 +        DocBook defines footnotes inline, whereas they
  33.200 +        may be anywere in reST.  This function replaces the 
  33.201 +        first instance of a ``footnote_reference`` with 
  33.202 +        the ``footnote`` element itself, and later 
  33.203 +        references of the same a  footnote with 
  33.204 +        ``footnoteref`` elements.
  33.205 +        """
  33.206 +        for (footnote_id,refs) in self.footnote_map.items():
  33.207 +            ref_id, context, pos = refs[0]
  33.208 +            context[pos] = ''.join(self.footnotes[footnote_id])
  33.209 +            for ref_id, context, pos in refs[1:]:
  33.210 +                context[pos] = '<footnoteref linkend="%s"/>' \
  33.211 +                    % (footnote_id,)
  33.212 +
  33.213 +    def attval(self, text,
  33.214 +               transtable=string.maketrans('\n\r\t\v\f', '     ')):
  33.215 +        """Cleanse, encode, and return attribute value text."""
  33.216 +        return self.encode(text.translate(transtable))
  33.217 +
  33.218 +    def starttag(self, node, tagname, suffix='\n', infix='', **attributes):
  33.219 +        """
  33.220 +        Construct and return a start tag given a node 
  33.221 +        (id & class attributes are extracted), tag name, 
  33.222 +        and optional attributes.
  33.223 +        """
  33.224 +        atts = {}
  33.225 +        for (name, value) in attributes.items():
  33.226 +            atts[name.lower()] = value
  33.227 +
  33.228 +        for att in ('id',):             # node attribute overrides
  33.229 +            if node.has_key(att):
  33.230 +                atts[att] = node[att]
  33.231 +
  33.232 +        attlist = atts.items()
  33.233 +        attlist.sort()
  33.234 +        parts = [tagname.lower()]
  33.235 +        for name, value in attlist:
  33.236 +            if value is None:           # boolean attribute
  33.237 +                # According to the HTML spec, ``<element boolean>`` is good,
  33.238 +                # ``<element boolean="boolean">`` is bad.
  33.239 +                # (But the XHTML (XML) spec says the opposite.  <sigh>)
  33.240 +                parts.append(name.lower())
  33.241 +            elif isinstance(value, ListType):
  33.242 +                values = [str(v) for v in value]
  33.243 +                parts.append('%s="%s"' % (name.lower(),
  33.244 +                                          self.attval(' '.join(values))))
  33.245 +            else:
  33.246 +                parts.append('%s="%s"' % (name.lower(),
  33.247 +                                          self.attval(str(value))))
  33.248 +        return '<%s%s>%s' % (' '.join(parts), infix, suffix)
  33.249 +
  33.250 +    def emptytag(self, node, tagname, suffix='\n', **attributes):
  33.251 +        """Construct and return an XML-compatible empty tag."""
  33.252 +        return self.starttag(node, tagname, suffix, infix=' /', **attributes)
  33.253 +
  33.254 +    def visit_Text(self, node):
  33.255 +        self.body.append(self.encode(node.astext()))
  33.256 +
  33.257 +    def depart_Text(self, node):
  33.258 +        pass
  33.259 +
  33.260 +    def visit_attention(self, node):
  33.261 +        self.body.append(self.starttag(node, 'note'))
  33.262 +        self.body.append('\n<title>%s</title>\n' 
  33.263 +            % (self.language.labels[node.tagname],))
  33.264 +
  33.265 +    def depart_attention(self, node):
  33.266 +        self.body.append('</note>\n')
  33.267 +
  33.268 +    # author is handled in ``visit_docinfo()``
  33.269 +    def visit_author(self, node):
  33.270 +        raise nodes.SkipNode
  33.271 +
  33.272 +    # authors is handled in ``visit_docinfo()``
  33.273 +    def visit_authors(self, node):
  33.274 +        raise nodes.SkipNode
  33.275 +
  33.276 +    def visit_block_quote(self, node):
  33.277 +        self.body.append(self.starttag(node, 'blockquote'))
  33.278 +
  33.279 +    def depart_block_quote(self, node):
  33.280 +        self.body.append('</blockquote>\n')
  33.281 +
  33.282 +    def visit_bullet_list(self, node):
  33.283 +        self.body.append(self.starttag(node, 'itemizedlist'))
  33.284 +
  33.285 +    def depart_bullet_list(self, node):
  33.286 +        self.body.append('</itemizedlist>\n')
  33.287 +
  33.288 +    def visit_caption(self, node):
  33.289 +        # NOTE: ideally, this should probably be stuffed into
  33.290 +        # the mediaobject as a "caption" element
  33.291 +        self.body.append(self.starttag(node, 'para'))
  33.292 +
  33.293 +    def depart_caption(self, node):
  33.294 +        self.body.append('</para>')
  33.295 +
  33.296 +    def visit_caution(self, node):
  33.297 +        self.body.append(self.starttag(node, 'caution'))
  33.298 +        self.body.append('\n<title>%s</title>\n' 
  33.299 +            % (self.language.labels[node.tagname],))
  33.300 +
  33.301 +    def depart_caution(self, node):
  33.302 +        self.body.append('</caution>\n')
  33.303 +
  33.304 +    # reST seems to handle citations as a labled
  33.305 +    # footnotes, whereas DocBook doesn't from what
  33.306 +    # I can tell, so I'm not sure how to give DocBook
  33.307 +    # citations that result in equivlent output
  33.308 +    # as the docutils html writer.
  33.309 +    #
  33.310 +    # Currently, citations are handled as footnotes,
  33.311 +    # using the citation label as the footnote label
  33.312 +    # which seems functionally equivlent, but the
  33.313 +    # DocBook stylesheets for generating HTML output
  33.314 +    # don't seem to be using the label for foonotes
  33.315 +    # so this doesn't work.
  33.316 +    #
  33.317 +    # So I'm at a bit of a loss as to how to 
  33.318 +    # handle citations. Any ideas or suggestions would
  33.319 +    # be welcome.
  33.320 +
  33.321 +    # TODO: citation
  33.322 +    def visit_citation(self, node):
  33.323 +        self.visit_footnote(node)
  33.324 +
  33.325 +    def depart_citation(self, node):
  33.326 +        self.depart_footnote(node)
  33.327 +
  33.328 +    # TODO: citation_reference
  33.329 +    def visit_citation_reference(self, node):
  33.330 +        self.visit_footnote_reference(node)
  33.331 +
  33.332 +    def depart_citation_reference(self, node):
  33.333 +        pass
  33.334 +
  33.335 +    def visit_classifier(self, node):
  33.336 +        self.body.append(' : ')
  33.337 +        self.body.append(self.starttag(node, 'type'))
  33.338 +
  33.339 +    def depart_classifier(self, node):
  33.340 +        self.body.append('</type>\n')
  33.341 +
  33.342 +    def visit_colspec(self, node):
  33.343 +        self.colnames.append('col_%d' % (len(self.colnames) + 1,))
  33.344 +        atts = {'colname': self.colnames[-1]}
  33.345 +        self.body.append(self.emptytag(node, 'colspec', **atts))
  33.346 +
  33.347 +    def depart_colspec(self, node):
  33.348 +        pass
  33.349 +
  33.350 +    def visit_comment(self, node, sub=re.compile('-(?=-)').sub):
  33.351 +        """Escape double-dashes in comment text."""
  33.352 +        self.body.append('<!-- %s -->\n' % sub('- ', node.astext()))
  33.353 +        raise nodes.SkipNode
  33.354 +
  33.355 +    # contact is handled in ``visit_docinfo()``
  33.356 +    def visit_contact(self, node):
  33.357 +        raise nodes.SkipNode
  33.358 +
  33.359 +    # copyright is handled in ``visit_docinfo()``
  33.360 +    def visit_copyright(self, node):
  33.361 +        raise nodes.SkipNode
  33.362 +
  33.363 +    def visit_danger(self, node):
  33.364 +        self.body.append(self.starttag(node, 'caution'))
  33.365 +        self.body.append('\n<title>%s</title>\n' 
  33.366 +            % (self.language.labels[node.tagname],))
  33.367 +
  33.368 +    def depart_danger(self, node):
  33.369 +        self.body.append('</caution>\n')
  33.370 +
  33.371 +    # date is handled in ``visit_docinfo()``
  33.372 +    def visit_date(self, node):
  33.373 +        raise nodes.SkipNode
  33.374 +
  33.375 +    # TODO: decoration
  33.376 +    def visit_decoration(self, node):
  33.377 +        pass
  33.378 +
  33.379 +    def depart_decoration(self, node):
  33.380 +        pass
  33.381 +
  33.382 +    def visit_definition(self, node):
  33.383 +        # "term" is not closed in depart_term
  33.384 +        self.body.append('</term>\n')
  33.385 +        self.body.append(self.starttag(node, 'listitem'))
  33.386 +
  33.387 +    def depart_definition(self, node):
  33.388 +        self.body.append('</listitem>\n')
  33.389 +
  33.390 +    def visit_definition_list(self, node):
  33.391 +        self.body.append(self.starttag(node, 'variablelist'))
  33.392 +
  33.393 +    def depart_definition_list(self, node):
  33.394 +        self.body.append('</variablelist>\n')
  33.395 +
  33.396 +    def visit_definition_list_item(self, node):
  33.397 +        self.body.append(self.starttag(node, 'varlistentry'))
  33.398 +
  33.399 +    def depart_definition_list_item(self, node):
  33.400 +        self.body.append('</varlistentry>\n')
  33.401 +
  33.402 +    def visit_description(self, node):
  33.403 +        self.body.append(self.starttag(node, 'entry'))
  33.404 +
  33.405 +    def depart_description(self, node):
  33.406 +        self.body.append('</entry>\n')
  33.407 +
  33.408 +    def visit_docinfo(self, node):
  33.409 +        """
  33.410 +        Collects all docinfo elements for the document.
  33.411 +
  33.412 +        Since reST's bibliography elements don't map very
  33.413 +        cleanly to DocBook, rather than maintain state and
  33.414 +        check dependencies within the different visitor
  33.415 +        fuctions all processing of bibliography elements
  33.416 +        is dont within this function.
  33.417 +
  33.418 +        .. NOTE:: Skips processing of all child nodes as
  33.419 +                  everything should be collected here.
  33.420 +        """
  33.421 +
  33.422 +        # XXX There are a number of fields in docinfo elements
  33.423 +        #     which don't map nicely to docbook elements and 
  33.424 +        #     reST allows one to insert arbitrary fields into
  33.425 +        #     the header, We need to be able to handle fields
  33.426 +        #     which either don't map or nicely or are unexpected.
  33.427 +        #     I'm thinking of just using DocBook to display these
  33.428 +        #     elements in some sort of tabular format -- but
  33.429 +        #     to collecting them is not straight-forward.  
  33.430 +        #     Paragraphs, links, lists, etc... can all live within
  33.431 +        #     the values so we either need a separate visitor
  33.432 +        #     to translate these elements, or to maintain state
  33.433 +        #     in any possible child elements (not something I
  33.434 +        #     want to do).
  33.435 +
  33.436 +        docinfo = ['<%sinfo>\n' % self.doctype]
  33.437 +
  33.438 +        authors = []
  33.439 +        author = ''
  33.440 +        contact = ''
  33.441 +        date = ''
  33.442 +        legalnotice = ''
  33.443 +        orgname = ''
  33.444 +        releaseinfo = ''
  33.445 +        revision, version = '', ''
  33.446 +
  33.447 +        for n in node:
  33.448 +            if isinstance(n, nodes.author):
  33.449 +                author = n.astext()
  33.450 +            elif isinstance(n, nodes.authors):
  33.451 +                for a in n:
  33.452 +                    authors.append(a.astext())
  33.453 +            elif isinstance(n, nodes.contact):
  33.454 +                contact = n.astext()
  33.455 +            elif isinstance(n, nodes.copyright):
  33.456 +                legalnotice = n.astext()
  33.457 +            elif isinstance(n, nodes.date):
  33.458 +                date = n.astext()
  33.459 +            elif isinstance(n, nodes.organization):
  33.460 +                orgname = n.astext()
  33.461 +            elif isinstance(n, nodes.revision):
  33.462 +                revision = 'Revision ' + n.astext()
  33.463 +            elif isinstance(n, nodes.status):
  33.464 +                releaseinfo = n.astext()
  33.465 +            elif isinstance(n, nodes.version):
  33.466 +                version = 'Version ' + n.astext()
  33.467 +            elif isinstance(n, nodes.field):
  33.468 +                # XXX
  33.469 +                import sys
  33.470 +                print >> sys.stderr, "I don't do 'field' yet"
  33.471 +            # since all child nodes are handled here raise an exception
  33.472 +            # if node is not handled, so it doesn't silently slip through.
  33.473 +            else:
  33.474 +                print dir(n)
  33.475 +                print n.astext()
  33.476 +                raise self.unimplemented_visit(n)
  33.477 +
  33.478 +        # can only add author if name is present
  33.479 +        # since contact is associate with author, the contact
  33.480 +        # can also only be added if an author name is given.
  33.481 +        if author:
  33.482 +            docinfo.append('<author>\n')
  33.483 +            docinfo.append('<othername>%s</othername>\n' % author)
  33.484 +            if contact:
  33.485 +                docinfo.append('<email>%s</email>\n' % contact)
  33.486 +            docinfo.append('</author>\n')
  33.487 +
  33.488 +        if authors:
  33.489 +            docinfo.append('<authorgroup>\n')
  33.490 +            for name in authors:
  33.491 +                docinfo.append(
  33.492 +                    '<author><othername>%s</othername></author>\n' % name)
  33.493 +            docinfo.append('</authorgroup>\n')
  33.494 +
  33.495 +        if revision or version:
  33.496 +            edition = version
  33.497 +            if edition and revision:
  33.498 +                edition += ', ' + revision
  33.499 +            elif revision:
  33.500 +                edition = revision
  33.501 +            docinfo.append('<edition>%s</edition>\n' % edition)
  33.502 +
  33.503 +        if date:
  33.504 +            docinfo.append('<date>%s</date>\n' % date)
  33.505 +
  33.506 +        if orgname:
  33.507 +            docinfo.append('<orgname>%s</orgname>\n' % orgname)
  33.508 +
  33.509 +        if releaseinfo:
  33.510 +            docinfo.append('<releaseinfo>%s</releaseinfo>\n' % releaseinfo)
  33.511 +
  33.512 +        if legalnotice:
  33.513 +            docinfo.append('<legalnotice>\n')
  33.514 +            docinfo.append('<para>%s</para>\n' % legalnotice)
  33.515 +            docinfo.append('</legalnotice>\n')
  33.516 +
  33.517 +        if len(docinfo) > 1:
  33.518 +            docinfo.append('</%sinfo>\n' % self.doctype)
  33.519 +
  33.520 +        self.docinfo = docinfo
  33.521 +
  33.522 +        raise nodes.SkipChildren
  33.523 +
  33.524 +    def depart_docinfo(self, node):
  33.525 +        pass
  33.526 +
  33.527 +    def visit_doctest_block(self, node):
  33.528 +        self.body.append('<informalexample>\n')
  33.529 +        self.body.append(self.starttag(node, 'programlisting'))
  33.530 +
  33.531 +    def depart_doctest_block(self, node):
  33.532 +        self.body.append('</programlisting>\n')
  33.533 +        self.body.append('</informalexample>\n')
  33.534 +
  33.535 +    def visit_document(self, node):
  33.536 +        pass
  33.537 +
  33.538 +    def depart_document(self, node):
  33.539 +        self.rearrange_footnotes()
  33.540 +
  33.541 +    def visit_emphasis(self, node):
  33.542 +        #self.body.append(self.starttag(node, 'emphasis'))  # XXX
  33.543 +        self.body.append('<emphasis>')
  33.544 +
  33.545 +    def depart_emphasis(self, node):
  33.546 +        self.body.append('</emphasis>')
  33.547 +
  33.548 +    def visit_entry(self, node):
  33.549 +        tagname = 'entry'
  33.550 +        atts = {}
  33.551 +        if node.has_key('morerows'):
  33.552 +            atts['morerows'] = node['morerows']
  33.553 +        if node.has_key('morecols'):
  33.554 +            atts['namest'] = self.colnames[self.entry_level]
  33.555 +            atts['nameend'] = self.colnames[self.entry_level \
  33.556 +                + node['morecols']]
  33.557 +        self.entry_level += 1   # for tracking what namest and nameend are
  33.558 +        self.body.append(self.starttag(node, tagname, '', **atts))
  33.559 +
  33.560 +    def depart_entry(self, node):
  33.561 +        self.body.append('</entry>\n')
  33.562 +
  33.563 +    def visit_enumerated_list(self, node):
  33.564 +        # TODO: need to specify "mark" type used for list items
  33.565 +        self.body.append(self.starttag(node, 'orderedlist'))
  33.566 +
  33.567 +    def depart_enumerated_list(self, node):
  33.568 +        self.body.append('</orderedlist>\n')
  33.569 +
  33.570 +    def visit_error(self, node):
  33.571 +        self.body.append(self.starttag(node, 'caution'))
  33.572 +        self.body.append('\n<title>%s</title>\n' 
  33.573 +            % (self.language.labels[node.tagname],))
  33.574 +
  33.575 +    def depart_error(self, node):
  33.576 +        self.body.append('</caution>\n')
  33.577 +
  33.578 +    # TODO: wrap with some element (filename used in DocBook example)
  33.579 +    def visit_field(self, node):
  33.580 +        self.body.append(self.starttag(node, 'varlistentry'))
  33.581 +
  33.582 +    def depart_field(self, node):
  33.583 +        self.body.append('</varlistentry>\n')
  33.584 +
  33.585 +    # TODO: see if this should be wrapped with some element
  33.586 +    def visit_field_argument(self, node):
  33.587 +        self.body.append(' ')
  33.588 +
  33.589 +    def depart_field_argument(self, node):
  33.590 +        pass
  33.591 +
  33.592 +    def visit_field_body(self, node):
  33.593 +        # NOTE: this requires that a field body always
  33.594 +        #   be present, which looks like the case
  33.595 +        #   (from docutils.dtd)
  33.596 +        self.body.append(self.context.pop())
  33.597 +        self.body.append(self.starttag(node, 'listitem'))
  33.598 +
  33.599 +    def depart_field_body(self, node):
  33.600 +        self.body.append('</listitem>\n')
  33.601 +
  33.602 +    def visit_field_list(self, node):
  33.603 +        self.body.append(self.starttag(node, 'variablelist'))
  33.604 +
  33.605 +    def depart_field_list(self, node):
  33.606 +        self.body.append('</variablelist>\n')
  33.607 +
  33.608 +    def visit_field_name(self, node):
  33.609 +        self.body.append(self.starttag(node, 'term'))
  33.610 +        # popped by visit_field_body, so "field_argument" is
  33.611 +        # content within "term"
  33.612 +        self.context.append('</term>\n')
  33.613 +
  33.614 +    def depart_field_name(self, node):
  33.615 +        pass
  33.616 +
  33.617 +    def visit_figure(self, node):
  33.618 +        self.body.append(self.starttag(node, 'informalfigure'))
  33.619 +        self.body.append('<blockquote>')
  33.620 +
  33.621 +    def depart_figure(self, node):
  33.622 +        self.body.append('</blockquote>')
  33.623 +        self.body.append('</informalfigure>\n')
  33.624 +
  33.625 +    # TODO: footer (this is where 'generated by docutils' arrives)
  33.626 +    # if that's all that will be there, it could map to "colophon"
  33.627 +    def visit_footer(self, node):
  33.628 +        raise nodes.SkipChildren
  33.629 +
  33.630 +    def depart_footer(self, node):
  33.631 +        pass
  33.632 +
  33.633 +    def visit_footnote(self, node):
  33.634 +        self.footnotes[node['id']] = []
  33.635 +        atts = {'id': node['id']}
  33.636 +        if isinstance(node[0], nodes.label):
  33.637 +            # FIXME: this fails with the second auto-sequenece character
  33.638 +            # used in the test document ``test.txt``.
  33.639 +            atts['label'] = node[0].astext()
  33.640 +        self.footnotes[node['id']].append(
  33.641 +            self.starttag(node, 'footnote', **atts))
  33.642 +
  33.643 +        # replace body with this with a footnote collector list
  33.644 +        # which will hold all the contents for this footnote.
  33.645 +        # This needs to be kept separate so it can be used to replace
  33.646 +        # the first ``footnote_reference`` as DocBook defines 
  33.647 +        # ``footnote`` elements inline. 
  33.648 +        self._body = self.body
  33.649 +        self.body = self.footnotes[node['id']]
  33.650 +
  33.651 +    def depart_footnote(self, node):
  33.652 +        # finish footnote and then replace footnote collector
  33.653 +        # with real body list.
  33.654 +        self.footnotes[node['id']].append('</footnote>\n')
  33.655 +        self.body = self._body
  33.656 +        self._body = None
  33.657 +
  33.658 +    def visit_footnote_reference(self, node):
  33.659 +        if node.has_key('refid'):
  33.660 +            refid = node['refid']
  33.661 +        else:
  33.662 +            refid = self.document.nameids[node['refname']]
  33.663 +
  33.664 +        # going to replace this footnote reference with the actual
  33.665 +        # footnote later on, so store the footnote id to replace
  33.666 +        # this reference with and the list and position to replace it
  33.667 +        # in. Both list and position are stored in case a footnote
  33.668 +        # reference is within a footnote, in which case ``self.body``
  33.669 +        # won't really be ``self.body`` but a footnote collector
  33.670 +        # list.
  33.671 +        refs = self.footnote_map.get(refid, [])
  33.672 +        refs.append((node['id'], self.body, len(self.body),))
  33.673 +        self.footnote_map[refid] = refs
  33.674 +
  33.675 +        # add place holder list item which should later be 
  33.676 +        # replaced with the contents of the footnote element
  33.677 +        # and it's child elements
  33.678 +        self.body.append('<!-- REPLACE WITH FOOTNOTE -->')
  33.679 +
  33.680 +        raise nodes.SkipNode
  33.681 +
  33.682 +    # TODO: header
  33.683 +
  33.684 +    # ??? does anything need to be done for generated?
  33.685 +    def visit_generated(self, node):
  33.686 +        pass
  33.687 +
  33.688 +    def depart_generated(self, node):
  33.689 +        pass
  33.690 +
  33.691 +    def visit_hint(self, node):
  33.692 +        self.body.append(self.starttag(node, 'note'))
  33.693 +        self.body.append('\n<title>%s</title>\n' 
  33.694 +            % (self.language.labels[node.tagname],))
  33.695 +
  33.696 +    def depart_hint(self, node):
  33.697 +        self.body.append('</note>\n')
  33.698 +
  33.699 +    def visit_image(self, node):
  33.700 +        atts = node.attributes.copy()
  33.701 +        atts['fileref'] = atts['uri']
  33.702 +        alt = None
  33.703 +        del atts['uri']
  33.704 +        if atts.has_key('alt'):
  33.705 +            alt = atts['alt']
  33.706 +            del atts['alt']
  33.707 +        if atts.has_key('height'):
  33.708 +            atts['depth'] = atts['height']
  33.709 +            del atts['height']
  33.710 +        # NOTE: using win32 port of xsltproc and docbook-stylesheets-1.51.1
  33.711 +        # I'm getting the following error when transforming:
  33.712 +        # Error C:\home\igor\src\gnome-xml\xpath.c:8023: Undefined 
  33.713 +        # namespace prefix xmlXPathCompiledEval: evaluation failed
  33.714 +        # When I switched to version 1.49 of the docbook-stylesheets
  33.715 +        # I didn't have this problem.
  33.716 +        self.body.append('<mediaobject>')
  33.717 +        self.body.append('<imageobject>')
  33.718 +        self.body.append(self.emptytag(node, 'imagedata', **atts))
  33.719 +        self.body.append('</imageobject>')
  33.720 +        if alt:
  33.721 +            self.body.append('<textobject><phrase>' \
  33.722 +                '%s</phrase></textobject>\n' % alt)
  33.723 +        self.body.append('</mediaobject>')
  33.724 +
  33.725 +    def depart_image(self, node):
  33.726 +        pass
  33.727 +
  33.728 +    def visit_important(self, node):
  33.729 +        self.body.append(self.starttag(node, 'important'))
  33.730 +
  33.731 +    def depart_important(self, node):
  33.732 +        self.body.append('</important>')
  33.733 +
  33.734 +    # @@@ Incomplete, pending a proper implementation on the
  33.735 +    # Parser/Reader end.
  33.736 +    def visit_interpreted(self, node):
  33.737 +        self.body.append('<constant>\n')
  33.738 +
  33.739 +    def depart_interpreted(self, node):
  33.740 +        self.body.append('</constant>\n')
  33.741 +
  33.742 +    def visit_label(self, node):
  33.743 +        # getting label for "footnote" in ``visit_footnote``
  33.744 +        # because label is an attribute for the ``footnote``
  33.745 +        # element.
  33.746 +        if isinstance(node.parent, nodes.footnote):
  33.747 +            raise nodes.SkipNode
  33.748 +        # TODO: handle citation label
  33.749 +        elif isinstance(node.parent, nodes.citation):
  33.750 +            raise nodes.SkipNode
  33.751 +
  33.752 +    def depart_label(self, node):
  33.753 +        pass
  33.754 +
  33.755 +    def visit_legend(self, node):
  33.756 +        # TODO: explain why this is empty....
  33.757 +        pass
  33.758 +
  33.759 +    def depart_legend(self, node):
  33.760 +        pass
  33.761 +
  33.762 +    def visit_line_block(self, node):
  33.763 +        self.body.append(self.starttag(node, 'literallayout'))
  33.764 +
  33.765 +    def depart_line_block(self, node):
  33.766 +        self.body.append('</literallayout>\n')
  33.767 +
  33.768 +    def visit_list_item(self, node):
  33.769 +        self.body.append(self.starttag(node, 'listitem'))
  33.770 +
  33.771 +    def depart_list_item(self, node):
  33.772 +        self.body.append('</listitem>\n')
  33.773 +
  33.774 +    def visit_literal(self, node):
  33.775 +         self.body.append('<literal>')
  33.776 +
  33.777 +    def depart_literal(self, node):
  33.778 +        self.body.append('</literal>')
  33.779 +
  33.780 +    def visit_literal_block(self, node):
  33.781 +        self.body.append(self.starttag(node, 'programlisting'))
  33.782 +
  33.783 +    def depart_literal_block(self, node):
  33.784 +        self.body.append('\n</programlisting>\n')
  33.785 +
  33.786 +    def visit_note(self, node):
  33.787 +        self.body.append(self.starttag(node, 'note'))
  33.788 +        self.body.append('\n<title>%s</title>\n' 
  33.789 +            % (self.language.labels[node.tagname],))
  33.790 +
  33.791 +    def depart_note(self, node):
  33.792 +        self.body.append('</note>\n')
  33.793 +
  33.794 +    def visit_option(self, node):
  33.795 +        self.body.append(self.starttag(node, 'command'))
  33.796 +        if self.context[-1]:
  33.797 +            self.body.append(', ')
  33.798 +
  33.799 +    def depart_option(self, node):
  33.800 +        self.context[-1] += 1
  33.801 +        self.body.append('</command>')
  33.802 +
  33.803 +    def visit_option_argument(self, node):
  33.804 +        self.body.append(node.get('delimiter', ' '))
  33.805 +        self.body.append(self.starttag(node, 'replaceable', ''))
  33.806 +
  33.807 +    def depart_option_argument(self, node):
  33.808 +        self.body.append('</replaceable>')
  33.809 +
  33.810 +    def visit_option_group(self, node):
  33.811 +        self.body.append(self.starttag(node, 'entry'))
  33.812 +        self.context.append(0)
  33.813 +
  33.814 +    def depart_option_group(self, node):
  33.815 +        self.context.pop()
  33.816 +        self.body.append('</entry>\n')
  33.817 +
  33.818 +    def visit_option_list(self, node):
  33.819 +        self.body.append(self.starttag(node, 'informaltable', frame='all'))
  33.820 +        self.body.append('<tgroup cols="2">\n')
  33.821 +        self.body.append('<colspec colname="option_col"/>\n')
  33.822 +        self.body.append('<colspec colname="description_col"/>\n')
  33.823 +        self.body.append('<thead>\n')
  33.824 +        self.body.append('<row>\n')
  33.825 +        # FIXME: shouldn't hardcode everything...
  33.826 +        self.body.append('<entry align="center">Option</entry>\n')
  33.827 +        self.body.append('<entry align="center">Description</entry>\n')
  33.828 +        self.body.append('</row>\n')
  33.829 +        self.body.append('</thead>\n')
  33.830 +        self.body.append('<tbody>\n')
  33.831 +
  33.832 +    def depart_option_list(self, node):
  33.833 +        self.body.append('</tbody>')
  33.834 +        self.body.append('</tgroup>\n')
  33.835 +        self.body.append('</informaltable>\n')
  33.836 +
  33.837 +    def visit_option_list_item(self, node):
  33.838 +        self.body.append(self.starttag(node, 'row'))
  33.839 +
  33.840 +    def depart_option_list_item(self, node):
  33.841 +        self.body.append('</row>\n')
  33.842 +
  33.843 +    def visit_option_string(self, node):
  33.844 +        pass
  33.845 +
  33.846 +    def depart_option_string(self, node):
  33.847 +        pass
  33.848 +
  33.849 +    # organization is handled in ``visit_docinfo()``
  33.850 +    def visit_organization(self, node):
  33.851 +        raise nodes.SkipNode
  33.852 +
  33.853 +    def visit_paragraph(self, node):
  33.854 +        self.body.append(self.starttag(node, 'para', ''))
  33.855 +
  33.856 +    def depart_paragraph(self, node):
  33.857 +        self.body.append('</para>\n')
  33.858 +
  33.859 +    # TODO: problematic
  33.860 +    visit_problematic = depart_problematic = lambda self, node: None
  33.861 +
  33.862 +    def visit_raw(self, node):
  33.863 +        if node.has_key('format') and node['format'] == 'docbook':
  33.864 +            self.body.append(node.astext())
  33.865 +        raise nodes.SkipNode
  33.866 +
  33.867 +    def visit_reference(self, node):
  33.868 +        atts = {}
  33.869 +        if node.has_key('refuri'):
  33.870 +            atts['url'] = node['refuri']
  33.871 +            self.context.append('ulink')
  33.872 +        elif node.has_key('refid'):
  33.873 +            atts['linkend'] = node['refid']
  33.874 +            self.context.append('link')
  33.875 +        elif node.has_key('refname'):
  33.876 +            atts['linkend'] = self.document.nameids[node['refname']]
  33.877 +            self.context.append('link')
  33.878 +        self.body.append(self.starttag(node, self.context[-1], '', **atts))
  33.879 +
  33.880 +    def depart_reference(self, node):
  33.881 +        self.body.append('</%s>' % (self.context.pop(),))
  33.882 +
  33.883 +    # revision is handled in ``visit_docinfo()``
  33.884 +    def visit_revision(self, node):
  33.885 +        raise nodes.SkipNode
  33.886 +
  33.887 +    def visit_row(self, node):
  33.888 +        self.entry_level = 0
  33.889 +        self.body.append(self.starttag(node, 'row'))
  33.890 +
  33.891 +    def depart_row(self, node):
  33.892 +        self.body.append('</row>\n')
  33.893 +
  33.894 +    def visit_section(self, node):
  33.895 +        if self.section == 0 and self.doctype == 'book':
  33.896 +            self.body.append(self.starttag(node, 'chapter'))
  33.897 +        else:
  33.898 +            self.body.append(self.starttag(node, 'section'))
  33.899 +        self.section += 1
  33.900 +
  33.901 +    def depart_section(self, node):
  33.902 +        self.section -= 1
  33.903 +        if self.section == 0 and self.doctype == 'book':
  33.904 +            self.body.append('</chapter>\n')
  33.905 +        else:
  33.906 +            self.body.append('</section>\n')
  33.907 +
  33.908 +    def visit_sidebar(self, node):
  33.909 +        self.body.append(self.starttag(node, 'sidebar'))
  33.910 +
  33.911 +    def depart_sidebar(self, node):
  33.912 +        self.body.append('</sidebar>\n')
  33.913 +
  33.914 +    # author is handled in ``visit_docinfo()``
  33.915 +    def visit_status(self, node):
  33.916 +        raise nodes.SkipNode
  33.917 +
  33.918 +    def visit_strong(self, node):
  33.919 +        # self.body.append(self.starttag(node, 'emphasis', role='strong')) # XXX
  33.920 +        self.body.append('<emphasis role="strong">')
  33.921 +
  33.922 +    def depart_strong(self, node):
  33.923 +        self.body.append('</emphasis>')
  33.924 +
  33.925 +    def visit_substitution_definition(self, node):
  33.926 +        raise nodes.SkipNode
  33.927 +
  33.928 +    def visit_substitution_reference(self, node):
  33.929 +        self.unimplemented_visit(node)
  33.930 +
  33.931 +    def visit_subtitle(self, node):
  33.932 +        self.body.append(self.starttag(node, 'subtitle'))
  33.933 +
  33.934 +    def depart_subtitle(self, node):
  33.935 +        self.body.append('</subtitle>\n')
  33.936 +        if isinstance(node.parent, nodes.sidebar):
  33.937 +            self.body.append('</sidebarinfo>\n')
  33.938 +
  33.939 +    # TODO: system_message
  33.940 +    visit_system_message = depart_system_message = lambda self, node: None
  33.941 +
  33.942 +    def visit_table(self, node):
  33.943 +        self.body.append(
  33.944 +            self.starttag(node, 'informaltable', frame='all')
  33.945 +        )
  33.946 +
  33.947 +    def depart_table(self, node):
  33.948 +        self.body.append('</informaltable>\n')
  33.949 +
  33.950 +    # TODO: target
  33.951 +    visit_target = depart_target = lambda self,node: None
  33.952 +
  33.953 +    def visit_tbody(self, node):
  33.954 +        self.body.append(self.starttag(node, 'tbody'))
  33.955 +
  33.956 +    def depart_tbody(self, node):
  33.957 +        self.body.append('</tbody>\n')
  33.958 +
  33.959 +    def visit_term(self, node):
  33.960 +        self.body.append(self.starttag(node, 'term'))
  33.961 +        self.body.append('<varname>')
  33.962 +
  33.963 +    def depart_term(self, node):
  33.964 +        # Leave the end tag "term" to ``visit_definition()``,
  33.965 +        # in case there's a classifier.
  33.966 +        self.body.append('</varname>')
  33.967 +
  33.968 +    def visit_tgroup(self, node):
  33.969 +        self.colnames = []
  33.970 +        atts = {'cols': node['cols']}
  33.971 +        self.body.append(self.starttag(node, 'tgroup', **atts))
  33.972 +
  33.973 +    def depart_tgroup(self, node):
  33.974 +        self.body.append('</tgroup>\n')
  33.975 +
  33.976 +    def visit_thead(self, node):
  33.977 +        self.body.append(self.starttag(node, 'thead'))
  33.978 +
  33.979 +    def depart_thead(self, node):
  33.980 +        self.body.append('</thead>\n')
  33.981 +
  33.982 +    def visit_tip(self, node):
  33.983 +        self.body.append(self.starttag(node, 'tip'))
  33.984 +
  33.985 +    def depart_tip(self, node):
  33.986 +        self.body.append('</tip>\n')
  33.987 +
  33.988 +    def visit_title(self, node):
  33.989 +        # HACK: sidebar subtitle needs to go into sidebarinfo
  33.990 +        # so it's easier to put the title in there too
  33.991 +        if isinstance(node.parent, nodes.sidebar):
  33.992 +            if isinstance(node.parent.children[1], nodes.subtitle):
  33.993 +                self.body.append('<sidebarinfo>')
  33.994 +        self.body.append(self.starttag(node, 'title', ''))
  33.995 +
  33.996 +    def depart_title(self, node):
  33.997 +        self.body.append('</title>\n')
  33.998 +
  33.999 +    # XXX just a hack for now
 33.1000 +    def visit_title_reference(self, node):
 33.1001 +        #self.body.append(self.starttag(node, 'emphasis'))  # XXX
 33.1002 +        self.body.append('<emphasis>')
 33.1003 +
 33.1004 +    # XXX just a hack for now
 33.1005 +    def depart_title_reference(self, node):
 33.1006 +        self.body.append('</emphasis>')
 33.1007 +
 33.1008 +    def visit_topic(self, node):
 33.1009 +        # TODO: map dedication to dedication
 33.1010 +
 33.1011 +        # Table of Contents generation handled by DocBook
 33.1012 +        if node.get('class') == 'contents':
 33.1013 +            raise nodes.SkipChildren
 33.1014 +        elif node.get('class') == 'abstract':
 33.1015 +            self.body.append(self.starttag(node, 'abstract'))
 33.1016 +            self.context.append('abstract')
 33.1017 +        # generic "topic" element 
 33.1018 +        # XXX I don't really know what else to do with it.
 33.1019 +        elif node.get('class','') == '':
 33.1020 +            self.body.append(self.starttag(node, 'section'))
 33.1021 +            self.context.append('section')
 33.1022 +        else:
 33.1023 +            # XXX debug code
 33.1024 +            print 'class:', node.get('class')
 33.1025 +            print node.__class__.__name__
 33.1026 +            print node
 33.1027 +            print `node`
 33.1028 +            print dir(node)
 33.1029 +            self.unimplemented_visit(node)
 33.1030 +
 33.1031 +    def depart_topic(self, node):
 33.1032 +        if len(self.context):
 33.1033 +            self.body.append('</%s>\n' % (self.context.pop(),))
 33.1034 +
 33.1035 +    # QUESTION: what to do for "transition"?
 33.1036 +    def visit_transition(self, node):
 33.1037 +        pass
 33.1038 +
 33.1039 +    def depart_transition(self, node):
 33.1040 +        pass
 33.1041 +
 33.1042 +    # author is handled in ``visit_docinfo()``
 33.1043 +    def visit_version(self, node):
 33.1044 +        raise nodes.SkipNode
 33.1045 +
 33.1046 +    def visit_warning(self, node):
 33.1047 +        self.body.append(self.starttag(node, 'warning'))
 33.1048 +
 33.1049 +    def depart_warning(self, node):
 33.1050 +        self.body.append('</warning>\n')
 33.1051 +
 33.1052 +    def unimplemented_visit(self, node):
 33.1053 +        raise NotImplementedError('visiting unimplemented node type: %s'
 33.1054 +                % node.__class__.__name__)
 33.1055 +
 33.1056 +# :collapseFolds=0:folding=sidekick:indentSize=4:
 33.1057 +# :lineSeparator=\n:noTabs=true:tabSize=4:
    34.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    34.2 +++ b/setup.cfg	Wed Apr 26 10:48:09 2006 +0000
    34.3 @@ -0,0 +1,4 @@
    34.4 +[bdist_rpm]
    34.5 +release = 0.1
    34.6 +packager = Sylvain Thénault  <sylvain.thenault@logilab.fr>
    34.7 +provides = doctools
    35.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    35.2 +++ b/setup.py	Wed Apr 26 10:48:09 2006 +0000
    35.3 @@ -0,0 +1,185 @@
    35.4 +#!/usr/bin/env python
    35.5 +# pylint: disable-msg=W0142, W0403,W0404, W0613,W0622,W0622, W0704, R0904
    35.6 +#
    35.7 +# Copyright (c) 2003 LOGILAB S.A. (Paris, FRANCE).
    35.8 +# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    35.9 +#
   35.10 +# This program is free software; you can redistribute it and/or modify it under
   35.11 +# the terms of the GNU General Public License as published by the Free Software
   35.12 +# Foundation; either version 2 of the License, or (at your option) any later
   35.13 +# version.
   35.14 +#
   35.15 +# This program is distributed in the hope that it will be useful, but WITHOUT
   35.16 +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   35.17 +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
   35.18 +#
   35.19 +# You should have received a copy of the GNU General Public License along with
   35.20 +# this program; if not, write to the Free Software Foundation, Inc.,
   35.21 +# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   35.22 +""" Generic Setup script, takes package info from __pkginfo__.py file """
   35.23 +
   35.24 +__revision__ = '$Id: setup.py,v 1.7 2005-03-29 12:17:21 syt Exp $'
   35.25 +
   35.26 +from __future__ import nested_scopes
   35.27 +import os
   35.28 +import sys
   35.29 +import shutil
   35.30 +from distutils.core import setup
   35.31 +from distutils.command import install_lib
   35.32 +from os.path import isdir, exists, join, walk
   35.33 +
   35.34 +# import required features
   35.35 +from __pkginfo__ import modname, version, license, short_desc, long_desc, \
   35.36 +     web, author, author_email
   35.37 +# import optional features
   35.38 +try:
   35.39 +    from __pkginfo__ import distname
   35.40 +except ImportError:
   35.41 +    distname = modname
   35.42 +try:
   35.43 +    from __pkginfo__ import scripts
   35.44 +except ImportError:
   35.45 +    scripts = []
   35.46 +try:
   35.47 +    from __pkginfo__ import data_files
   35.48 +except ImportError:
   35.49 +    data_files = None
   35.50 +try:
   35.51 +    from __pkginfo__ import subpackage_of
   35.52 +except ImportError:
   35.53 +    subpackage_of = None
   35.54 +try:
   35.55 +    from __pkginfo__ import include_dirs
   35.56 +except ImportError:
   35.57 +    include_dirs = []
   35.58 +try:
   35.59 +    from __pkginfo__ import ext_modules
   35.60 +except ImportError:
   35.61 +    ext_modules = None
   35.62 +
   35.63 +BASE_BLACKLIST = ('CVS', 'debian', 'dist', 'build', '__buildlog')
   35.64 +IGNORED_EXTENSIONS = ('.pyc', '.pyo', '.elc')
   35.65 +    
   35.66 +
   35.67 +def ensure_scripts(linux_scripts):
   35.68 +    """
   35.69 +    Creates the proper script names required for each platform
   35.70 +    (taken from 4Suite)
   35.71 +    """
   35.72 +    from distutils import util
   35.73 +    if util.get_platform()[:3] == 'win':
   35.74 +        scripts_ = [script + '.bat' for script in linux_scripts]
   35.75 +    else:
   35.76 +        scripts_ = linux_scripts
   35.77 +    return scripts_
   35.78 +
   35.79 +
   35.80 +def get_packages(directory, prefix):
   35.81 +    """return a list of subpackages for the given directory
   35.82 +    """
   35.83 +    result = []
   35.84 +    for package in os.listdir(directory):
   35.85 +        absfile = join(directory, package)
   35.86 +        if isdir(absfile):
   35.87 +            if exists(join(absfile, '__init__.py')) or \
   35.88 +                   package in ('test', 'tests'):
   35.89 +                if prefix:
   35.90 +                    result.append('%s.%s' % (prefix, package))
   35.91 +                else:
   35.92 +                    result.append(package)
   35.93 +                result += get_packages(absfile, result[-1])
   35.94 +    return result
   35.95 +
   35.96 +def export(from_dir, to_dir,
   35.97 +           blacklist=BASE_BLACKLIST,
   35.98 +           ignore_ext=IGNORED_EXTENSIONS):
   35.99 +    """make a mirror of from_dir in to_dir, omitting directories and files
  35.100 +    listed in the black list
  35.101 +    """
  35.102 +    def make_mirror(arg, directory, fnames):
  35.103 +        """walk handler"""
  35.104 +        for norecurs in blacklist:
  35.105 +            try:
  35.106 +                fnames.remove(norecurs)
  35.107 +            except ValueError:
  35.108 +                pass
  35.109 +        for filename in fnames:
  35.110 +            # don't include binary files
  35.111 +            if filename[-4:] in ignore_ext:
  35.112 +                continue
  35.113 +            if filename[-1] == '~':
  35.114 +                continue
  35.115 +            src = '%s/%s' % (directory, filename)
  35.116 +            dest = to_dir + src[len(from_dir):]
  35.117 +            print >> sys.stderr, src, '->', dest
  35.118 +            if os.path.isdir(src):
  35.119 +                if not exists(dest):
  35.120 +                    os.mkdir(dest)
  35.121 +            else:
  35.122 +                if exists(dest):
  35.123 +                    os.remove(dest)
  35.124 +                shutil.copy2(src, dest)
  35.125 +    try:
  35.126 +        os.mkdir(to_dir)
  35.127 +    except OSError, ex:
  35.128 +        # file exists ?
  35.129 +        import errno
  35.130 +        if ex.errno != errno.EEXIST:
  35.131 +            raise
  35.132 +    walk(from_dir, make_mirror, None)
  35.133 +
  35.134 +
  35.135 +EMPTY_FILE = '"""generated file, don\'t modify or your data will be lost"""\n'
  35.136 +
  35.137 +class MyInstallLib(install_lib.install_lib):
  35.138 +    """extend install_lib command to handle  package __init__.py and
  35.139 +    include_dirs variable if necessary
  35.140 +    """
  35.141 +    def run(self):
  35.142 +        """overriden from install_lib class"""
  35.143 +        install_lib.install_lib.run(self)
  35.144 +        # create Products.__init__.py if needed
  35.145 +        if subpackage_of:
  35.146 +            product_init = join(self.install_dir, subpackage_of, '__init__.py')
  35.147 +            if not exists(product_init):
  35.148 +                self.announce('creating %s' % product_init)
  35.149 +                stream = open(product_init, 'w')
  35.150 +                stream.write(EMPTY_FILE)
  35.151 +                stream.close()
  35.152 +        # manually install included directories if any
  35.153 +        if include_dirs:
  35.154 +            if subpackage_of:
  35.155 +                base = join(subpackage_of, modname)
  35.156 +            else:
  35.157 +                base = modname
  35.158 +            for directory in include_dirs:
  35.159 +                dest = join(self.install_dir, base, directory)
  35.160 +                export(directory, dest)
  35.161 +        
  35.162 +def install(**kwargs):
  35.163 +    """setup entry point"""
  35.164 +    if subpackage_of:
  35.165 +        package = subpackage_of + '.' + modname
  35.166 +        kwargs['package_dir'] = {package : '.'}
  35.167 +        packages = [package] + get_packages(os.getcwd(), package)
  35.168 +    else:
  35.169 +        kwargs['package_dir'] = {modname : '.'}
  35.170 +        packages = [modname] + get_packages(os.getcwd(), modname)
  35.171 +    kwargs['packages'] = packages
  35.172 +    return setup(name = distname,
  35.173 +                 version = version,
  35.174 +                 license =license,
  35.175 +                 description = short_desc,
  35.176 +                 long_description = long_desc,
  35.177 +                 author = author,
  35.178 +                 author_email = author_email,
  35.179 +                 url = web,
  35.180 +                 scripts = ensure_scripts(scripts),
  35.181 +                 data_files=data_files,
  35.182 +                 ext_modules=ext_modules,
  35.183 +                 cmdclass={'install_lib': MyInstallLib},
  35.184 +                 **kwargs
  35.185 +                 )
  35.186 +            
  35.187 +if __name__ == '__main__' :
  35.188 +    install()
    36.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    36.2 +++ b/test/runtests.py	Wed Apr 26 10:48:09 2006 +0000
    36.3 @@ -0,0 +1,5 @@
    36.4 +from logilab.common.testlib import main
    36.5 +
    36.6 +if __name__ == '__main__':
    36.7 +    import sys, os
    36.8 +    main(os.path.dirname(sys.argv[0]) or '.')
    37.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    37.2 +++ b/test/unittest_transformer.py	Wed Apr 26 10:48:09 2006 +0000
    37.3 @@ -0,0 +1,119 @@
    37.4 +# Copyright (c) 2000-2003 LOGILAB S.A. (Paris, FRANCE).
    37.5 +# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    37.6 +
    37.7 +# This program is free software; you can redistribute it and/or modify it under
    37.8 +# the terms of the GNU General Public License as published by the Free Software
    37.9 +# Foundation; either version 2 of the License, or (at your option) any later
   37.10 +# version.
   37.11 +
   37.12 +# This program is distributed in the hope that it will be useful, but WITHOUT
   37.13 +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   37.14 +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
   37.15 +
   37.16 +# You should have received a copy of the GNU General Public License along with
   37.17 +# this program; if not, write to the Free Software Foundation, Inc.,
   37.18 +# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   37.19 +"""
   37.20 +unit tests for the transformer module
   37.21 +"""
   37.22 +__revision__ = "$Id: unittest_transformer.py,v 1.1 2003-09-10 12:38:09 syt Exp $"
   37.23 +
   37.24 +import unittest
   37.25 +import sys
   37.26 +
   37.27 +from logilab.doctools.transformer import PIManager, FormattingException, \
   37.28 +     guess_format, InputGuessException
   37.29 +
   37.30 +class PIManagerClassTest(unittest.TestCase):
   37.31 +    """unittest of the  processing instruction manager"""
   37.32 +    
   37.33 +    def setUp(self):
   37.34 +        self.manager = PIManager()
   37.35 +        
   37.36 +    def test_known_values(self):
   37.37 +        preprocess, styles = self.manager.fromString('''<?xml version="1.0" encoding="iso-8859-15"?>
   37.38 +<?logidoc-style target="html" xslt="thisone"?>
   37.39 +<?logidoc-style target="pdf" xslt="theotherone"?>
   37.40 +<?logidoc-style target="pdf" xslt="ignorethisone"?>
   37.41 +<?logidoc-preprocess xslt="thefirstone"?>
   37.42 +<?logidoc-preprocess xslt="thesecondone"?>
   37.43 +<?ignored-pi xslt="thesecondone"?>
   37.44 +<article id="test" lang="fr">
   37.45 +Gna gna gna
   37.46 +</article>''')        
   37.47 +        self.assertEqual(preprocess, ['thefirstone', 'thesecondone'])
   37.48 +        self.assertEqual(styles, {'pdf' : 'theotherone', 'html': 'thisone'})
   37.49 +
   37.50 +    def test_raise_1(self):
   37.51 +        self.assertRaises(FormattingException, self.manager.fromString,
   37.52 +                         '''<?xml version="1.0" encoding="iso-8859-15"?>
   37.53 +<?logidoc-style target="html"?>
   37.54 +<article id="test" lang="fr">
   37.55 +Gna gna gna
   37.56 +</article>''')
   37.57 +        
   37.58 +    def test_raise_2(self):
   37.59 +        self.assertRaises(FormattingException, self.manager.fromString,
   37.60 +                         '''<?xml version="1.0" encoding="iso-8859-15"?>
   37.61 +<?logidoc-style xslt="theotherone"?>
   37.62 +<article id="test" lang="fr">
   37.63 +Gna gna gna
   37.64 +</article>''')
   37.65 +        
   37.66 +    def test_raise_3(self):
   37.67 +        self.assertRaises(FormattingException, self.manager.fromString,
   37.68 +                         '''<?xml version="1.0" encoding="iso-8859-15"?>
   37.69 +<?logidoc-style target="html" xslt="theotherone" truc="bidule"?>
   37.70 +<article id="test" lang="fr">
   37.71 +Gna gna gna
   37.72 +</article>''')
   37.73 +        
   37.74 +    def test_raise_4(self):
   37.75 +        self.assertRaises(FormattingException, self.manager.fromString,
   37.76 +                         '''<?xml version="1.0" encoding="iso-8859-15"?>
   37.77 +<?logidoc-preprocess xstl="thefirstone"?>
   37.78 +<article id="test" lang="fr">
   37.79 +Gna gna gna
   37.80 +</article>''')
   37.81 +
   37.82 +class GuessformatFunctionTest(unittest.TestCase):
   37.83 +    
   37.84 +
   37.85 +    def test_known_values_1(self):
   37.86 +        self.assertEqual(guess_format('machin.txt'), 'rest')
   37.87 +        
   37.88 +    def test_known_values_2(self):
   37.89 +        self.assertEqual(guess_format('machin.rst'), 'rest')
   37.90 +        
   37.91 +    def test_known_values_3(self):
   37.92 +        self.assertEqual(guess_format('machin.rest'), 'rest')
   37.93 +        
   37.94 +    def test_known_values_4(self):
   37.95 +        self.assertEqual(guess_format('machin.xml'), 'docbook')
   37.96 +        
   37.97 +    def test_known_values_5(self):
   37.98 +        self.assertEqual(guess_format('machin.dbk'), 'docbook')
   37.99 +        
  37.100 +    def test_known_values_6(self):
  37.101 +        self.assertEqual(guess_format('machin.fo'), 'fo')
  37.102 +        
  37.103 +    def test_raise_1(self):
  37.104 +        self.assertRaises(InputGuessException, guess_format, 'machin.unk')
  37.105 +        
  37.106 +def suite():
  37.107 +    """return the unitest suite"""
  37.108 +    loader = unittest.TestLoader()
  37.109 +    testsuite = loader.loadTestsFromModule(sys.modules[__name__])
  37.110 +    return testsuite
  37.111 +
  37.112 +def Run(runner=None):
  37.113 +    """run tests"""
  37.114 +    testsuite = suite()
  37.115 +    if runner is None:
  37.116 +        runner = unittest.TextTestRunner()
  37.117 +        # uncomment next line to write tests results in a file
  37.118 +        #runner.__init__(open('tests.log','w+'))    
  37.119 +    return runner.run(testsuite)
  37.120 +        
  37.121 +if __name__ == '__main__':
  37.122 +    Run()
    38.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    38.2 +++ b/test/validation_tests.py	Wed Apr 26 10:48:09 2006 +0000
    38.3 @@ -0,0 +1,356 @@
    38.4 +# -*- coding: ISO-8859-1 -*-
    38.5 +"""Validation tests for doctools
    38.6 +
    38.7 +These tests are based on the specification document (see doc/spec_mkdoc.pdf)
    38.8 +"""
    38.9 +MKDOC = '../bin/mkdoc'
   38.10 +DEBUG = 0
   38.11 +
   38.12 +__revision__ = '$Id: validation_tests.py,v 1.26 2005-11-30 15:40:16 alf Exp $'
   38.13 +
   38.14 +
   38.15 +import unittest
   38.16 +import os, sys, shutil
   38.17 +
   38.18 +SAMPLEDOC = '''<?xml version="1.0" encoding="iso-8859-15"?>
   38.19 +<!DOCTYPE article>
   38.20 +%s
   38.21 +<article id="test" lang="fr">
   38.22 +<articleinfo>
   38.23 +<title>Test</title>
   38.24 +<subtitle>Gros test</subtitle>
   38.25 +<author><firstname>Alexandre</firstname><surname>FAYOLLE</surname></author>
   38.26 +<copyright><year>2003</year><holder>Logilab</holder></copyright>
   38.27 +</articleinfo>
   38.28 +<section><title>Présentation</title>
   38.29 +<para>Test des utilitaires mkdoc de <orgname>Logilab</orgname>.</para>
   38.30 +</section>
   38.31 +</article>'''
   38.32 +
   38.33 +SAMPLELETTRE = '''<?xml version="1.0" encoding="iso-8859-15"?>
   38.34 +<!DOCTYPE lettre>
   38.35 +%s
   38.36 +<lettre id="test" lang="fr">
   38.37 +<articleinfo>
   38.38 +<title>Test</title>
   38.39 +<subtitle>Gros test</subtitle>
   38.40 +<author><firstname>Alexandre</firstname><surname>FAYOLLE</surname></author>
   38.41 +<copyright><year>2003</year><holder>Logilab</holder></copyright>
   38.42 +</articleinfo>
   38.43 +<section><title>Présentation</title>
   38.44 +<para>Test des utilitaires mkdoc de <orgname>Logilab</orgname>.</para>
   38.45 +</section>
   38.46 +</lettre>'''
   38.47 +
   38.48 +SAMPLECONTRAT = '''<?xml version="1.0" encoding="iso-8859-15"?>
   38.49 +<!DOCTYPE contrat>
   38.50 +%s
   38.51 +<contrat id="test" lang="fr">
   38.52 +<articleinfo>
   38.53 +<title>Test</title>
   38.54 +<subtitle>Gros test</subtitle>
   38.55 +<author><firstname>Alexandre</firstname><surname>FAYOLLE</surname></author>
   38.56 +<copyright><year>2003</year><holder>Logilab</holder></copyright>
   38.57 +</articleinfo>
   38.58 +<section><title>Présentation</title>
   38.59 +<para>Test des utilitaires mkdoc de <orgname>Logilab</orgname>.</para>
   38.60 +</section>
   38.61 +</contrat>'''
   38.62 +
   38.63 +SAMPLESITE = '''<?xml version="1.0" encoding="iso-8859-15"?>
   38.64 +<!DOCTYPE site>
   38.65 +%s
   38.66 +<site id="test" lang="fr" target-dir="html">
   38.67 +<page filename="index">
   38.68 +<title>Test</title>
   38.69 +<para>Gros test</para>
   38.70 +</page>
   38.71 +</site>'''
   38.72 +
   38.73 +SAMPLEREST = '''Test ReST
   38.74 +=========
   38.75 +
   38.76 +:Author: Sylvain Thénault
   38.77 +:Organization: Logilab
   38.78 +:Version: $Revision: 1.26 $
   38.79 +:Date: $Date: 2005-11-30 15:40:16 $
   38.80 +:Abstract:
   38.81 +    Test de la conversion avec du ReST
   38.82 +
   38.83 +Test1
   38.84 +-----
   38.85 +
   38.86 +Description
   38.87 +```````````
   38.88 +Des tests et du text à convertir. Convertit des fichiers au format ReST_ (Restructured Text) ou Docbook_ dans divers formats tels que html ou pdf.
   38.89 +
   38.90 +.. _ReST: http://docutils.sourceforge.net/rst.html
   38.91 +.. _Docbook: http://www.docbook.org
   38.92 +
   38.93 +
   38.94 +Les subsititutions avec des charactères non latin-1 sont également mises en |oe| uvre.
   38.95 +
   38.96 +.. |oe| unicode:: &#x0153;
   38.97 +   :rtrim:
   38.98 +
   38.99 +'''
  38.100 +
  38.101 +SAMPLEFO = '''<?xml version="1.0" encoding="UTF-8"?>
  38.102 + <root xmlns="http://www.w3.org/1999/XSL/Format" font-size="16pt">
  38.103 +  <layout-master-set>
  38.104 +    <simple-page-master 
  38.105 +         margin-right="15mm" margin-left="15mm" 
  38.106 +         margin-bottom="15mm" margin-top="15mm" 
  38.107 +         page-width="210mm" page-height="297mm" 
  38.108 +         master-name="bookpage">
  38.109 +      <region-body region-name="bookpage-body"
  38.110 +         margin-bottom="5mm" margin-top="5mm" />
  38.111 +    </simple-page-master>
  38.112 +  </layout-master-set>
  38.113 +  <page-sequence master-reference="bookpage">
  38.114 +    <title>Hello world example</title>
  38.115 +    <flow flow-name="bookpage-body">
  38.116 +      <block>Hello XSLFO!</block>
  38.117 +    </flow>
  38.118 +  </page-sequence>
  38.119 + </root>'''
  38.120 +
  38.121 +EXTENSIONS = {
  38.122 +    'pdf': 'pdf',
  38.123 +    'html': 'html',
  38.124 +    'docbook': 'xml',
  38.125 +    }
  38.126 +
  38.127 +class TestMkdoc(unittest.TestCase):
  38.128 +    """Base class. Defines some helper functions and no tests"""
  38.129 +    
  38.130 +    EXTENSION = 'xml'
  38.131 +    TARGET = None
  38.132 +    def setUp(self):
  38.133 +        """initialize the name of the file to process and the name of the result"""
  38.134 +        self.filename = mktemp('.' + self.EXTENSION)
  38.135 +        self.targetname = os.path.basename(self.filename).replace(self.EXTENSION,
  38.136 +                                                                  EXTENSIONS[self.TARGET])
  38.137 +
  38.138 +    def tearDown(self):
  38.139 +        """remove generated files"""
  38.140 +        os.remove(self.filename)
  38.141 +        if os.path.exists(self.targetname):
  38.142 +            os.remove(self.targetname)
  38.143 +        
  38.144 +
  38.145 +    def run_mkdoc(self, doc, style=None, **additional_options):
  38.146 +        f = open(self.filename,'w')
  38.147 +        f.write(doc)
  38.148 +        f.close()
  38.149 +        commandline = [MKDOC, '--target', self.TARGET, '--check', 'no']
  38.150 +        if not DEBUG:
  38.151 +            commandline.append('--quiet')
  38.152 +        if style:
  38.153 +            commandline.append('--stylesheet')
  38.154 +            commandline.append(style)
  38.155 +        for key, val in additional_options.items():
  38.156 +            commandline.append('--%s' % key)
  38.157 +            commandline.append(val)
  38.158 +            
  38.159 +        commandline.append(self.filename)
  38.160 +        if DEBUG:
  38.161 +            print '*'*80
  38.162 +            print ' '.join(commandline)
  38.163 +            print 'target', self.targetname
  38.164 +            sys.stdout.flush()
  38.165 +        status = os.spawnv(os.P_WAIT,MKDOC,  commandline)
  38.166 +        return status
  38.167 +
  38.168 +    def make_doc(self, target, xslt, sample=SAMPLEDOC):
  38.169 +        """default implementation for XML documents"""
  38.170 +        PI = '<?logidoc-style target="%s" xslt="%s"?>'%(target,xslt)
  38.171 +        doc = sample % PI
  38.172 +        return doc
  38.173 +
  38.174 +
  38.175 +    def default_test(self,target=None,xslt=None, **additional_options):
  38.176 +        """The average default test.
  38.177 +        Tests that mkdoc runs fine and that a file is generated
  38.178 +        with some contents"""
  38.179 +        doc = self.make_doc(target, xslt)
  38.180 +        status = self.run_mkdoc(doc, xslt, **additional_options)
  38.181 +        self.failUnlessEqual(status,0)
  38.182 +        self.failUnless(os.path.isfile(self.targetname))
  38.183 +        self.failUnless(os.path.getsize(self.targetname)>0)
  38.184 +
  38.185 +class Xml2Pdf(TestMkdoc):
  38.186 +    """Tests the XML to PDF conversions"""
  38.187 +    TARGET = 'pdf'
  38.188 +
  38.189 +    def test_pdf_standard(self):
  38.190 +        self.default_test('pdf','standard')
  38.191 +
  38.192 +    def test_pdf_standard(self):
  38.193 +        self.default_test('pdf','standard')
  38.194 +        
  38.195 +    def test_pdf_ao(self):
  38.196 +        self.default_test('pdf','reponse-ao')
  38.197 +        
  38.198 +    def test_pdf_admin(self):
  38.199 +        self.default_test('pdf','rapport-admin')
  38.200 +        
  38.201 +    def test_pdf_pubtech(self):
  38.202 +        self.default_test('pdf','publi-technique')
  38.203 +
  38.204 +    def test_pdf_pubcom(self):
  38.205 +        self.default_test('pdf','publi-commerciale')
  38.206 +
  38.207 +    def test_pdf_lettre(self):
  38.208 +        doc = self.make_doc(self.TARGET, 'lettre', sample=SAMPLELETTRE)
  38.209 +        status = self.run_mkdoc(doc)
  38.210 +        self.failUnlessEqual(status,0)
  38.211 +        self.failUnless(os.path.isfile(self.targetname))
  38.212 +
  38.213 +    def test_pdf_contrat(self):
  38.214 +        doc = self.make_doc(self.TARGET, 'contrat', sample=SAMPLECONTRAT)
  38.215 +        status = self.run_mkdoc(doc)
  38.216 +        self.failUnlessEqual(status,0)
  38.217 +        self.failUnless(os.path.isfile(self.targetname))
  38.218 +
  38.219 +    def test_pdf_formation(self):
  38.220 +        self.default_test('pdf','catalogue-formation')
  38.221 +
  38.222 +
  38.223 +class Xml2Html(TestMkdoc):
  38.224 +    """Tests the XML to HTML conversions"""
  38.225 +    EXTENSION = 'dbk'
  38.226 +    TARGET = 'html'
  38.227 +
  38.228 +    def tearDown(self):
  38.229 +        """remove generated files"""
  38.230 +        if os.path.exists(self.filename):
  38.231 +            os.remove(self.filename)
  38.232 +        if os.path.exists(self.targetname):
  38.233 +            os.remove(self.targetname)
  38.234 +        if os.path.exists('html'):
  38.235 +            shutil.rmtree('html')
  38.236 +
  38.237 +    def multi_test(self, target=None, xslt=None, sample=SAMPLEDOC,
  38.238 +                   **additional_options):
  38.239 +        """The average default test.
  38.240 +        Tests that mkdoc runs fine and that a file is generated
  38.241 +        with some contents"""
  38.242 +        doc = self.make_doc(target, xslt, sample)
  38.243 +        status = self.run_mkdoc(doc, xslt, **additional_options)
  38.244 +        self.failUnlessEqual(status,0)
  38.245 +        self.failUnless(os.path.isdir('html'))
  38.246 +        self.failUnless(os.path.isfile('html/index.html'))
  38.247 +
  38.248 +    def test_html_standard(self):
  38.249 +        self.default_test('html','standard')
  38.250 +
  38.251 +    def test_html_single(self):
  38.252 +        self.default_test('html','single-file')
  38.253 +
  38.254 +    def test_html_multi(self):
  38.255 +        self.multi_test('html','multi-files')
  38.256 +
  38.257 +    def test_html_website(self):
  38.258 +        self.multi_test('html','web-site', sample=SAMPLESITE)
  38.259 +
  38.260 +class Rest2Html(Xml2Html):
  38.261 +    EXTENSION = 'rst'
  38.262 +
  38.263 +    def make_doc(self, target, xslt, sample=None):
  38.264 +        return SAMPLEREST
  38.265 +    
  38.266 +    def test_html_website(self):
  38.267 +        pass
  38.268 +        #self.multi_test('html','web-site', doctype='site')
  38.269 +
  38.270 +class Rest2Pdf(Xml2Pdf):
  38.271 +    EXTENSION = 'txt'
  38.272 +
  38.273 +    def test_pdf_admin(self):
  38.274 +        self.default_test('pdf','rapport-admin', doctype='article')
  38.275 +
  38.276 +    def test_pdf_lettre(self):
  38.277 +        self.default_test('pdf','lettre', doctype='lettre')
  38.278 +    
  38.279 +    def test_pdf_contrat(self):
  38.280 +        self.default_test('pdf','contrat', doctype='contrat')
  38.281 +    
  38.282 +    def test_pdf_pubcom(self):
  38.283 +        self.default_test('pdf','publi-commerciale', doctype='article')
  38.284 +        
  38.285 +    def make_doc(self,target,xslt):
  38.286 +        return SAMPLEREST
  38.287 +    
  38.288 +
  38.289 +class Rest2Docbook(TestMkdoc):
  38.290 +    EXTENSION = 'rest'
  38.291 +    TARGET = 'docbook'
  38.292 +    def make_doc(self,*args):
  38.293 +        return SAMPLEREST
  38.294 +
  38.295 +    def test_rest_to_docbook(self):
  38.296 +        self.default_test()
  38.297 +                        
  38.298 +class Fo2Pdf(TestMkdoc):
  38.299 +    EXTENSION = 'fo'
  38.300 +    TARGET = 'pdf'
  38.301 +    def make_doc(self,*args):
  38.302 +        return SAMPLEFO
  38.303 +
  38.304 +    def test_fo_to_pdf(self):
  38.305 +        self.default_test()
  38.306 +                        
  38.307 +
  38.308 +class Xml2PdfWithPreprocess(TestMkdoc):
  38.309 +    EXTENSION = 'xml'
  38.310 +    TARGET = 'pdf'
  38.311 +    def make_doc(self,target,xslt):
  38.312 +        """default implementation for XML documents"""
  38.313 +        PI = '<?logidoc-style target="%s" xslt="%s"?>'%(target,xslt)
  38.314 +        PI2 = '<?logidoc-preprocess xslt="session2prg-cours"?>'
  38.315 +        doc = SAMPLEDOC % (PI + PI2)
  38.316 +        return doc
  38.317 +
  38.318 +    def test_pdf_admin(self):
  38.319 +        self.default_test('pdf','rapport-admin')
  38.320 +
  38.321 +    
  38.322 +
  38.323 +def ensure_mkdoc_executable():
  38.324 +    import stat
  38.325 +    mask = stat.S_IMODE(os.stat(MKDOC)[stat.ST_MODE])
  38.326 +    if not mask & 0111:
  38.327 +        try:
  38.328 +            os.chmod(MKDOC, mask|0111)
  38.329 +        except OSError:
  38.330 +            sys.exit('%s is not executable')
  38.331 +    
  38.332 +def mktemp(extension=''):
  38.333 +    import time, md5
  38.334 +    while 1:
  38.335 +        filename = '/tmp/temp_%s%s'%(md5.new(str(time.time())).hexdigest(),
  38.336 +                                     extension)
  38.337 +        if os.path.exists(filename):
  38.338 +            time.sleep(.1)
  38.339 +        else:
  38.340 +            return filename
  38.341 +
  38.342 +
  38.343 +ensure_mkdoc_executable()
  38.344 +
  38.345 +def suite():
  38.346 +    loader = unittest.TestLoader()
  38.347 +    testsuite = loader.loadTestsFromModule(sys.modules[__name__])
  38.348 +    return testsuite    
  38.349 +    
  38.350 +
  38.351 +if __name__ == '__main__':
  38.352 +    if os.environ.get('PYUNIT', 'text') == 'graphic':
  38.353 +        try:
  38.354 +            from unittestgui import main
  38.355 +        except ImportError:
  38.356 +            from unittest import main
  38.357 +    else:
  38.358 +        from unittest import main
  38.359 +    main()
    39.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    39.2 +++ b/transform.py	Wed Apr 26 10:48:09 2006 +0000
    39.3 @@ -0,0 +1,58 @@
    39.4 +#!/usr/bin/env python
    39.5 +
    39.6 +# -*- coding: ISO-8859-1 -*-
    39.7 +
    39.8 +# Copyright (c) 2000-2003 LOGILAB S.A. (Paris, FRANCE).
    39.9 +# http://www.logilab.fr/ -- mailto:contact@logilab.fr
   39.10 +#
   39.11 +# This program is free software; you can redistribute it and/or modify it under
   39.12 +# the terms of the GNU General Public License as published by the Free Software
   39.13 +# Foundation; either version 2 of the License, or (at your option) any later
   39.14 +# version.
   39.15 +#
   39.16 +# This program is distributed in the hope that it will be useful, but WITHOUT
   39.17 +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   39.18 +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
   39.19 +#
   39.20 +# You should have received a copy of the GNU General Public License along with
   39.21 +# this program; if not, write to the Free Software Foundation, Inc.,
   39.22 +# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   39.23 +
   39.24 +"""main entry point for the transformer"""
   39.25 +
   39.26 +__revision__ = '$Id: transform.py,v 1.12 2004-10-31 02:18:06 nico Exp $'
   39.27 +
   39.28 +import sys
   39.29 +from logilab.doctools.transformer import Transformer, FormattingException, \
   39.30 +     GuessException
   39.31 +
   39.32 +def run(args):
   39.33 +    """
   39.34 +    main
   39.35 +    """
   39.36 +    transformer = Transformer()
   39.37 +    transformer.load_file_configuration()
   39.38 +    args = transformer.load_command_line_configuration(args)
   39.39 +    if not args:
   39.40 +        print transformer.help()
   39.41 +        return 4
   39.42 +    status = 0
   39.43 +    for filename in args:
   39.44 +        if len(args) > 1:
   39.45 +            print '*' * 80
   39.46 +        try:
   39.47 +            output_file = transformer.transform(filename)
   39.48 +        except GuessException, exc:
   39.49 +            print 'Error:', exc
   39.50 +            status = 1
   39.51 +        except FormattingException, exc:
   39.52 +            print 'Error:', exc
   39.53 +            status = 2
   39.54 +        except:
   39.55 +            import traceback
   39.56 +            traceback.print_exc()
   39.57 +            status = 3
   39.58 +    return status
   39.59 +
   39.60 +if __name__ == '__main__':
   39.61 +    sys.exit(run(sys.argv[1:]))
    40.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    40.2 +++ b/transformer.py	Wed Apr 26 10:48:09 2006 +0000
    40.3 @@ -0,0 +1,494 @@
    40.4 +# -*- coding: ISO-8859-1 -*-
    40.5 +
    40.6 +# Copyright (c) 2000-2003 LOGILAB S.A. (Paris, FRANCE).
    40.7 +# http://www.logilab.fr/ -- mailto:contact@logilab.fr
    40.8 +#
    40.9 +# This program is free software; you can redistribute it and/or modify it under
   40.10 +# the terms of the GNU General Public License as published by the Free Software
   40.11 +# Foundation; either version 2 of the License, or (at your option) any later
   40.12 +# version.
   40.13 +#
   40.14 +# This program is distributed in the hope that it will be useful, but WITHOUT
   40.15 +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   40.16 +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
   40.17 +#
   40.18 +# You should have received a copy of the GNU General Public License along with
   40.19 +# this program; if not, write to the Free Software Foundation, Inc.,
   40.20 +# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   40.21 +
   40.22 +""" %prog [options] <input_file>
   40.23 +
   40.24 +  Transform ReST / DOCBOOK XML to differents output formats
   40.25 +"""
   40.26 +
   40.27 +__revision__ = "$Id: transformer.py,v 1.15 2006-02-27 09:03:13 nico Exp $"
   40.28 +
   40.29 +import os
   40.30 +import sys
   40.31 +from os.path import isabs, join, exists, expanduser, splitext, basename#, exists
   40.32 +from cStringIO import StringIO
   40.33 +from commands import getstatusoutput
   40.34 +from xml.sax import ContentHandler, make_parser, SAXNotRecognizedException
   40.35 +from xml.sax import InputSource, ErrorHandler
   40.36 +from xml.sax.handler import feature_external_pes, feature_external_ges
   40.37 +from logilab.common.configuration import OptionsManagerMixIn
   40.38 +from logilab.common.configuration import OptionsProviderMixIn
   40.39 +
   40.40 +from logilab.doctools.__pkginfo__ import version
   40.41 +
   40.42 +#import tempfile
   40.43 +#tempfile.tempdir='.'
   40.44 +
   40.45 +REST_EXTENSIONS = ('.txt', '.rst', '.rest')
   40.46 +DOCBOOK_EXTENSIONS = ('.xml', '.dbk')
   40.47 +FO_EXTENSIONS = ('.fo',)
   40.48 +
   40.49 +if os.environ.has_key('MKDOCRC') and exists(os.environ['MKDOCRC']):
   40.50 +    MKDOCRC = os.environ['MKDOCRC']
   40.51 +else:
   40.52 +    USER_HOME = expanduser('~')
   40.53 +    if USER_HOME == '~':
   40.54 +        MKDOCRC = ".mkdocrc"
   40.55 +    else:
   40.56 +        MKDOCRC = join(USER_HOME, '.mkdocrc')
   40.57 +    if not exists(MKDOCRC):
   40.58 +        if exists('/etc/mkdocrc'):
   40.59 +            MKDOCRC = '/etc/mkdocrc'
   40.60 +        else:
   40.61 +            MKDOCRC = None
   40.62 +
   40.63 +ENV_HELP = """
   40.64 +The following environment variables are used :                                 
   40.65 +    * MKDOCRC                                                               
   40.66 +    path to the configuration file. If not found, it will use the first        
   40.67 +existant file in ~/.mkdocrc, /etc/mkdocrc.
   40.68 +"""
   40.69 +if MKDOCRC:
   40.70 +    ENV_HELP += 'The current configuration file in use is %s.'% MKDOCRC
   40.71 +else:
   40.72 +    ENV_HELP += 'No configuration file has been found for this run.'
   40.73 +
   40.74 +# exceptions ##################################################################
   40.75 +
   40.76 +class FormattingException(Exception):
   40.77 +    """raised when a transformation failed"""
   40.78 +    
   40.79 +class GuessException(Exception):
   40.80 +    """raised when we are not able to guess something"""
   40.81 +    
   40.82 +class InputGuessException(GuessException): 
   40.83 +    """raised when we are not able to guess the input file format"""
   40.84 +
   40.85 +class OutputGuessException(GuessException): 
   40.86 +    """raised when we are not able to guess the input file format"""
   40.87 +
   40.88 +# utilities ###################################################################
   40.89 +
   40.90 +class PIManager(ContentHandler):
   40.91 +    """try to get preprocess and style xslt from processing instruction"""
   40.92 +    def __init__(self, quiet=1):
   40.93 +        self.quiet = quiet
   40.94 +        
   40.95 +    def processingInstruction(self, target, data):
   40.96 +        """Receive notification of a processing instruction."""
   40.97 +        if target == 'logidoc-style':
   40.98 +            target, xslt = None, None
   40.99 +            for attr in data.split(' '):
  40.100 +                name, value = attr.split('=')
  40.101 +                if name == 'target':
  40.102 +                    target = value[1:-1]
  40.103 +                elif name == 'xslt':
  40.104 +                    xslt = value[1:-1]
  40.105 +                else:
  40.106 +                    msg = 'Bad logidoc-style attribute %s'
  40.107 +                    raise FormattingException(msg % name)
  40.108 +            if not target:
  40.109 +                msg = 'Bad logidoc-style, missing target' 
  40.110 +                raise FormattingException(msg)
  40.111 +            if not xslt:
  40.112 +                msg = 'Bad logidoc-style, missing xslt' 
  40.113 +                raise FormattingException(msg)
  40.114 +            if not self.xslts.has_key(target):
  40.115 +                self.xslts[target] = xslt
  40.116 +            else:
  40.117 +                print 'Warning: ignoring %s stylesheet for %s' % (xslt, target)
  40.118 +        elif target == 'logidoc-preprocess':
  40.119 +            name, value = data.split('=')
  40.120 +            if name != 'xslt':
  40.121 +                msg = 'Bad logidoc-preprocess attribute %s' 
  40.122 +                raise FormattingException(msg % name)
  40.123 +                
  40.124 +            assert name == 'xslt'
  40.125 +            self.preprocess.append(value[1:-1])
  40.126 +            
  40.127 +    def reset(self):
  40.128 +        self.preprocess = []
  40.129 +        self.xslts = {}
  40.130 +
  40.131 +    def fromFile(self, filename):
  40.132 +        inputsource = InputSource('file://'+os.path.abspath(filename))
  40.133 +        return self.fromInputSource(inputsource)
  40.134 +    
  40.135 +    def fromString(self, string):
  40.136 +        inputsource = InputSource()
  40.137 +        inputsource.setByteStream(StringIO(string))
  40.138 +        inputsource.setSystemId('file://'+os.path.abspath("hardcoded_string"))
  40.139 +        return self.fromInputSource(inputsource)
  40.140 +
  40.141 +
  40.142 +    def fromInputSource(self, inputsource):
  40.143 +        self.reset()
  40.144 +        parser = make_parser()
  40.145 +        parser.setContentHandler(self)
  40.146 +        parser.setErrorHandler(ErrorHandler())
  40.147 +        # do not include any external entities
  40.148 +##         try:
  40.149 +##             parser.setFeature(feature_external_ges, 0)
  40.150 +##         except SAXNotRecognizedException:
  40.151 +##             pass
  40.152 +##         try:
  40.153 +##             parser.setFeature(feature_external_pes, 0)
  40.154 +##         except SAXNotRecognizedException:
  40.155 +##             pass
  40.156 +        parser.parse(inputsource)
  40.157 +        return self.preprocess, self.xslts
  40.158 +        
  40.159 +    
  40.160 +def exec_cmd(cmd):
  40.161 +    """executed a command, check status and return output"""
  40.162 +    status, output = getstatusoutput(cmd)
  40.163 +    if status != 0:
  40.164 +        raise FormattingException('"%s" returned status %s\n%s' % (
  40.165 +            cmd, status, output))
  40.166 +    return output
  40.167 +
  40.168 +def guess_format(filename):
  40.169 +    """guess file format according to its extension"""
  40.170 +    ext = splitext(filename)[1]
  40.171 +    if ext in REST_EXTENSIONS:
  40.172 +        return 'rest'
  40.173 +    if ext in DOCBOOK_EXTENSIONS:
  40.174 +        return 'docbook'
  40.175 +    if ext in FO_EXTENSIONS:
  40.176 +        return 'fo'
  40.177 +    raise InputGuessException("Unable to guess file format from %s" % filename)
  40.178 +    
  40.179 +
  40.180 +def xmlproc_output(output, checked):
  40.181 +    """parse xmlproc output and check for errors"""
  40.182 +    for line in output.strip().split('\n'):
  40.183 +        if line[0:2] == 'E:' or line[0:2] == 'W:' :
  40.184 +            print line
  40.185 +    j = line.find(" error(s)")
  40.186 +    assert j != -1
  40.187 +    i = line.rfind(" ", 0, j)
  40.188 +    err = line[i+1:j]
  40.189 +    if err != "0" :
  40.190 +        raise FormattingException('Not a %s xml file' % checked)
  40.191 +
  40.192 +        
  40.193 +# the transformer #############################################################
  40.194 +
  40.195 +# FIXME: on a besoin de rajouter des étapes de transformation pour intégrer
  40.196 +# pybill et trf-session. faudrait remanier ce code pour faire apparaître un
  40.197 +# objet Chainon qui sera une transformation élémentaire et ensuite on
  40.198 +# pourra chaîner les chaînons avec un joli générateur/producteur paresseux
  40.199 +
  40.200 +class Transformer(OptionsManagerMixIn, OptionsProviderMixIn):
  40.201 +    name = 'MAIN'
  40.202 +    options = (
  40.203 +        # main options
  40.204 +        ('target',
  40.205 +         {'type': 'choice',
  40.206 +          'choices': ('docbook', 'html', 'fo', 'pdf'),
  40.207 +          'default' : 'pdf',
  40.208 +          'metavar' : "<format>",
  40.209 +          'help': "output format. Available format are docbook (if input is a \
  40.210 +ReST file), html and pdf."
  40.211 +        }),
  40.212 +        ('source',
  40.213 +         {'type': 'choice',
  40.214 +          'choices': ('rest', 'docbook', 'fo'),
  40.215 +          'metavar' : "<format>",
  40.216 +          'help': "source format. Available format are rest, docbook, fo. If \
  40.217 +not specified, source format will be guessed from the file's extension."
  40.218 +        }),
  40.219 +        ('check',
  40.220 +         {'type' : 'yn',
  40.221 +          'metavar' : '<y_or_n>',
  40.222 +          'default' : 1,
  40.223 +          'help': "tell if we should check that the docbook input file is \
  40.224 +well formed xml."
  40.225 +          }),
  40.226 +        ('validate',
  40.227 +         {'type' : 'yn',
  40.228 +          'metavar' : '<y_or_n>',
  40.229 +          'default' : 0,
  40.230 +          'help': "tell if we should validate the docbook input file."
  40.231 +          }),
  40.232 +        ('preprocess',
  40.233 +         {'type': 'string',
  40.234 +          'action': 'append',
  40.235 +          'default': (),
  40.236 +          'metavar' : '<xslt>',
  40.237 +          'help': "add a pre-processing style sheet. You can set this option \
  40.238 +multiple times. If not specified, preprocessing."
  40.239 +          }),
  40.240 +        ('stylesheet',
  40.241 +         {'type': 'string',
  40.242 +          'metavar' : '<xslt>',
  40.243 +          'help': "set the main style sheet."
  40.244 +          }),
  40.245 +        ('ignore-pi',
  40.246 +         {'action': 'store_true',
  40.247 +          'dest': 'ignore_pi',
  40.248 +          'help': "Do not try to guess main/pre-process stylesheets from \
  40.249 +processing instruction."
  40.250 +          }),
  40.251 +        ('keep',
  40.252 +         {'action': 'store_true',
  40.253 +          'help': "Keep temporary files."
  40.254 +          }),
  40.255 +        ('quiet',
  40.256 +         {'action': 'store_true',
  40.257 +          'help': "Do not display information about what we're doing..."
  40.258 +          }),
  40.259 +        
  40.260 +        # FOP related options
  40.261 +        ('fop',
  40.262 +         {'type': 'string',
  40.263 +          'default' : 'fop',
  40.264 +          'metavar' : "<binpath>",
  40.265 +          'help': "path of the fop executable."
  40.266 +          }),
  40.267 +        ('fop-options', 
  40.268 +         {'type': 'string',
  40.269 +          'dest' : 'fop_opts',
  40.270 +          'default' : '',
  40.271 +          'metavar' : "<options list>",
  40.272 +          'help': "options given to the fop executable."
  40.273 +          }),
  40.274 +        
  40.275 +        # xsltproc related options
  40.276 +        ('xsltproc', 
  40.277 +         {'type': 'string',
  40.278 +          'default' : 'xsltproc',
  40.279 +          'metavar' : "<binpath>",
  40.280 +          'help': "path of the xsltproc executable."
  40.281 +          }),
  40.282 +        ('xsltproc-options', 
  40.283 +         {'type': 'string',
  40.284 +          'dest' : 'xsltproc_opts',
  40.285 +          'default' : '--xinclude --catalogs',
  40.286 +          'metavar' : "<options list>",
  40.287 +          'help': "options given to the xsltproc executable."
  40.288 +          }),
  40.289 +        ('param', 
  40.290 +         {'type': 'named',
  40.291 +          'action' : 'append',
  40.292 +          'default' : (),
  40.293 +          'dest': 'parameters',
  40.294 +          'metavar' : "<name>=<value>",
  40.295 +          'help': "sets the <name> stylesheet parameter to <value>. You may \
  40.296 +set this option multiple times. Parameters are given to the xslt processor."
  40.297 +          }),
  40.298 +        # ReST related options
  40.299 +        ('doctype', 
  40.300 +         {'type': 'string',
  40.301 +          'default' : 'book',
  40.302 +          'metavar' : "<doctype>",
  40.303 +          'help': "doctype to use when converting ReST to DOCBOOK."
  40.304 +          }),
  40.305 +       
  40.306 +        ## FIXME Path to xslt directory has not to be specified.
  40.307 +        ## TODO : Remove all references to xslt root and use id in catalog
  40.308 +        # xslts location
  40.309 +        ('xsltroot',
  40.310 +         {'type': 'string',
  40.311 +          'metavar' : "<xslt directory>",
  40.312 +          'default' : '/usr/share/sgml/logilab-xml/stylesheet/',
  40.313 +          'help': "directory where logilab's stylesheets are located."
  40.314 +          }),
  40.315 +        )
  40.316 +    
  40.317 +    def __init__(self):
  40.318 +        OptionsManagerMixIn.__init__(self, usage=__doc__, version=version,
  40.319 +                                     config_file=MKDOCRC, quiet=1)
  40.320 +        OptionsProviderMixIn.__init__(self)
  40.321 +        self.register_options_provider(self)
  40.322 +        self.add_help_section('Environment variables', ENV_HELP)
  40.323 +        self.pimanager = PIManager()
  40.324 +
  40.325 +    def xslt_transform(self, input_file, output_file, xslt_file):
  40.326 +        """xsltproc based transformation
  40.327 +        """
  40.328 +        if not self.config.quiet:
  40.329 +            print '-' * 80
  40.330 +            print "Transforms %s to %s using %s" % (input_file, output_file,
  40.331 +                                                    xslt_file)
  40.332 +        cmd = [self.config.xsltproc, self.config.xsltproc_opts,
  40.333 +               "--output ", output_file]
  40.334 +        params = []
  40.335 +        for name, value in self.config.parameters :
  40.336 +            cmd.append('--param')
  40.337 +            cmd.append(name)
  40.338 +            cmd.append("\"'" + value + "'\"")
  40.339 +        cmd.append(xslt_file)
  40.340 +        cmd.append(input_file)
  40.341 +        # executes transformation command line
  40.342 +        output = exec_cmd(' '.join(cmd))
  40.343 +        if not self.config.quiet:
  40.344 +            print output
  40.345 +        return output_file
  40.346 +
  40.347 +
  40.348 +    def fop_transform(self, fo_file, output_file):
  40.349 +        """FOP based transformation
  40.350 +        """
  40.351 +        if not self.config.quiet:
  40.352 +            print '-' * 80
  40.353 +            print "Transforms Formatting Objects to PDF (%s -> %s)" % (
  40.354 +                fo_file, output_file)
  40.355 +        # executes transformation command line
  40.356 +        output = exec_cmd("%s %s %s %s" %(self.config.fop, self.config.fop_opts,
  40.357 +                                 fo_file, output_file))
  40.358 +        if not self.config.quiet:
  40.359 +            print output
  40.360 +        return output_file
  40.361 +
  40.362 +
  40.363 +    def rest_transform(self, rest_file, output_file):
  40.364 +        """transforms Restructured Text to DOCBOOK XML
  40.365 +        """
  40.366 +        if not self.config.quiet:
  40.367 +            print '-' * 80
  40.368 +            print "Transforms Restructured Text to DOCBOOK XML (%s -> %s)" % (
  40.369 +                rest_file, output_file)
  40.370 +        from docutils import core, io
  40.371 +        from logilab.doctools.rest_docbook import Writer
  40.372 +        writer = Writer()
  40.373 +        pub = core.Publisher(writer=writer)
  40.374 +        pub.set_reader(reader_name='standalone',
  40.375 +                       parser_name='restructuredtext', parser=None)
  40.376 +        pub.source = io.FileInput(source_path=rest_file, encoding='ISO-8859-1')
  40.377 +        pub.destination = io.FileOutput(destination_path=output_file,
  40.378 +                                        encoding='UTF-8')
  40.379 +        # FIXME : find the way to specify docutils no parsing args
  40.380 +        # hint: use core.publish_programmatically ?
  40.381 +        sys.argv = [sys.argv[0]]
  40.382 +        try:
  40.383 +            pub.publish(settings_overrides={'output_encoding': 'UTF-8',
  40.384 +                                            'report_level': 2,
  40.385 +                                            'doctype' : self.config.doctype,
  40.386 +                                            })
  40.387 +        except Exception, ex:
  40.388 +            raise FormattingException(str(ex))
  40.389 +        return output_file
  40.390 +
  40.391 +
  40.392 +    def check_xml(self, xml_file):
  40.393 +        """check the given xml file is well formed XML
  40.394 +        """
  40.395 +        if not self.config.quiet:
  40.396 +            print '-' * 80
  40.397 +            print 'Checking %s' % xml_file
  40.398 +        output = exec_cmd("xmlproc_parse " + xml_file)
  40.399 +        xmlproc_output(output, 'well formed')
  40.400 +        return xml_file
  40.401 +
  40.402 +
  40.403 +    def validate_xml(self, xml_file):
  40.404 +        """check the given xml file is valid XML
  40.405 +        """
  40.406 +        if not self.config.quiet:
  40.407 +            print '-' * 80
  40.408 +            print 'Validating %s' % xml_file
  40.409 +        output = exec_cmd("xmlproc_val " + xml_file)
  40.410 +        xmlproc_output(output, 'valid')
  40.411 +        return xml_file
  40.412 +
  40.413 +
  40.414 +    def transform(self, filename):
  40.415 +        """run transforms on filename
  40.416 +        """
  40.417 +        # get transform parameters
  40.418 +        to_remove = []
  40.419 +        source_format = self.config.source
  40.420 +        dest_format = self.config.target
  40.421 +        preprocess = self.config.preprocess
  40.422 +        stylesheet = self.config.stylesheet
  40.423 +        base = splitext(basename(filename))[0]
  40.424 +
  40.425 +        if not source_format:
  40.426 +            source_format = guess_format(filename)
  40.427 +            
  40.428 +        # check docbook xml for validity or well formness
  40.429 +        if source_format == 'docbook':
  40.430 +            if not self.config.ignore_pi and (not preprocess or not stylesheet):
  40.431 +                self.pimanager.quiet = self.config.quiet
  40.432 +                preproc, styles = self.pimanager.fromFile(filename)
  40.433 +                preprocess = preprocess or preproc
  40.434 +                stylesheet = stylesheet or styles.get(dest_format)
  40.435 +
  40.436 +        # check we have a main stylesheet
  40.437 +        if not stylesheet and dest_format != 'docbook' and source_format != 'fo':
  40.438 +            raise OutputGuessException('Unable to guess the main style sheet')
  40.439 +
  40.440 +        # transform ReST to docbook ?
  40.441 +        if source_format == 'rest':
  40.442 +            filename = self.rest_transform(filename, base + '.xml')
  40.443 +            if dest_format != 'docbook':
  40.444 +                to_remove.append(filename)
  40.445 +                
  40.446 +        # are we arrived ?
  40.447 +        if dest_format == 'docbook':
  40.448 +            return filename
  40.449 +        
  40.450 +        # preprocessing
  40.451 +        for preprocess_xslt in preprocess:
  40.452 +            xslt = self.absolute_stylesheet(preprocess_xslt, 'pre-process')
  40.453 +            output = '%s.%s.xml' % (base, preprocess_xslt)
  40.454 +            filename = self.xslt_transform(filename, output, xslt)
  40.455 +            to_remove.append(output)
  40.456 +            
  40.457 +        # finalization
  40.458 +        if dest_format == 'html':
  40.459 +            # transform DOCBOOK to HTML
  40.460 +            xslt = self.absolute_stylesheet(stylesheet, 'html')
  40.461 +            filename = self.xslt_transform(filename, base + '.html', xslt)
  40.462 +        else:
  40.463 +            if source_format != 'fo':
  40.464 +                # transform DOCBOOK to FO
  40.465 +                xslt = self.absolute_stylesheet(stylesheet, 'fo')
  40.466 +                filename = self.xslt_transform(filename, base + '.fo', xslt)
  40.467 +                to_remove.append(filename)
  40.468 +            # are we arrived ?
  40.469 +            if dest_format == 'fo':
  40.470 +                return filename
  40.471 +            # transform FO to PDF
  40.472 +            filename = self.fop_transform(filename, base + '.pdf')
  40.473 +
  40.474 +           
  40.475 +        self.clean(to_remove)
  40.476 +        return filename
  40.477 +            
  40.478 +
  40.479 +    def absolute_stylesheet(self, stylesheet, type):
  40.480 +        """return the absolute path of the given stylesheet"""
  40.481 +        if isabs(stylesheet):
  40.482 +            return stylesheet
  40.483 +        if stylesheet.endswith('.xsl') and stylesheet.endswith('.xslt'):
  40.484 +            return join(self.config.xsltroot, type, stylesheet)
  40.485 +        return join(self.config.xsltroot, type, stylesheet, 'root.xsl')
  40.486 +
  40.487 +    def clean(self, files):
  40.488 +        """remove temporary files, unless configuration tells to keep them"""
  40.489 +        if self.config.keep:
  40.490 +            return
  40.491 +        if not self.config.quiet and files:
  40.492 +            print '-' * 80
  40.493 +            print 'Removing temporary files'
  40.494 +        for file in files:
  40.495 +            os.remove(file)
  40.496 +        
  40.497 +        
    41.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    41.2 +++ b/xmlformat.py	Wed Apr 26 10:48:09 2006 +0000
    41.3 @@ -0,0 +1,319 @@
    41.4 +#!/usr/bin/python
    41.5 +'''%(PROG)s: format xml source code to xml docbook using roles
    41.6 +
    41.7 +USAGE: %(PROG)s [OPTIONS] <input.xml>...
    41.8 +
    41.9 +OPTIONS:
   41.10 +  -h / --help
   41.11 +       display this help message and exit
   41.12 +       
   41.13 +  -o / --output <OUTPUT_FILE>
   41.14 +       write results in file <OUTPUT_FILE>.
   41.15 +  -s / --stdout
   41.16 +       write results to standard output.
   41.17 +  -e / --encoding iso-8859-1
   41.18 +       specify encoding to use in outputs.
   41.19 +       
   41.20 +  -n / --no-head
   41.21 +       do not insert output headers.
   41.22 +       
   41.23 +  -f / --format <OUTPUT_FORMAT>
   41.24 +       set output format. Default to %(DEFAULT_FORMAT)s.
   41.25 +       Available formats are %(FORMATS)s.
   41.26 +'''
   41.27 +
   41.28 +__revision__ = "$Id: xmlformat.py,v 1.7 2004-10-31 02:18:06 nico Exp $"
   41.29 +
   41.30 +import sys
   41.31 +from os.path import basename
   41.32 +from xml.dom.ext import SplitQName
   41.33 +from xml.sax.handler import ContentHandler
   41.34 +
   41.35 +PROG = basename(sys.argv[0])
   41.36 +FORMATS = ('docbook', 'extended-docbook', 'html')
   41.37 +DEFAULT_FORMAT = 'docbook'
   41.38 +
   41.39 +_ROOT, _STRING, _COMMENT, _NAME, _KEYWORD, _TEXT, _HEAD = 0, 1, 2, 3, 4, 5, 6
   41.40 +
   41.41 +LOGILAB = {
   41.42 +    _HEAD: ('''<?xml version="1.0" encoding="%s"?>
   41.43 +<article>''', '</article>'),
   41.44 +    _ROOT: ('<programlisting role="python">','</programlisting>'),
   41.45 +    _STRING: ('<emphasis role="string">', '</emphasis>'),
   41.46 +    _COMMENT:('<emphasis role="comment">', '</emphasis>'),
   41.47 +    _NAME:   ('<emphasis role="name">', '</emphasis>'),
   41.48 +    _KEYWORD:('<emphasis role="keyword">', '</emphasis>'),
   41.49 +    _TEXT:   ('', '')
   41.50 +    }
   41.51 +DOCBOOK = {
   41.52 +    _HEAD: ('''<?xml version="1.0" encoding="%s"?>
   41.53 +<article>''', '</article>'),
   41.54 +    _ROOT: ('<programlisting>','</programlisting>'),
   41.55 +    _STRING: ('<emphasis>', '</emphasis>'),
   41.56 +    _COMMENT:('<emphasis>', '</emphasis>'),
   41.57 +    _NAME:   ('', ''),
   41.58 +    _KEYWORD:('<emphasis role="bold">', '</emphasis>'),
   41.59 +    _TEXT:   ('', '')
   41.60 +    }
   41.61 +HTML = {
   41.62 +    _HEAD: ('''<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
   41.63 +<html>
   41.64 +<head>
   41.65 +  <meta http-equiv="Content-Type" content="text/html; charset=%s">
   41.66 +  <link rel="stylesheet" type="text/css" href="/intranet.css">
   41.67 +</head>
   41.68 +<body>''','</body>\n</html>'),
   41.69 +    _ROOT: ('<div>', '</div>'),
   41.70 +    _STRING: ('<font color="#004080">', '</font>'),
   41.71 +    _COMMENT:('<font color="#008000">', '</font>'),
   41.72 +    _NAME:   ('', ''),
   41.73 +    _KEYWORD:('<font color="#C00000">', '</font>'),
   41.74 +    _TEXT:   ('', '')
   41.75 +    }
   41.76 +
   41.77 +## full sax handler, print each event to output ###############################
   41.78 +
   41.79 +class XmlFormatSaxHandler(ContentHandler):
   41.80 +    """
   41.81 +    Format an xmlfile to docbook or html
   41.82 +    """
   41.83 +    
   41.84 +    def __init__(self, head=1, output=sys.stdout, encoding='UTF-8'):
   41.85 +        self._out = output
   41.86 +        self._cod = encoding
   41.87 +        self._head = head
   41.88 +        self._o_d = LOGILAB
   41.89 +        self._ind = 0
   41.90 +        
   41.91 +    def set_format(self, format):
   41.92 +        if format == 'docbook':
   41.93 +            self._o_d = DOCBOOK
   41.94 +        elif format == 'extended-docbook':
   41.95 +            self._o_d = LOGILAB           
   41.96 +        if format == 'html':
   41.97 +            self._o_d = HTML
   41.98 +            
   41.99 +    ## content handler ########################################################
  41.100 +    def startDocument(self):
  41.101 +        if self._head:
  41.102 +            self._out.write(self._o_d[_HEAD][0] % self._cod)
  41.103 +        self._out.write(self._o_d[_ROOT][0])
  41.104 +            
  41.105 +    def endDocument(self):
  41.106 +        self._out.write(self._o_d[_ROOT][1])
  41.107 +        if self._head:
  41.108 +            self._out.write(self._o_d[_HEAD][1])
  41.109 +        
  41.110 +    def startElement(self, name, attrs):
  41.111 +        prefix, local = SplitQName(name)
  41.112 +        if prefix:
  41.113 +            self._out.write('&lt;%s%s%s:%s%s%s'.encode(self._cod) % (
  41.114 +                self._o_d[_KEYWORD][0], prefix, self._o_d[_KEYWORD][1],
  41.115 +                self._o_d[_NAME][0], local, self._o_d[_NAME][1]))
  41.116 +        else:
  41.117 +            self._out.write('&lt;%s%s%s'.encode(self._cod) % (
  41.118 +                self._o_d[_KEYWORD][0], local, self._o_d[_KEYWORD][1]))
  41.119 +        for key, val in attrs.items():
  41.120 +            prefix, local = SplitQName(key)
  41.121 +            if prefix:
  41.122 +                self._out.write(' %s%s%s:%s%s%s=%s"%s"%s'.encode(self._cod) % (
  41.123 +                    self._o_d[_KEYWORD][0], prefix, self._o_d[_KEYWORD][1],
  41.124 +                    self._o_d[_NAME][0], local, self._o_d[_NAME][1],
  41.125 +                    self._o_d[_STRING][0], val, self._o_d[_STRING][1]))
  41.126 +            else:
  41.127 +                self._out.write(' %s%s%s=%s"%s"%s'.encode(self._cod) % (
  41.128 +                    self._o_d[_NAME][0], local, self._o_d[_NAME][1],
  41.129 +                    self._o_d[_STRING][0], val, self._o_d[_STRING][1]))
  41.130 +        self._out.write('>')
  41.131 +        
  41.132 +    def endElement(self, name):
  41.133 +        prefix, local = SplitQName(name)
  41.134 +        if prefix:
  41.135 +            self._out.write('&lt;/%s%s%s:%s%s%s>'.encode(self._cod) % (
  41.136 +                self._o_d[_KEYWORD][0], prefix, self._o_d[_KEYWORD][1],
  41.137 +                self._o_d[_NAME][0], local, self._o_d[_NAME][1]))
  41.138 +        else:
  41.139 +            self._out.write('&lt;/%s%s%s>'.encode(self._cod) % (
  41.140 +                self._o_d[_KEYWORD][0], local, self._o_d[_KEYWORD][1]))
  41.141 +        
  41.142 +    def processingInstruction(self, target, data):
  41.143 +        self._out.write('&lt;?%s%s%s %s%s%s>'.encode(self._cod) % (
  41.144 +            self._o_d[_NAME][0], target, self._o_d[_NAME][1],
  41.145 +            self._o_d[_STRING][0], data, self._o_d[_STRING][1]))
  41.146 +        
  41.147 +    def characters(self, ch):
  41.148 +        self._out.write('%s%s%s' % (
  41.149 +            self._o_d[_TEXT][0], ch.replace('<', '&lt;').encode(self._cod),
  41.150 +            self._o_d[_TEXT][1]))
  41.151 +        
  41.152 +    ## lexical handler ########################################################
  41.153 +    def comment(self, comment):
  41.154 +        self._out.write('%s&lt;!--%s-->%s' % (
  41.155 +            self._o_d[_COMMENT][0],
  41.156 +            comment.replace('<', '&lt;').encode(self._cod),
  41.157 +            self._o_d[_COMMENT][1]))
  41.158 +        
  41.159 +    def startCDATA(self):
  41.160 +        self.cdata = 0
  41.161 +        self._out.write('&lt;%s[CDATA[%s' % (
  41.162 +            self._o_d[_KEYWORD][0], self._o_d[_KEYWORD][1]))
  41.163 +        
  41.164 +    def endCDATA(self):
  41.165 +        self._out.write('%s]]%s>' % (
  41.166 +            self._o_d[_KEYWORD][0], self._o_d[_KEYWORD][1]))
  41.167 +        
  41.168 +    def startDTD(self, name, public_id, system_id):
  41.169 +        self._out.write('&lt;%s!DOCTYPE%s %s'.encode(self._cod) % (
  41.170 +            self._o_d[_KEYWORD][0], self._o_d[_KEYWORD][1], name))
  41.171 +        if public_id:
  41.172 +            self._out.write(' PUBLIC %s"%s"%s %s"%s"%s [\n'.encode(self._cod) % (
  41.173 +                self._o_d[_STRING][0], public_id, self._o_d[_STRING][1],
  41.174 +                self._o_d[_STRING][0], system_id, self._o_d[_STRING][1]))
  41.175 +        else:
  41.176 +            self._out.write(' SYSTEM %s"%s"%s [\n'.encode(self._cod) % (
  41.177 +                self._o_d[_STRING][0], system_id, self._o_d[_STRING][1]))
  41.178 +            
  41.179 +    def endDTD(self):
  41.180 +        self._out.write(']>\n')
  41.181 +        
  41.182 +    def startEntity(self, name):
  41.183 +        pass
  41.184 +
  41.185 +    def endEntity(self, name):
  41.186 +        pass
  41.187 +
  41.188 +    ## decl handler ###########################################################
  41.189 +    def internalEntityDecl(self, name, value):
  41.190 +        self._out.write('&lt;%s!ENTITY%s %s %s>\n'.encode(self._cod) % (
  41.191 +            self._o_d[_KEYWORD][0], self._o_d[_KEYWORD][1], name, value))
  41.192 +            
  41.193 +    def externalEntityDecl(self, name, public_id, system_id):
  41.194 +        self._out.write('&lt;%s!ENTITY%s %s'.encode(self._cod) % (
  41.195 +            self._o_d[_KEYWORD][0], self._o_d[_KEYWORD][1], name))
  41.196 +        if public_id:
  41.197 +            self._out.write(' PUBLIC %s"%s"%s %s"%s"%s>\n'.encode(self._cod) % (
  41.198 +                self._o_d[_STRING][0], public_id, self._o_d[_STRING][1],
  41.199 +                self._o_d[_STRING][0], system_id, self._o_d[_STRING][1]))
  41.200 +        else:
  41.201 +            self._out.write(' SYSTEM %s"%s"%s>\n'.encode(self._cod) % (
  41.202 +                self._o_d[_STRING][0], system_id, self._o_d[_STRING][1]))
  41.203 +
  41.204 +    def elementDecl(self, elem_name, content_model):
  41.205 +        c_m = _decode_content_model(content_model)
  41.206 +        self._out.write('&lt;%s!ELEMENT%s %s %s>\n'.encode(self._cod) % (
  41.207 +             self._o_d[_KEYWORD][0], self._o_d[_KEYWORD][1], elem_name,
  41.208 +            c_m))
  41.209 +        
  41.210 +    def attributeDecl(self, elem_name, attr_name, type_d, value_def, value):
  41.211 +        import types
  41.212 +        if type(type_d) is types.ListType:
  41.213 +            s = ''
  41.214 +            for pos in type_d:
  41.215 +                if not s:
  41.216 +                    s = '(%s' % pos
  41.217 +                else:
  41.218 +                    s = '%s|%s' % (s, pos)
  41.219 +            s = '%s)' % s
  41.220 +            self._out.write('&lt;%s!ATTLIST%s %s %s %s %s>\n'.encode(self._cod) % (
  41.221 +                self._o_d[_KEYWORD][0], self._o_d[_KEYWORD][1], elem_name,
  41.222 +                attr_name, s , value_def))
  41.223 +        else:
  41.224 +            self._out.write('&lt;%s!ATTLIST%s %s %s %s>\n'.encode(self._cod) % (
  41.225 +                self._o_d[_KEYWORD][0], self._o_d[_KEYWORD][1], elem_name,
  41.226 +                attr_name, type))
  41.227 +            
  41.228 +C_OP, C_VAL, C_NUM = 0, 1, 2
  41.229 +def _decode_content_model(content_m):
  41.230 +    s = ''
  41.231 +    if content_m[C_OP] == ',':        
  41.232 +        for c_m in content_m[C_VAL]:
  41.233 +            if not s:
  41.234 +                s = '(%s' % _decode_content_model(c_m)
  41.235 +            else:
  41.236 +                s = '%s, %s' % (s, _decode_content_model(c_m))
  41.237 +        s = '%s)%s' % (s, content_m[C_NUM] )
  41.238 +    elif content_m[C_OP] == '|':
  41.239 +        for c_m in content_m[C_VAL]:
  41.240 +            if not s:
  41.241 +                s = '(%s' % _decode_content_model(c_m)
  41.242 +            else:
  41.243 +                s = '%s|%s' % (s, _decode_content_model(c_m))
  41.244 +        s = '%s)%s' % (s, content_m[C_NUM] )
  41.245 +    else:
  41.246 +        s = '%s%s' % (s, content_m[C_OP])
  41.247 +        s = '%s%s' % (s, content_m[-1])
  41.248 +    return s
  41.249 +            
  41.250 +
  41.251 +def run(args):
  41.252 +    """
  41.253 +    main
  41.254 +    """
  41.255 +    import getopt, os
  41.256 +    from xml.sax import make_parser
  41.257 +    from xml.sax.handler import property_lexical_handler
  41.258 +    from xml.sax.handler import property_declaration_handler
  41.259 +    ## get options
  41.260 +    (options, args) = getopt.getopt(args,
  41.261 +                                'he:o:sf:n',
  41.262 +                                ['help', 'encoding=', 'output=', 'stdout',
  41.263 +                                 'format=', 'no-head'])
  41.264 +    encod, output, dest, head, format = 'UTF-8', None, None, 1, DEFAULT_FORMAT
  41.265 +    for opt in options:
  41.266 +        if opt[0] == '-h' or opt[0] == '--help':
  41.267 +            print __doc__ % globals()
  41.268 +            return
  41.269 +        elif opt[0] == '-o' or opt[0] == '--output':
  41.270 +            output = opt[1]
  41.271 +            dest = open(output, 'w')
  41.272 +        elif opt[0] == '-s' or opt[0] == '--stdout':
  41.273 +            dest = sys.stdout
  41.274 +        elif opt[0] == '-o' or opt[0] == '--format':
  41.275 +            val = opt[1].lower()
  41.276 +            if not val in FORMATS:
  41.277 +                raise 'Unknown format %s' % val
  41.278 +            format = val
  41.279 +        elif opt[0] == '-e' or opt[0] == '--encoding':
  41.280 +            encod = opt[1]
  41.281 +        elif opt[0] == '-n' or opt[0] == '--no-head':
  41.282 +            head = 0
  41.283 +    if len(args) == 0:
  41.284 +        print __doc__ % globals()
  41.285 +        return
  41.286 +    ## transforms source files (xmlproc support property_lexical_handler while
  41.287 +    ##      pyexpat doen't)
  41.288 +    #p = make_parser(['xml.sax.drivers2.drv_xmlproc'])
  41.289 +    p = make_parser()
  41.290 +    for filename in args:
  41.291 +        source = open(filename, 'r')
  41.292 +        ## prepare handler
  41.293 +        if not dest:
  41.294 +            if filename[-4:] not in ('.xml', '.dtd'):
  41.295 +                sys.stderr.write('Unknown extension %s, ignored file %s\n' % \
  41.296 +                                 (filename[-4:], filename))
  41.297 +                continue
  41.298 +            dest = open('%s_dcbk.xml' % os.path.basename(filename)[:-4], 'w+')
  41.299 +        h = XmlFormatSaxHandler(head, dest, encod)
  41.300 +        h.set_format(format)
  41.301 +        p.setContentHandler(h)
  41.302 +        try:
  41.303 +            p.setProperty(property_lexical_handler, h)
  41.304 +        except Exception, exc:
  41.305 +            print exc
  41.306 +        try:
  41.307 +            p.setProperty(property_declaration_handler, h)
  41.308 +        except Exception, exc:
  41.309 +            print exc
  41.310 +        sys.stderr.write("Formatting %s ...\n" % filename)
  41.311 +
  41.312 +        ## parse and write colorized version to output file
  41.313 +        p.parse(source)
  41.314 +        
  41.315 +        source.close()
  41.316 +        if not output and not dest is sys.stdout:
  41.317 +            dest.close()
  41.318 +            dest = None
  41.319 +
  41.320 +    
  41.321 +if __name__ == "__main__":
  41.322 +    run(sys.argv[1:])