logilab/doctools
changeset 0:cc367abb080e
forget the past.
forget the past.
forget the past.
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!<k$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("&", "&")
33.188 + text = text.replace("<", "<")
33.189 + text = text.replace('"', """)
33.190 + text = text.replace(">", ">")
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:: œ
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('<%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('<%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('</%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('</%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('<?%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('<', '<').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<!--%s-->%s' % (
41.155 + self._o_d[_COMMENT][0],
41.156 + comment.replace('<', '<').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('<%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('<%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('<%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('<%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('<%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('<%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('<%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:])
