Using Pangocairo
I’ve been experimenting with the new Pango/Cairo integration in the latest PyGTK and have figured out a few things (cairo is somewhat sparsely documented at the moment).
I decided to try my hand on PDFs to start with, since printing is my biggest issue at present. Presumably other backends work in a similar fashion.
The first challenge was figuring out the units for specifying the surface width and height. After some work, I discovered that this should be in Postscript points. A4 paper is 595 x 842 points; I found this and other well-know page sizes defined in the Scribus online documentation.
So, the workflow seems to be as follows.
- Create a surface, specifying size in Postscript points. Each backend seems to define its own surface type.
- Create a rendering context. Pango has its own context that wraps a standard cairo context.
- Create a pango layout for each paragraph, preferably using the Pango markup format to apply formatting within the paragraph. Pango exposes a bunch of other ways to format the text, have fun. Postscript points are 3/4 the size of font points (fontSize/0.75=Postscript points). That’s because fonts assume 72pt/inch, while PS assumes 96pt/inch.
- For a given paragraph, call layout.set_width() so that the paragraph wraps correctly. It’s not obvious, but width is expressed in thousandths of a point, so basically multiply the desired width by 1000.
- Line spacing is supposed to be controlled by layout.set_spacing() [also in thousandths of a point], but the version I have installed seems to ignore this - I’ll assume this a bug that will be fixed in short order.
- Position the paragraph using cairocontext.move_to() or cairocontext.rel_move_to(). Again, the coordinates shound be in Postscript points. Calling layout.get_pixel_size() will give you the width and height in Postscript points.
- Render the paragraph by calling cairocontext.show_layout().
- Finished laying out a page? Call cairocontext.show_page() to create the actual page. Any work you do after this point will be on a new page.
- Finally, call surface.finish() to write out the actual PDF file and release your handles.
If you want to lay out the individual lines yourself, you can get individual lines using layout.get_line(index) and use the resulting object to access the line measurements. You will probably want to do this when spanning page or column breaks. Use cariocontext.show_layout_line() to render individual lines.
August 26th, 2005 at 2:48 am
You might not have noticed but as well as RDF, I maintain the Cairo debian packages for the project itself - http://cairographics.org/packages/debian/ and in Debian itself. One day I’ll try to join these two interests up
February 24th, 2006 at 2:56 am
I agree with you that cairo & pango have almost nil documentation (apart from the api reference). I’m myself trying to get the head & tail of pango from past some days. Looking at other people’s code is the only possible was (os of now)
Your workflow description is really nice & helpfull. Thanks.
Oh, yes, there’s a typo in workflow-point-1
:)
Mayank
March 3rd, 2006 at 12:30 pm
Thanks, I’ve fixed the typo. I really wrote that workflow down for myself; glad that it has helped somebody.
I’ll put together a proper tutorial once there’s an official release of cairo with PDF support turned on.