使用Pycairo使用(调整大小的)PNG图像创建PDF - 重新缩放Surface问题

时间:2011-08-17 21:19:14

标签: python pdf resize cairo geometry-surface

我有要下载的som PNG图像链接,“转换为缩略图”并使用Python和Cairo保存为PDF。

现在,我有一个正常工作的代码,但我不知道如何在纸上控制图像大小。有没有办法将PyCairo Surface调整到我想要的尺寸(恰好比原件小)?我希望将原始像素“缩小”到更高的分辨率(在纸上)。

另外,我尝试了PIL中的Image.rescale()功能,但是它给了我一个20x20像素的输出(在一个200x200像素的原始图像中,这不是代码中的横幅示例)。我想要的是在纸上20x20毫米的正方形内绘制的200x200像素图像(而不是现在我得到的200x200毫米正方形)

我目前的代码是:

#!/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()

请注意,美丽的开罗横幅甚至不适合页面... 理想的结果是,我可以用户空间单位(在这种情况下为毫米)控制此图像的宽度和高度,以创建一个漂亮的标题图像,例如。

感谢阅读和任何帮助或评论!

3 个答案:

答案 0 :(得分:2)

在绘制图像时尝试缩放上下文。

E.g。

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

有关详细信息,请参阅:http://cairographics.org/documentation/pycairo/2/reference/context.html

答案 1 :(得分:0)

杰里米·弗洛雷斯在将图像界面设置为源之前,通过缩放目标曲面很好地解决了我的问题。尽管如此,也许有一天你实际上需要调整Surface的大小(或以任何方式对其进行转换),因此我将简要描述我的备用答案(已包含在问题中)中使用的基本原理,在完整阅读文档后推断:< / p>

  1. 将表面设置为上下文的来源 - 它隐式创建cairo.Pattern !!
  2. 使用Context.get_source()恢复模式;
  3. 创建cairo.Matrix;
  4. 将此矩阵(及其所有变换)应用于模式;
  5. 油漆!
  6. 唯一的问题似乎是始终围绕原点进行的变换,因此必须先进行缩放和旋转,然后对原点进行互补翻译(bleargh)。

答案 2 :(得分:0)

这不是回答这个问题,我只想分享heltonbiker编辑的当前代码以运行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()