Valentine scripting | osp blog

osp blog

Valentine scripting


Poster and flyer designed and produced in OpenOffice

This week and next, colleague and friend An Mertens (a.k.a. Ana Foor) works out of the Elsene local library Sans Souci. She'll be listening to your account of meeting a loved one for the first time, those habits that keep your relationship alive or which imaginary place you would like to visit with your best friend. Just like an oldfashioned Ecrivain Public, Ana Foor will transform these conversations into unique Valentine letters and -stories published on the fly. In the waiting room, a selection of (Dutch language) romantic literature is available, plus a choice of styles and formats presented in a catalog developed/designed by OSP's Femke, Ludivine, Ivan, Nicolas and Pierre M.

For practical and conceptual reasons, we wanted to produce the customised stories and letters with the help of the well-known word processing tool OpenOffice, and were curious to see what could happen if we'd use that same tool for designing and printing the catalog, poster and invitation too. Most of all we couldn't resist to play with odfpy, a Python library that can generate .odt documents from scratch. It got us into various strange and exotic problems, just the way we like it :-)

Templates


This form lists all possible styles presented in the catalog

We designed a set of 6 basic lay-outs mixed with 9 different decorative 'spices' and this for three different types of content (story, poem or letter). On top of that, there are three types of media to choose from (web, A4 sheet and booklet). To help identify each of the styles, we came up with a flexible system of re-combinatorial template names that also somehow explains how the project works:

  • Sexy Maan
  • Lavendel Panter
  • Lungo Ijs
  • Espresso Stormen
  • Chocolade Valleien
  • Koraal Verlangen
  • Kristallen Maan
  • Roze Panter
  • Champagne Ijs

odfpy
"Unlike other more convenient APIs, this one is essentially an abstraction layer just above the XML format. The main focus has been to prevent the programmer from creating invalid documents."

Nicolas had pointed us to the odfpy library a while ago, and this lovely catalog seemed the ultimate opportunity to experiment with it. odfpy adds a Python-scripting interface to OpenOffice; with the help of this library you can dynamically generate files in the Open Document Format (.odt, .ods). odfpy uses OpenOffice in the background, but without its graphical user interface.


To see what elements an .odt-file consists of, 'extract' it like a .zip or .tar file

<office:body><office:text><text:sequence-decls><text:sequence-decl text:display-outline-level="0" text:name="Illustration"/><text:sequence-decl text:display-outline-level="0" text:name="Table"/><text:sequence-decl text:display-outline-level="0" text:name="Text"/><text:sequence-decl text:display-outline-level="0" text:name="Drawing"/></text:sequence-decls><text:p text:style-name="Standard">Hello World</text:p></office:text></office:body>

Fragment of content.xml

The Open Document format is written in a rather clean kind of xhtml, which means it is legible compared to most other Word Processing or Desktop Publishing files we looked at. Still that did not mean it was evident how odfpy generates a frame, or calculates the size of a font. The odfpy documentation is rudimentary; it provides extensive information on the hierarchy of frames, elements and styles but for example not a list of what style-options are available. While it is nice to know at what point a frame needs to be generated, without the syntax for a single or triple border, whether it is red or shaded you can only guess. We designed sample files and than browsed their 'source', reverse engineering document settings and -styles with the help of the Open Document file specifications. It obviously took us a ridiculous amount of time to generate 162 different templates in this way.

# -*- coding: utf-8 -*- # drawing hearts in the snow! from odf.opendocument import OpenDocumentText from odf.text import P, Span from odf.style import Style, TextProperties, ParagraphProperties, BackgroundImage, GraphicProperties from odf.draw import Frame, TextBox from texts import * from liblove import *
From the header of character_spices.py: A heartwarming address

Even when the odfpy-scripts Ivan was writing and rewriting, started to read more and more like love letters themselves ... even with our general distrust of efficient methods ... would we have been better off doing all this by hand? With the printer deadline approaching rapidly, we had to settle for a very basic set of styles but managed to use nearly every OSP font currently available. This publication will be both a stalenboek and fontcatalog.

herbal_scripts.zip
After having installed odfpy, run $ python name_of_script.py in your commandline and generate many .odt files at once

OSP_fonts_ok.zip
Sneak preview: Drop the contents of this folder into [yourhomefolder]/.fonts and restart OpenOffice

Output to different media
Our next concern was to generate a pdf that could be handled by the in-house city council printer of Elsene. We had 162 A4 .odt files and three A5 .pdf files (produced in Scribus) that needed to be combined into one pdf. We wrote a bash-script that takes all .odt files inside a folder, transforms them into pdf, resizes them from A4 to A5 and than gathers all files into a multi-page pdf.

