2025, Nov 30 03:02
Как разместить кликабельное изображение в левом верхнем углу PDF без смещений
Разбираем, почему оверлей на ReportLab+PyPDF2 смещается, и показываем надежный способ: кликабельный логотип в левом верхнем углу PDF через PyMuPDF и штамповку
Разместить кликабельное изображение точно в левом верхнем углу PDF кажется простой задачей, но на практике элемент часто смещается — особенно когда оверлей рисуется на холсте фиксированного размера, а затем накладывается на страницы с другой геометрией. Ниже — краткое руководство: где возникает сбой и как надежно добиться желаемого результата.
Кратко о задаче
Нужно добавить значок-изображение в левый верхний угол первой страницы PDF и сделать его ссылкой на URL. Простой подход — нарисовать оверлей в ReportLab и объединить его с исходником через PyPDF2.
Проблемный пример
Этот фрагмент показывает, как создается оверлей с фиксированным размером страницы Letter и затем накладывается на первую страницу. Несмотря на вычисление координат для верхнего левого угла с отступом, изображение оказывается ближе к центру.
from PyPDF2 import PdfReader, PdfWriter
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from reportlab.lib.units import inch
import io
# Входные данные
src_pdf = "original.pdf"
badge_path = "icon.jpeg"
out_path = "output.pdf"
# Создаем холст-оверлей
buf = io.BytesIO()
painter = canvas.Canvas(buf, pagesize=letter)
# Задуманное размещение в левом верхнем углу с отступом
pos_x, pos_y = 40, 792 - 40 - inch
painter.drawImage(badge_path, pos_x, pos_y, width=inch, height=inch)
painter.linkURL("https://t.me/newsmalayalampdf", (pos_x, pos_y, pos_x + inch, pos_y + inch), relative=0)
painter.save()
buf.seek(0)
# Объединяем оверлей с исходным PDF
mask_pdf = PdfReader(buf)
reader_pdf = PdfReader(src_pdf)
writer_pdf = PdfWriter()
page0 = reader_pdf.pages[0]
page0.merge_page(mask_pdf.pages[0])
writer_pdf.add_page(page0)
for pg in reader_pdf.pages[1:]:
writer_pdf.add_page(pg)
with open(out_path, "wb") as handle:
writer_pdf.write(handle)
Почему так происходит
Когда оверлей рисуется на холсте фиксированного размера Letter и затем накладывается, позиционирование становится хрупким, если целевая страница использует иную геометрию. Страница PDF может сообщать несколько «размеров», и жестко заданные значения ведут себя непоследовательно в разных файлах. Пересчет координаты Y относительно фиксированной константы не исправляет несоответствие. Кроме того, стоит помнить, что изображения в ReportLab зависят от Pillow, а сам PyPDF2 больше не поддерживается — проект переехал в pypdf.
Чистый и надежный подход: штамповка или PyMuPDF
Есть два практичных способа без проблем с оверлеем. Первый — одной командой оболочки «проштамповать» заранее подготовленный PDF с вашим изображением и ссылкой поверх каждой страницы. Второй — использовать PyMuPDF, программно разместив картинку и явно определив кликабельный прямоугольник на каждой странице.
Если нужен однострочный вариант из терминала, штамповка выглядит так:
cpdf -stamp-on logouri.pdf -pos-left "40 580" in.pdf -o output.pdf
Метод удобен как техника без дополнительных библиотек, но добавляет нюансы в воспроизведении. Похожий по простоте программный путь — использовать PyMuPDF: код получается коротким и наглядным.
Решение: размещение и ссылка в PyMuPDF
Скрипт ниже принимает входной PDF, рисует изображение с заданным смещением и добавляет к той же области URI‑ссылку. Результат можно применить ко всем страницам либо только к первой.
import pymupdf
import sys
import os
if len(sys.argv) < 2:
print("Usage: python logouri.py input.pdf")
sys.exit(1)
in_file = sys.argv[1]
out_file = os.path.splitext(in_file)[0] + "-withlogo.pdf"
img_file = "logo.jpeg"
jump_url = "https://t.me/newsmalayalampdf"
# Offsets and display size (points)
x_pad = 36
y_pad = 36
square = 72
doc = pymupdf.open(in_file)
for pg in doc:
box = pymupdf.Rect(x_pad, y_pad, x_pad + square, y_pad + square)
pg.insert_image(box, filename=img_file)
pg.insert_link({"kind": pymupdf.LINK_URI, "from": box, "uri": jump_url})
doc.save(out_file)
Если требуется лишь первая страница, замените цикл по страницам прямым обращением к нулевой странице.
doc = pymupdf.open(in_file)
pg = doc[0]
box = pymupdf.Rect(x_pad, y_pad, x_pad + square, y_pad + square)
pg.insert_image(box, filename=img_file)
pg.insert_link({"kind": pymupdf.LINK_URI, "from": box, "uri": jump_url})
doc.save(out_file)
Почему это важно
Программная компоновка PDF чувствительна к геометрии страницы. Использовать единственный холст формата Letter и накладывать его на произвольные документы — подход хрупкий, часто дающий смещенные оверлеи. Инструменты штамповки и API, работающие прямо в координатном пространстве каждой страницы, обеспечивают предсказуемое размещение и более простой код. Полезно также помнить сопутствующие детали — например, зависимость ReportLab от Pillow и переход проекта с PyPDF2 на pypdf.
Выводы
Если оверлей, нарисованный на фиксированном холсте, попадает не туда, откажитесь от жестко заданных размеров. Либо проштампуйте подготовленный ресурс одной командой, либо разместите изображение и ссылку напрямую на странице через PyMuPDF, используя прямоугольник. Когда важна точность, считывайте фактическую геометрию страницы и не делайте предположений о размере — так выход будет стабильным, а интеграция — поддерживаемой.