Posts about rst2pdf (old posts, page 6)

2010-10-06 21:39

Rst2pdf 0.16 is out!

Finally, a new release of rst2pdf!

You can get it at its site: http://rst2pdf.googlecode.com

rst2pdf is a tool to convert restructured text (a light, cool markup language) to PDF using reportlab instead of LaTeX.

It has been used for many things, from books, to magazines, to brochures, to manuals, to websites and has lots of features:

  • Font embedding (TTF or Type1 fonts)
  • Cascading Stylesheets
  • Extremely flexible plugin architecture (you can do things like render the headings from arbitrary SVG files!)
  • Sphinx integration.
  • Configurable page layouts
  • Custom cover pages via templates
  • And much, much more...

The biggest change in 0.16 is surely the improved support for Sphinx 1.0.x so if you are using Sphinx, you really want this version.

Also, it has a ton of bugfixes, and a few minor but useful new features.

Here's the whole changelog if you don't believe me:

  • Fixed Issue 343: Plugged memory leak in the RSON parser.
  • Fix for Issue 287: there is still a corner case if you have two sections with the same title, at the same level, in the same page, in different files where the links will break.
  • Fixed Issue 367: german-localized dates are MM. DD. YYYY so when used in sphinx's template cover they appeared weird, like a list item. Fixed with a minor workaround in the template.
  • Fixed Issue 366: links to "#" make no sense on a PDF file
  • Made definitions from definition lists more stylable.
  • Moved definition lists to SplitTables, so you can have very long definitions.
  • Fixed Issue 318: Implemented Domain specific indexes for Sphinx 1.0.x
  • Fixed Index links when using Sphinx/pdfbuilder.
  • Fixed Issue 360: Set literal.wordWrap to None by default so it doesn't inherit wordWrap CJK when you use the otherwise correct japanese settings. In any case, literal blocks are not supposed to wrap at all.
  • Switched pdfbuilder to use SplitTables by default (it made no sense not to do it)
  • Fixed Issue 365: some TTF fonts don't validate but they work anyway.
  • Set a valid default baseurl for Sphinx (makes it much faster!)
  • New feature: --use-numbered-links to show section numbers in links to sections, like "See section 2.3 Termination"
  • Added stylesheets for landscape paper sizes (i.e: a4-landscape.style)
  • Fixed Issue 364: Some options not respected when passed in per-doc options in sphinx.
  • Fixed Issue 361: multiple linebreaks in line blocks were collapsed.
  • Fixed Issue 363: strange characters in some cases in math directive.
  • Fixed Issue 362: Smarter auto-enclosing of equations in $...$
  • Fixed Issue 358: --real--footnotes defaults to False, but help text indicates default is True
  • Fixed Issue 359: Wrong --fit-background-mode help string
  • Fixed Issue 356: missing cells if a cell spawns rows and columns.
  • Fixed Issue 349: Work correctly with languages that are available in form aa_bb and not aa (example: zh_cn)
  • Fixed Issue 345: give file/line info when there is an error in a raw PDF directive.
  • Fixed Issue 336: JPEG images should work even without PIL (but give a warning because sizes will probably be wrong)
  • Fixed Issue 351: footnote/citation references were generated incorrectly, which caused problems if there was a citation with the same text as a heading.
  • Fixed Issue 353: better handling of graphviz, so that it works without vectorpdf but gives a warning about it.
  • Fixed Issue 354: make todo_node from sphinx customizable.
  • Fixed bug where nested lists broke page layout if the page was small.
  • Smarter --inline-links option
  • New extension: fancytitles, see //ralsina.me/weblog/posts/BB906.html
  • New feature: tab-width option in code-block directive (defaults to 8).
  • Fixed Issue 340: endnotes/footnotes were not styled.
  • Fixed Issue 339: class names using _ were not usable.
  • Fixed Issue 335: ugly crash when using images in some specific places (looks like a reportlab bug)
  • Fixed Issue 329: make the figure alignment/class attributes work more like LaTeX than HTML.
  • Fixed Issue 328: list item styles were being ignored.
  • Fixed Issue 186: new --use-floating-images makes images with :align: set work like in HTML, with the next flowable flowing beside it.
  • Fixed Issue 307: header/footer from stylesheet now supports inline rest markup and substitutions defined in the main document.
  • New pdf_toc_depth option for Sphinx/pdfbuilder
  • New pdf_use_toc option for Sphinx/pdfbuilder
  • Fixed Issue 308: compatibility with reportlab from SVN
  • Fixed Issue 323: errors in the config.sample made it work weird.
  • Fixed Issue 322: Image substitutions didn't work in document title.
  • Implemented Issue 321: underline and strikethrough available in stylesheet.
  • Fixed Issue 317: Ugly error message when file does not exist

2010-08-05 18:55

Extending rst2pdf: easy and powerful

