Создать PDF с (измененными) размерами изображений PNG, используя Pycairo - масштабирование поверхности. - PullRequest
2 голосов
/ 18 августа 2011

У меня есть ссылки на изображения в формате PNG, которые я хочу загрузить, «преобразовать в эскизы» и сохранить в PDF с помощью Python и Cairo.

Теперь у меня есть рабочий код, но я не знаю, какконтролировать размер изображения на бумаге.Есть ли способ изменить размер PyCairo Surface до размеров, которые я хочу (который оказывается меньше, чем оригинал)?Я хочу, чтобы исходные пиксели были «сжаты» до более высокого разрешения (на бумаге).

Кроме того, я пробовал функцию Image.rescale() из PIL, но она возвращает мне выходной сигнал 20x20 пикселей (из 200x200исходное пиксельное изображение, которое не является примером баннера в коде).То, что я хочу, - это изображение размером 200x200 пикселей, нанесенное внутри квадрата 20x20 мм на бумаге (вместо того, чтобы получать квадрат 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 ]

2 голосов
/ 18 августа 2011

Попробуйте масштабировать контекст при рисовании изображения.

* 1003 Е.Г. *

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.

0 голосов
/ 29 мая 2013

Это не ответ на вопрос, я просто хотел поделиться текущим кодом хелтонбайкера, отредактированного для работы с 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 голосов
/ 18 августа 2011

Джереми Флорес очень хорошо решил мою проблему, масштабировав поверхность цели перед тем, как установить поверхность изображения в качестве источника.Хотя, возможно, однажды вам действительно потребуется изменить размер поверхности (или каким-либо образом ее преобразовать), поэтому я кратко опишу обоснование, использованное в моем альтернативном ответе (уже включенном в вопрос), выведенный после тщательного прочтения документации:

  1. Установите вашу поверхность в качестве источника контекста - она ​​неявно создает cairo.Pattern !!
  2. Используйте Context.get_source(), чтобы получить образец назад;
  3. Создать cairo.Matrix;
  4. Применить эту матрицу (со всеми ее преобразованиями) к шаблону;
  5. Paint!

Единственной проблемой, по-видимому, являются преобразования, работающие всегда вокругначало координат, поэтому перед началом масштабирования и поворота должны следовать дополнительные переводы в начало координат (bleargh).

...