Rev 4008: (Colin D Bennett) Generate PDF version of the User Guide. in file:///home/pqm/archives/thelove/bzr/%2Btrunk/

Canonical.com Patch Queue Manager pqm at pqm.ubuntu.com
Sat Feb 14 16:54:11 GMT 2009


At file:///home/pqm/archives/thelove/bzr/%2Btrunk/

------------------------------------------------------------
revno: 4008
revision-id: pqm at pqm.ubuntu.com-20090214165408-vlwzj9x1qax0i1b0
parent: pqm at pqm.ubuntu.com-20090213220525-f4o6y76g5mjtxb7k
parent: colin at gibibit.com-20090212172102-0t7xufywds9l1g33
committer: Canonical.com Patch Queue Manager <pqm at pqm.ubuntu.com>
branch nick: +trunk
timestamp: Sat 2009-02-14 16:54:08 +0000
message:
  (Colin D Bennett) Generate PDF version of the User Guide.
added:
  tools/prepare_for_latex.py     prepare_for_latex.py-20090212165735-79tn1t4dhnxyz7m9-1
  tools/rst2pdf.py               rst2pdf.py-20090212165735-79tn1t4dhnxyz7m9-2
modified:
  .bzrignore                     bzrignore-20050311232317-81f7b71efa2db11a
  Makefile                       Makefile-20050805140406-d96e3498bb61c5bb
    ------------------------------------------------------------
    revno: 4000.4.5
    revision-id: colin at gibibit.com-20090212172102-0t7xufywds9l1g33
    parent: colin at gibibit.com-20090212171833-s8k62ifqy4uf4cxc
    committer: Colin D Bennett <colin at gibibit.com>
    branch nick: pdf-docs
    timestamp: Thu 2009-02-12 09:21:02 -0800
    message:
      Strip trailing whitespace.
    modified:
      Makefile                       Makefile-20050805140406-d96e3498bb61c5bb
      tools/prepare_for_latex.py     prepare_for_latex.py-20090212165735-79tn1t4dhnxyz7m9-1
    ------------------------------------------------------------
    revno: 4000.4.4
    revision-id: colin at gibibit.com-20090212171833-s8k62ifqy4uf4cxc
    parent: colin at gibibit.com-20090212171141-bq8gllubhwyfgr52
    committer: Colin D Bennett <colin at gibibit.com>
    branch nick: pdf-docs
    timestamp: Thu 2009-02-12 09:18:33 -0800
    message:
      Put PDF cleanup in clean-docs target; added note on Inkscape requirement.
    modified:
      Makefile                       Makefile-20050805140406-d96e3498bb61c5bb
    ------------------------------------------------------------
    revno: 4000.4.3
    revision-id: colin at gibibit.com-20090212171141-bq8gllubhwyfgr52
    parent: colin at gibibit.com-20090212170752-roshfon2ckj8atyg
    committer: Colin D Bennett <colin at gibibit.com>
    branch nick: pdf-docs
    timestamp: Thu 2009-02-12 09:11:41 -0800
    message:
      Ignore all generated PDFs, not just the A4 size document.
    modified:
      .bzrignore                     bzrignore-20050311232317-81f7b71efa2db11a
    ------------------------------------------------------------
    revno: 4000.4.2
    revision-id: colin at gibibit.com-20090212170752-roshfon2ckj8atyg
    parent: colin at gibibit.com-20090212165739-02xv63odccfmxomw
    committer: Colin D Bennett <colin at gibibit.com>
    branch nick: pdf-docs
    timestamp: Thu 2009-02-12 09:07:52 -0800
    message:
      Made PDF documents a separate target for people who don't have LaTeX installed.
    modified:
      Makefile                       Makefile-20050805140406-d96e3498bb61c5bb
    ------------------------------------------------------------
    revno: 4000.4.1
    revision-id: colin at gibibit.com-20090212165739-02xv63odccfmxomw
    parent: pqm at pqm.ubuntu.com-20090211011240-gv0zdxmwomt3ndtn
    committer: Colin D Bennett <colin at gibibit.com>
    branch nick: pdf-docs
    timestamp: Thu 2009-02-12 08:57:39 -0800
    message:
      Generate PDF version of the User Guide.
    added:
      tools/prepare_for_latex.py     prepare_for_latex.py-20090212165735-79tn1t4dhnxyz7m9-1
      tools/rst2pdf.py               rst2pdf.py-20090212165735-79tn1t4dhnxyz7m9-2
    modified:
      .bzrignore                     bzrignore-20050311232317-81f7b71efa2db11a
      Makefile                       Makefile-20050805140406-d96e3498bb61c5bb
