2025, Nov 01 06:17

Как правильно масштабировать логотип PNG в ReportLab для PDF

Почему PNG‑логотип в ReportLab выходит полноразмерным и как это исправить: задайте width и height в drawImage для точного масштабирования на страницах PDF.

Добавлять небольшой логотип или шеврон на каждую страницу PDF — обычная практика при генерации отчётов. Трудности начинаются, когда исходный PNG имеет большой размер, а наивная попытка изменить масштаб прямо в вызове отрисовки не срабатывает: вместо уменьшенной версии на каждой странице оказывается полноразмерное изображение.

Пример, показывающий проблему

Ниже видно, как логотип добавляется на каждую страницу, но попытки ограничить его размер не работают. Картинка продолжает выводиться в исходных габаритах.

def paint_sheet(sheet, dossier, pagesize=A4):
    pg_no = sheet.getPageNumber()
    emblem = Image('/opt/rspro/home/e8409/projects/CRAMM logo.png')
    emblem._restrictSize(1 * inch, 2 * inch)
    sheet.drawImage(emblem, 0, 0)
    sheet.showPage()

from reportlab.platypus import PageTemplate

upright_tpl = PageTemplate(
  id='upright', 
  frames=upright_frame,
  onPage=paint_sheet, 
  pagesize=A4)

from reportlab.platypus import BaseDocTemplate

pdf_doc = BaseDocTemplate(
  'report.pdf',
  pageTemplates=[
    upright_tpl
  ]
)

Что происходит

Масштаб пытаются задать через ограничение размера, но отрисовка выполняется методом, который это ограничение не наследует. Вызов draw использует исходные размеры изображения, если явно не переданы width и height. В итоге логотип снова и снова появляется в полном размере на каждой странице.

Решение

Укажите целевые размеры прямо в вызове отрисовки. При необходимости сначала прочитайте изображение и посмотрите его габариты, затем передайте нужные значения при выводе — так масштабирование будет таким, как задумано.

from reportlab.lib.utils import ImageReader

def render_stamp(canvas_ctx, doc_ctx, pagesize=A4):
    logo_path = '/opt/rspro/home/e8409/projects/CRAMM logo.png'
    img_reader = ImageReader(logo_path)
    orig_w, orig_h = img_reader.getSize()

    target_w = desired_width
    target_h = desired_height

    canvas_ctx.drawImage(
        logo_path,
        0,
        0,
        width=target_w,
        height=target_h,
        preserveAspectRatio=True
    )

Такой подход оставляет работу с изображением в том же месте, где формируется PDF. Дополнительная предварительная обработка PNG не требуется. Из практики ещё одно наблюдение: если вы запускаете код в среде с исполняемыми ячейками, выполнение шагов не по порядку может скрыть эффект ваших правок.

Почему это важно

Стабильное и предсказуемое масштабирование критично при размещении брендинга или любых фиксированных графических элементов на многих страницах. Без явного задания размеров крупный исходник «забьёт» холст, нарушит компоновку и вытеснит контент. Управление габаритами в самом вызове отрисовки гарантирует одинаковый результат на каждой странице отчёта.

Итог

При добавлении повторяющихся изображений в PDF контролируйте масштаб параметрами width и height метода отрисовки и, при необходимости, сохраняйте пропорции. Если нужно узнать исходные размеры, прочитайте изображение, а затем выводите его на каждой странице с явными габаритами. Если результат по‑прежнему выглядит странно, убедитесь, что выполняются нужные участки кода и в правильной последовательности.

Статья основана на вопросе на StackOverflow от vashts85 и ответе от RCDevs Security.