Press can now generate PDFs. For example:

from press import Press

with Press('press-demo.pdf', size=Press.LETTER, margin=1*Press.INCH) as p:
    # Top and bottom borders
    p.line(p.page_min_x, p.page_min_y, p.page_max_x, p.page_min_y)
    p.line(p.page_min_x, p.page_max_y, p.page_max_x, p.page_max_y)

    # Rotated colored rectangle
    p.stroke(hsl=(0, 0.5, 0.5))
    p.translate(p.page_min_x + 4*Press.INCH, p.page_max_y - 4*Press.INCH)
    p.rect(0, 0, 2*Press.INCH, 2*Press.INCH)

    # Lines of varying thickness
    p.translate(p.page_min_x + 1*Press.INCH, p.page_min_y + 1*Press.CM)
    for i in range(1, 20):
        p.pen(i / 2)
        p.line(0, 0, 30, 0)
        p.translate(0, 20)

That code generates the following PDF (linked):


I’m working on text/font support now, which is by far the most complicated thing about this project.

Since there isn’t a clean, cross-platform way to select a font via code, I’ve decided to use font maps (inspired by @font-face in CSS):

fontmap = {
    'paths': [ './fonts', '/Library/Fonts', ],
    'Minion Pro': [
        { 'weight': 300, 'italics': False, 'filename': 'MinionPro-Regular.otf', },
        { 'weight': 300, 'italics': True,  'filename': 'MinionPro-It.otf', },
        { 'weight': 600, 'italics': False, 'filename': 'MinionPro-Bold.otf', },
        { 'weight': 600, 'italics': True,  'filename': 'MinionPro-BoldIt.otf', },

with Press('output.pdf', size=Press.LETTER, fontmap=fontmap) as p:
    p.font('Minion Pro', size=24, weight=300, italics=True, dlig=True, smcp=True, tracking=50)
    p.text("This is a test.", 50, 50)

Font maps are admittedly extra work, but they do have some advantages as well: you can use fonts you haven’t installed, for example, and you can specify exactly which font files you want to use. And I can’t see any good way around the lack of a cross-platform font selection mechanism (meaning, a way to pass in ‘Minion Pro’ with specific weight and styles, and get a font filename in return).

Anyway, I’m in the middle of reading the PDF spec on CIDFonts and CMaps. It’s … complicated. It makes my head hurt. But it’ll be awesome when it’s done.