I do almost all my business writing (and my book) using restructured text. And when I want to produce print-quality output, I tend to use my own tool, rst2pdf.

It's popular, surely my most popular program, but very few know it's also extremely easy to extend (hat tip to Patrick Maupin, who wrote this part!). And not only that, but you can make it do some amazing stuff with a little effort.

To show that, let's create the most dazzling section headings known to man ( Let's see you do what this baby can do in LaTeX ;-).

First: define the problem.

The titles rst2pdf can produce are boring. If you pull every lever and push every button, you may end with the title text, in a Comic Sans, right-aligned, in pink lettering, with avocado-green background and a red border.

And that's as far as the customization capabilities go using stylesheets. That's usually enough, because rst2pdf is not meant for brochures or something like that (but I have done it).

The real problem is that when you get all graphic-designer on rst2pdf, you lose document structure, because you are not being semantic.

Second: define the goal.

So, imagine you want to make a heading that looks like this:

fancytitles1

The image is taken from the library of congress with some light (and bad) gimping by me to leave that empty space at the left, and the title was added using Inkscape.

Can you do that with rst2pdf? Hell no you can't. Not without coding. So let's code an extension that lets you create any heading you like within the limits of Inkscape!

First, we create a SVG template for the headings (it's a bit big because it has the bitmap embedded).

Three: the image-heading flowable

Suppose you have an image of the heading just like the one above. How would you draw that in a PDF? In reportlab, you do that using flowables which are elements that compose the story that is your document. These flowables are arranged in pages, and that's your PDF.

If you are doing a heading, there's a bit more, in that you need to add a bookmark, so it appears on the PDF table of contents.

So, here's a flowable that does just that. It's cobbled from pieces inside rst2pdf, and is basically an unholy mix of Heading and MyImage:

class FancyHeading(MyImage):
  '''This is a cross between the Heading flowable, that adds outline
  entries so you have a PDF TOC, and MyImage, that draws images'''

  def __init__(self, *args, **kwargs):
      # The inicialization is taken from rst2pdf.flowables.Heading
      self.stext = kwargs.pop('text')
      # Cleanup title text
      self.stext = re.sub(r'<[^>]*?>', '', unescape(self.stext))
      self.stext = self.stext.strip()

      # Stuff needed for the outline entry
      self.snum = kwargs.pop('snum')
      self.level = kwargs.pop('level')
      self.parent_id= kwargs.pop('parent_id')


      MyImage.__init__(self, *args, **kwargs)

  def drawOn(self,canv,x,y,_sW):

      ## These two lines are magic.
      #if isinstance(self.parent_id, tuple):
          #self.parent_id=self.parent_id[0]

      # Add outline entry. This is copied from rst2pdf.flowables.heading
      canv.bookmarkHorizontal(self.parent_id,0,0+self.image.height)

      if canv.firstSect:
          canv.sectName = self.stext
          canv.firstSect=False
          if self.snum is not None:
              canv.sectNum = self.snum
          else:
              canv.sectNum = ""

      canv.addOutlineEntry(self.stext.encode('utf-8','replace'),
                                self.parent_id.encode('utf-8','replace'),
                                int(self.level), False)

      # And let MyImage do all the drawing
      MyImage.drawOn(self,canv,x,y,_sW)

And how do we tell rst2df to use that instead of a regular Heading? by overriding the TitleHandler class. Here's where the extension magic kicks in.

If you define, in an extension, a class like this:

class FancyTitleHandler(genelements.HandleParagraph, docutils.nodes.title):

Then that class will handle all docutils nodes of class docutils.nodes.title. Here, I just took rst2pdf.genelements.HandleTitle and changed how it works for level-1 headings, making it generate a FancyHeading instead of a Heading... and that's all there is to it.

class FancyTitleHandler(genelements.HandleParagraph, docutils.nodes.title):
  '''
  This class will handle title nodes.

  It takes a "titletemplate.svg", replaces TITLEGOESHERE with
  the actual title text, and draws that using the FancyHeading flowable
  (see below).

  Since this class is defined in an extension, it
  effectively replaces rst2pdf.genelements.HandleTitle.
  '''

  def gather_elements(self, client, node, style):
      # This method is copied from the HandleTitle class
      # in rst2pdf.genelements.

      # Special cases: (Not sure this is right ;-)
      if isinstance(node.parent, docutils.nodes.document):
          #node.elements = [Paragraph(client.gen_pdftext(node),
                                      #client.styles['title'])]
          # The visible output is now done by the cover template
          node.elements = []
          client.doc_title = node.rawsource
          client.doc_title_clean = node.astext().strip()
      elif isinstance(node.parent, docutils.nodes.topic):
          node.elements = [Paragraph(client.gen_pdftext(node),
                                      client.styles['topic-title'])]
      elif isinstance(node.parent, docutils.nodes.Admonition):
          node.elements = [Paragraph(client.gen_pdftext(node),
                                      client.styles['admonition-title'])]
      elif isinstance(node.parent, docutils.nodes.table):
          node.elements = [Paragraph(client.gen_pdftext(node),
                                      client.styles['table-title'])]
      elif isinstance(node.parent, docutils.nodes.sidebar):
          node.elements = [Paragraph(client.gen_pdftext(node),
                                      client.styles['sidebar-title'])]
      else:
          # Section/Subsection/etc.
          text = client.gen_pdftext(node)
          fch = node.children[0]
          if isinstance(fch, docutils.nodes.generated) and \
              fch['classes'] == ['sectnum']:
              snum = fch.astext()
          else:
              snum = None
          key = node.get('refid')
          maxdepth=4
          if reportlab.Version > '2.1':
              maxdepth=6

          # The parent ID is the refid + an ID to make it unique for Sphinx
          parent_id=(node.parent.get('ids', [None]) or [None])[0]+u'-'+unicode(id(node))
          if client.depth > 1:
              node.elements = [ Heading(text,
                      client.styles['heading%d'%min(client.depth, maxdepth)],
                      level=client.depth-1,
                      parent_id=parent_id,
                      node=node,
                      )]
          else: # This is an important title, do our magic ;-)
              # Hack the title template SVG
              tfile = open('titletemplate.svg')
              tdata = tfile.read()
              tfile.close()
              tfile = tempfile.NamedTemporaryFile(dir='.', delete=False, suffix='.svg')
              tfname = tfile.name
              tfile.write(tdata.replace('TITLEGOESHERE', text))
              tfile.close()

              # Now tfname contains a SVG with the right title.
              # Make rst2pdf delete it later.
              client.to_unlink.append(tfname)

              e = FancyHeading(tfname, width=700, height=100,
                  client=client, snum=snum, parent_id=parent_id,
                  text=text, level=client.depth-1)

              node.elements = [e]

          if client.depth <= client.breaklevel:
              node.elements.insert(0, MyPageBreak(breakTo=client.breakside))
      return node.elements

The full extension is in SVN and you can try it this way:

[fancytitles]$ rst2pdf -e fancytitles -e inkscape demo.txt -b1

You need to enable the Inkscape extension so the SVG will look nice. And here's the output:

fancytitles2

You can override how any element is handled. That's being extensible :-)

2010-03-24 04:53

rst2pdf 0.14 released!

It's my pleasure to announce that I just uploaded rst2pdf 0.14 to the site at http://rst2pdf.googlecode.com.

Rst2pdf is a program and a library to convert restructured text directly into PDF using Reportlab.

It supports True Type and Type 1 font embedding, most raster and vector image formats, source code highlighting, arbitrary text frames in a page, cascading stylesheets, the full restructured text syntax and much, much more.

It also includes a sphinx extension so you can use it to generate PDFs from documents built with Sphinx.

In case of problems, please report them in the Issue tracker (http://code.google.com/p/rst2pdf/issues/list) or the mailing list (http://groups.google.com/group/rst2pdf-discuss)

This release fixes several bugs and adds some minor features compared to 0.13.2. Here are some of the changes:

  • Fixed Issue 197: Table borders were confusing.
  • Fixed Issue 297: styles from default.json leaked onto other syntax highlighting stylesheets.
  • Fixed Issue 295: keyword replacement in headers/footers didn't work if ###Page### and others was inside a table.
  • New feature: oddeven directive to display alternative content on odd/even pages (good for headers/footers!)
  • Switched all stylesheets to more readable RSON format.
  • Fixed Issue 294: Images were deformed when only height was specified.
  • Fixed Issue 293: Accept left/center/right as alignments in stylesheets.
  • Fixed Issue 292: separate style for line numbers in codeblocks
  • Fixed Issue 291: support class directive for codeblocks
  • Fixed Issue 104: total number of pages in header/footer works in all cases now.
  • Fixed Issue 168: linenos and linenothreshold options in Sphinx now work correctly.
  • Fixed regression in 0.12 (interaction between rst2pdf and sphinx math)
  • Documented extensions in the manual
  • Better styling of bullets/items (Issue 289)
  • Fixed Issue 290: don't fail on broken images
  • Better font finding in windows (patch by techtonik, Issue 282).
  • Fixed Issue 166: Implemented Sphinx's hlist (horizontal lists)
  • Fixed Issue 284: Implemented production lists for sphinx
  • Fixed Issue 165: Definition lists not properly indented inside admonitions or tables.
  • SVG Images work inline when using the inkscape extension.
  • Fixed Issue 268: TOCs shifted to the left on RL 2.4
  • Fixed Issue 281: sphinx test automation was broken
  • Fixed Issue 280: wrong page templates used in sphinx

Enjoy!

Contents © 2000-2019 Roberto Alsina