=== modified file '.bzrignore'
--- a/.bzrignore	2008-12-11 02:18:59 +0000
+++ b/.bzrignore	2009-02-12 17:11:41 +0000
@@ -53,3 +53,5 @@
 bzrlib/_*.pyd
 # generated help topics
 doc/en/user-reference/*.txt
+./doc/en/user-guide/latex_prepared
+./doc/en/user-guide/*.pdf

=== modified file 'Makefile'
--- a/Makefile	2008-12-15 04:37:10 +0000
+++ b/Makefile	2009-02-12 17:21:02 +0000
@@ -111,6 +111,26 @@
 doc/en/user-guide/index.html: $(wildcard $(addsuffix /*.txt, doc/en/user-guide)) 
 	$(rst2html) --stylesheet=../../default.css doc/en/user-guide/index.txt $@
 
+# Set the paper size for PDF files.
+# Options:  'a4' (ISO A4 size), 'letter' (US Letter size)
+PAPERSIZE = a4
+PDF_DOCS := doc/en/user-guide/user-guide.$(PAPERSIZE).pdf
+
+# Copy and modify the RST sources, and convert SVG images to PDF
+# files for use a images in the LaTeX-generated PDF.
+# Then generate the PDF output from the modified RST sources.
+doc/en/user-guide/user-guide.$(PAPERSIZE).pdf: $(wildcard $(addsuffix /*.txt, doc/en/user-guide))
+	mkdir -p doc/en/user-guide/latex_prepared
+	$(PYTHON) tools/prepare_for_latex.py \
+	    --out-dir=doc/en/user-guide/latex_prepared \
+	    --in-dir=doc/en/user-guide
+	cd doc/en/user-guide/latex_prepared && \
+	    $(PYTHON) ../../../../tools/rst2pdf.py \
+	        --documentoptions=10pt,$(PAPERSIZE)paper \
+	        --input-encoding=UTF-8:strict --output-encoding=UTF-8:strict \
+	        --strict --title="Bazaar User Guide" \
+	        index.txt ../user-guide.$(PAPERSIZE).pdf
+
 doc/developers/%.html: doc/developers/%.txt
 	$(rst2html) --stylesheet=../default.css $< $@
 
@@ -163,10 +183,15 @@
 html-docs: docs
 	$(PYTHON) tools/win32/ostools.py copytree $(WEB_DOCS) $(HTMLDIR)
 
+# Produce PDF documents.  Requires pdfLaTeX, rubber, and Inkscape.
+pdf-docs: $(PDF_DOCS)
+
 # clean produced docs
 clean-docs:
 	$(PYTHON) tools/win32/ostools.py remove $(ALL_DOCS) \
-	$(HTMLDIR) $(derived_txt_files)
+	    $(HTMLDIR) $(derived_txt_files)
+	rm -f doc/en/user-guide/*.pdf
+	rm -rf doc/en/user-guide/latex_prepared
 
 
 ### Windows Support ###

=== added file 'tools/prepare_for_latex.py'
--- a/tools/prepare_for_latex.py	1970-01-01 00:00:00 +0000
+++ b/tools/prepare_for_latex.py	2009-02-12 17:21:02 +0000
@@ -0,0 +1,173 @@
+#!/usr/bin/python
+#
+# Modify reStructuredText 'image' directives by adding a percentage 'width'
+# attribute so that the images are scaled to fit on the page when the document
+# is renderd to LaTeX, and add a center alignment.
+#
+# Also convert references to PNG images to use PDF files generated from SVG
+# files if available.
+#
+# Without the explicit size specification, the images are ridiculously huge and
+# most extend far off the right side of the page.
+#
+# Copyright (C) 2009 Colin D Bennett
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+import os
+import re
+import shutil
+import sys
+from sys import argv
+from subprocess import call
+
+verbose = False
+
+IMAGE_DIRECTIVE_PATTERN = re.compile(ur'^..\s+image::\s+(.*)\s+$')
+DIRECTIVE_ELEMENT_PATTERN = re.compile(ur'^\s+:[^:]+:\s+')
+
+class Converter(object):
+    def __init__(self, srcdir, destdir):
+        self.srcdir = srcdir
+        self.destdir = destdir
+
+    # Process .txt files in sourcedir, generating output in destdir.
+    def process_files(self):
+        for filename in os.listdir(self.srcdir):
+            # Process all text files in the current directory.
+            if filename.endswith('.txt'):
+                inpath = os.path.join(self.srcdir, filename)
+                outpath = os.path.join(self.destdir, filename)
+                self._process_file(inpath, outpath)
+
+    def _process_file(self, inpath, outpath):
+        infile = open(inpath, 'r')
+        outfile = open(outpath, 'w')
+        foundimg = False
+        for line in infile:
+            if foundimg and DIRECTIVE_ELEMENT_PATTERN.match(line) is None:
+                if verbose:
+                    print('Fixing image directive')
+                # The preceding image directive has no elements.
+                outfile.write(' :width: 85%\n')
+                outfile.write(' :align: center\n')
+            foundimg = False
+
+            image_fixer = ImageFixer(self.srcdir, self.destdir)
+            image_fixer_lambda = lambda match: image_fixer.substitute_pdf_image(match)
+            line = IMAGE_DIRECTIVE_PATTERN.sub(image_fixer_lambda, line)
+            directive_match = IMAGE_DIRECTIVE_PATTERN.match(line)
+            if directive_match is not None:
+                image_src = directive_match.group(1)
+                if verbose:
+                    print('Image ' + image_src + ' in ' + filename
+                          + ': ' + line.strip())
+
+                foundimg = True
+            outfile.write(line)
+        outfile.close()
+        infile.close()
+
+class ImageFixer(object):
+    def __init__(self, srcdir, destdir):
+        self.srcdir = srcdir
+        self.destdir = destdir
+
+    def substitute_pdf_image(self, match):
+        prefix = match.string[:match.start(1)]
+        newname = self.convert_image_to_pdf(match.group(1))
+        suffix = match.string[match.end(1):]
+        return prefix + newname + suffix
+
+    def replace_extension(self, path, newext):
+        if path.endswith(newext):
+            raise Exception("File '" + path + "' already has extension '"
+                            + newext +"'")
+        dot = path.rfind('.')
+        if dot == -1:
+            return path + newext
+        else:
+            return path[:dot] + newext
+
+    # Possibly use an SVG alternative to a PNG image, converting the SVG image
+    # to a PDF first.  Whether or not a conversion is made, the image to use is
+    # written to the destination directory and the path to use in the RST #
+    # source is returned.
+    def convert_image_to_pdf(self, filename):
+        # Make the directory structure for the image in the destination dir.
+        image_dirname = os.path.dirname(filename)
+        if image_dirname:
+            image_dirpath = os.path.join(self.destdir, image_dirname)
+            if not os.path.exists(image_dirpath):
+                os.mkdir(image_dirpath)
+
+        # Decide how to handle this image.
+        if filename.endswith('.png'):
+            # See if there is a vector alternative.
+            svgfile = self.replace_extension(filename, '.svg')
+            svgpath = os.path.join(self.srcdir, svgfile)
+            if os.path.exists(svgpath):
+                if verbose:
+                    print('Using SVG alternative to PNG')
+                # Convert SVG to PDF with Inkscape.
+                pdffile = self.replace_extension(filename, '.pdf')
+                pdfpath = os.path.join(self.destdir, pdffile)
+                if call(['/usr/bin/inkscape',
+                         '--export-pdf=' + pdfpath, svgpath]) != 0:
+                    raise Exception("Conversion to pdf failed")
+                return pdffile
+
+        # No conversion, just copy the file.
+        srcpath = os.path.join(self.srcdir, filename)
+        destpath = os.path.join(self.destdir, filename)
+        shutil.copyfile(srcpath, destpath)
+        return filename
+
+if __name__ == '__main__':
+    IN_DIR_OPT = '--in-dir='
+    OUT_DIR_OPT = '--out-dir='
+    srcdir = None
+    destdir = None
+
+    if len(argv) < 2:
+        print('Usage: ' + argv[0] + ' ' + IN_DIR_OPT + 'INDIR '
+              + OUT_DIR_OPT + 'OUTDIR')
+        print
+        print('This will convert all .txt files in INDIR into file in OUTDIR')
+        print('while adjusting the use of images and possibly converting SVG')
+        print('images to PDF files so LaTeX can include them.')
+        sys.exit(1)
+
+    for arg in argv[1:]:
+        if arg == '-v' or arg == '--verbose':
+            verbose = True
+        elif arg.startswith(IN_DIR_OPT):
+            srcdir = arg[len(IN_DIR_OPT):]
+        elif arg.startswith(OUT_DIR_OPT):
+            destdir = arg[len(OUT_DIR_OPT):]
+        else:
+            print('Invalid argument ' + arg)
+            sys.exit(1)
+
+    if srcdir is None or destdir is None:
+        print('Please specify the ' + IN_DIR_OPT + ' and '
+              + OUT_DIR_OPT + ' options.')
+        sys.exit(1)
+
+    if not os.path.exists(destdir):
+        os.mkdir(destdir)
+    Converter(srcdir, destdir).process_files()
+
+# vim: set ts=4 sw=4 et:

=== added file 'tools/rst2pdf.py'
--- a/tools/rst2pdf.py	1970-01-01 00:00:00 +0000
+++ b/tools/rst2pdf.py	2009-02-12 16:57:39 +0000
@@ -0,0 +1,179 @@
+#!/usr/bin/env python
+# -*- coding: utf8 -*-
+# $Id: rst2pdf.py 5560 2008-05-20 13:00:31Z milde $
+
+# rst2pdf.py
+# ==========
+# ::
+
+"""
+A front end to the Docutils Publisher, producing PDF.
+
+Produces a latex file with the "latex" writer and converts
+it to PDF with the "rubber" building system for LaTeX documents. 
+"""
+
+# ``rst2pdf.py`` is a PDF front-end for docutils that is compatible
+# with the ``rst2*.py`` front ends of the docutils_ suite.
+# It enables the generation of PDF documents from a reStructuredText source in
+# one step.
+# 
+# It is implemented as a combination of docutils' ``rst2latex.py`` 
+# by David Goodger and rubber_ by Emmanuel Beffara.
+# 
+# Copyright: © 2008 Günter Milde
+#            Licensed under the `Apache License, Version 2.0`_
+#            Provided WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND
+# 
+# Changelog
+# ---------
+# 
+# =====  ==========  =======================================================
+# 0.1    2008-05-20  first attempt
+# =====  ==========  =======================================================
+# 
+# :: 
+
+_version = 0.1
+
+
+# Imports
+# =======
+# ::
+
+#from pprint import pprint # for debugging
+import os
+
+# Docutils::
+
+try:
+    import locale
+    locale.setlocale(locale.LC_ALL, '')
+except:
+    pass
+
+from docutils.core import default_usage, default_description, Publisher
+
+# Rubber (rubber is not installed in the PYTHONPATH)::
+
+import sys
+sys.path.append("/usr/share/rubber")
+
+try:
+    import rubber.cmdline
+    import rubber.cmd_pipe
+except ImportError:
+    print "Cannot find the rubber modules, rubber not installed correctly."
+    sys.exit(1)
+
+# Generate the latex file
+# =======================
+# 
+# We need to replace the <destination> by a intermediate latex file path.
+# The most reliable way to get the value of <destination> is to 
+# call the Publisher "by hand", and query its settings. 
+# 
+# Modeled on the publish_cmdline() function of docutils.core
+# 
+# Default values::
+
+reader=None
+reader_name='standalone'
+parser=None
+parser_name='restructuredtext'
+writer=None
+writer_name='pseudoxml'
+settings=None
+settings_spec=None
+settings_overrides=None
+config_section=None
+enable_exit_status=1
+argv=None
+usage=default_usage
+description=default_description
+
+# Argument values given to publish_cmdline() in rst2latex.py::
+
+description = ('Generates PDF documents from standalone reStructuredText '
+               'sources using the "latex" Writer and the "rubber" '
+               'building system for LaTeX documents.  ' + default_description)
+writer_name = 'latex'
+
+# Set up the publisher::
+
+pub = Publisher(reader, parser, writer, settings=settings)
+pub.set_components(reader_name, parser_name, writer_name)
+
+# Parse the command line args 
+# (Publisher.publish does this in a try statement)::
+
+pub.process_command_line(argv, usage, description, settings_spec, 
+                         config_section, **(settings_overrides or {}))
+# pprint(pub.settings.__dict__)
+
+# Get source and destination path::
+
+source = pub.settings._source
+destination = pub.settings._destination
+# print source, destination
+
+# Generate names for the temporary files and set ``destination`` to temporary
+# latex file:
+# 
+# make_name() from rubber.cmd_pipe checks that no existing file is
+# overwritten. If we are going to support rubbers ``--inplace`` and ``--into``
+# options, the chdir() must occure before this point to have the check in the
+# right directory. ::
+
+tmppath = rubber.cmd_pipe.make_name()
+texpath = tmppath + ".tex"
+pdfpath = tmppath + ".pdf"
+
+pub.settings._destination = texpath
+
+# Now do the rst -> latex conversion::
+
+pub.publish(argv, usage, description, settings_spec, settings_overrides, 
+            config_section=config_section, enable_exit_status=enable_exit_status)
+
+
+# Generating the PDF document with rubber
+# =======================================
+# 
+# 
+# rubber_ has no documentet API for programmatic use. We simualate a command
+# line call and pass command line arguments (see man: rubber-pipe) in an array::
+
+rubber_argv = ["--pdf",    # use pdflatex to produce PDF
+               "--short",   # Display LaTeX’s error messages one error per line.
+               texpath
+              ]
+
+# Get a TeX processing class instance and do the latex->pdf conversion::
+
+tex_processor = rubber.cmdline.Main()
+tex_processor(rubber_argv)
+
+# Rename output to _destination or print to stdout::
+
+if destination is None:
+    pdffile = file(pdfpath)
+    print  pdffile.read()
+    pdffile.close()
+else:
+    os.rename(pdfpath, destination)
+
+# Clean up (remove intermediate files)
+# 
+# ::
+
+tex_processor(["--clean"] + rubber_argv)
+os.remove(texpath)
+
+
+# .. References
+# 
+# .. _docutils: http://docutils.sourceforge.net/
+# .. _rubber: http://www.pps.jussieu.fr/~beffara/soft/rubber/
+# .. _Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
+#                                                                         




More information about the bazaar-commits mailing list