cairopycairo

Can cairo load a PDF?


This sounds really simple, but I can't find a way to load a PDF into Cairo. I want to be able to do cairo.PDFSurface.create_from_pdf in the same way I do cairo.ImageSurface.create_from_png (yes I'm using the Python bindings but that's neither here nor there).

The documentation doesn't appear to give any hints.


Solution

  • No, cairo cannot read PDFs. However, poppler can load PDFs an paint them onto a cairo context - http://poppler.freedesktop.org/

    (Oh and create_from_png() is part of cairo's toy API. It works, but you are recommended to use some "real" API which can do more things than just loading PNGs)

    Example code to render a PDF get it into a pycairo ImageSurface:

    import io
    from pathlib import Path
    
    import cairo
    import poppler
    from PIL import Image
    
    filepath = Path('test.pdf')
    document = poppler.load_from_file(filepath)
    
    renderer = poppler.PageRenderer()
    
    # Render first page and convert to image buffer
    page = document.create_page(0)
    image = renderer.render_page(page, xres=72, yres=72)
    buf = io.BytesIO(image.data).getbuffer()
    
    if False: # for debugging
        pil_img = Image.frombytes('RGBA', (image.width, image.height),
                                  image.data, 'raw', str(image.format))
        pil_img.save('test.png')
    
    # Use page dimensions to create cairo surface
    rect = page.page_rect()
    width = int(rect.width)
    height = int(rect.height)
    #print(f'bytes used per pixel: {len(buf)/(width*height)}')
    
    surface = cairo.ImageSurface.create_for_data(buf, cairo.FORMAT_ARGB32, width, height)
    

    The resulting surface can be used as input to Context.set_source_surface() to paint onto an existing PDFSurface. If the resulting PDF shows a grainy image, increasing xres or yres by a factor of 72 can help, but this creates a need to scale down before painting.