Create PDF with (resized) PNG images using Pycairo - scaling

I have links for PNG PNG images that I want to download, "convert to thumbnails" and save to PDF using Python and Cairo.

Now I have a working code, but I do not know how to control the size of the image on paper. Is there a way to resize the PyCairo surface to the dimensions I want (which happens less than the original)? I want the original pixels to be “squeezed” to a higher resolution (on paper).

In addition, I tried the Image.rescale() function from PIL, but it returns me the result of 20x20 pixels (from the original image 200x200 pixels, which is not an example of a banner in the code). I want the 200 × 200 image to be applied to a 20 × 20 mm square on paper (instead of a 200 × 200 mm square, as I get now)

My current code is:

 #!/usr/bin/python import cairo, urllib, StringIO, Image # could I do it without Image module? paper_width = 210 paper_height = 297 margin = 20 point_to_milimeter = 72/25.4 pdfname = "out.pdf" pdf = cairo.PDFSurface(pdfname , paper_width*point_to_milimeter, paper_height*point_to_milimeter) cr = cairo.Context(pdf) cr.scale(point_to_milimeter, point_to_milimeter) f=urllib.urlopen("http://cairographics.org/cairo-banner.png") i=StringIO.StringIO(f.read()) im=Image.open(i) # are these StringIO operations really necessary? imagebuffer = StringIO.StringIO() im.save(imagebuffer, format="PNG") imagebuffer.seek(0) imagesurface = cairo.ImageSurface.create_from_png(imagebuffer) ### EDIT: best answer from Jeremy, and an alternate answer from mine: best_answer = True # put false to use my own alternate answer if best_answer: cr.save() cr.scale(0.5, 0.5) cr.set_source_surface(imagesurface, margin, margin) cr.paint() cr.restore() else: cr.set_source_surface(imagesurface, margin, margin) pattern = cr.get_source() scalematrix = cairo.Matrix() # this can also be used to shear, rotate, etc. scalematrix.scale(2,2) # matrix numbers seem to be the opposite - the greater the number, the smaller the source scalematrix.translate(-margin,-margin) # this is necessary, don't ask me why - negative values!! pattern.set_matrix(scalematrix) cr.paint() pdf.show_page() 

Please note that a beautiful Cairo banner is not even suitable for the page ... The ideal result would be that I could control the width and height of this image in units of user space (in this case, millimeters) to create a nice header image, eg.

Thanks for reading and for any help or comment!

+2
python resize pdf cairo geometry-surface
source share
3 answers

Try scaling the context when drawing the image.

eg.

 cr.save() # push a new context onto the stack cr.scale(0.5, 0.5) # scale the context by (x, y) cr.set_source_surface(imagesurface, margin, margin) cr.paint() cr.restore() # pop the context 

See http://cairographics.org/documentation/pycairo/2/reference/context.html for details.

+2
source share

Jeremy Flores solved my problem very well by scaling the target surface before setting the image as a source. Despite the fact that someday you really need to change the size of the surface (or somehow transform it), so I will briefly describe the rationale used in my alternative answer (already included in the question), derived after a thorough reading of the documents :

  • Define your surface as a source of context - it implicitly creates cairo.Pattern !!
  • Use Context.get_source() to return the template;
  • Create cairo.Matrix ;
  • Apply this matrix (with all its transformations) to the template;
  • Paint!

The only problem, apparently, is that the transforms always work around the origin, so you need to scale and rotate, and they are followed by a complementary translation to the origin (bleargh).

0
source share

This does not answer the question, I just wanted to share the current heltonbiker code edited to run with Python 3.2:

 import cairo, urllib.request, io from PIL import Image paper_width = 210 paper_height = 297 margin = 20 point_to_millimeter = 72/25.4 pdfname = "out.pdf" pdf = cairo.PDFSurface( pdfname, paper_width*point_to_millimeter, paper_height*point_to_millimeter ) cr = cairo.Context(pdf) cr.scale(point_to_millimeter, point_to_millimeter) # load image f = urllib.request.urlopen("http://cairographics.org/cairo-banner.png") i = io.BytesIO(f.read()) im = Image.open(i) imagebuffer = io.BytesIO() im.save(imagebuffer, format="PNG") imagebuffer.seek(0) imagesurface = cairo.ImageSurface.create_from_png(imagebuffer) cr.save() cr.scale(0.5, 0.5) cr.set_source_surface(imagesurface, margin, margin) cr.paint() cr.restore() pdf.show_page() 
0
source share

All Articles