transform.sh
Using some of our favorite tools: unoconv, ps2pdf, psresize and pdftk transform a series of A4 .odt files into a multi-page A5 pdf.

As we were working on generating the files until the last minute, we failed to notice that we somehow introduced full color into a pdf that would need to be printed in black-and-white. Also, the printer needed PDF1.3 and we were generating default PDF1.4. In a quick fix, we added ghostscript to the already long list of pdf-tools used in the bash-script; probably making many other steps redundant (ps2pdf has ghostscript integrated for example) but no time for clean-up or rewrite.

transform_gray.sh

Still not all trouble was over. Our printer reported that he was unable to deal with part of the embedded fonts (Unable to read .ttf? Using an old-style rip?) but probably inspired by our friendly persistance, he figured out a way to transform our files into postscript once more, regenerate the font-images from there and finally ... the file passed.

Textflow and style names


OpenOffice: Where did the characterstyles go?

The custom publications An will produce on the fly are based on the same .odt templates that we printed in the catalog. In a first dry-run, we realised that they needed to be corrected since the documents we had generated for the printed catalog, basically consisted of single frames. Also, it took a bit of work to generate documents that made the applied styles correctly available in the OpenOffice style menu. We managed by making each of the documents 16 pages (maximum length of the booklet) but gave up on making character styles behave correctly.

podofoimpose
PoDoFo is a library to work with the PDF file format and includes also a few tools. The name comes from the first two letters of PDF (Portable Document Format)

After Ludivine converted three of our favourite templates into css stylesheets for web-output (we love @fontface!), our last challenge was to deal with was imposition. Since early OSP-days we've been experimenting with several ways to generate A5 booklets, but we never managed 2 x 8 pages on an A4 (A7 booklet). Pierre Marchand pointed us once again to PoDoFO, a pdf-processing tool to which he once added podofoimpose, adding the useful function to run custom imposition 'plans'. With only a bit of trouble and help, we installed PoDoFo on our machines (We're using Ubuntu 8.10 and 9.10):

Start with downloading ('check out') the latest files with svn (install svn with $ sudo apt-get install subversion)

$ svn co https://podofo.svn.sourceforge.net/svnroot/podofo/podofo/trunk podofo

Download and install the libraries PoDofo depends on:

$ sudo apt-get install build-essential g++ cmake libz-dev libtiff-dev libjpeg-dev libfreetype6-dev libfontconfig-dev
$ sudo apt-get install liblua5.1-0-dev

Than, copy the files into a folder into your home directory (you need to create the folder first):

$ mkdir /home/[your_user_name]/src/podofo
$ cp /home/[podofo_download] /home/[your_user_name]/src/podofo

From inside the build folder, you now need to compile PoDoFo from source with the help of the cmake-compiler (install cmake with $ sudo apt-get install cmake)

$ cd /home/[your_user_name]/src/podofo/build
$ mkdir $ cd $ cmake ../podofo $ make $ sudo su $ make install

podofoimpose can read imposition instructions from files written in lua, a scripting language that is used with ConTeXt for example. With the help of a proper paper dummy, Ludivine managed to write the correct 'plan' to output A7 booklets :-D

InOctavo.plan

PushRecord((pgroup*count)+9, (count*2)+1, rot2, 0, tw) PushRecord((pgroup*count)+16, (count*2)+1, rot1, 2*th, 0) PushRecord((pgroup*count)+13, (count*2)+1, rot1, 2*th, 3*tw) PushRecord((pgroup*count)+12, (count*2)+1, rot2, 0, 4*tw) PushRecord((pgroup*count)+1, (count*2)+1, rot1, 2*th, tw) PushRecord((pgroup*count)+8, (count*2)+1, rot2, 0, 2*tw) PushRecord((pgroup*count)+4, (count*2)+1, rot1, 2*th, 2*tw) PushRecord((pgroup*count)+5, (count*2)+1, rot2, 0, 3*tw)
Turning and twisting: imposition from scratch

You can use the plan like this:

$ podofoimpose output.pdf input.pdf InOctavo.plan lua

A loose hand
When the printed catalog is finally delivered, the result is not what we expected. Pages are out of order and it seems they only printed the left bottom crop/bleed mark and than everything has been cut out of the format at the top and right side. When we try to complain, we realise that dealing with an in-house city council printer has its complications. To explain our exotic project to them is not easy. But we do not flinch and keep smiling, even if we did not hand in any certified pdf's ... and even omitted page numbers! In the end, the responsible politician grants us a re-print.

So ... after all: A Happy Valentine!