2025, Dec 07 21:01

Обрезка SVG из PDF в PyMuPDF по границам рисунка

Показываем, как обрезать экспорт SVG из PDF в PyMuPDF по границам векторных элементов: объединить bbox рисунков, задать cropbox и сохранить безрамочный SVG.

Извлекая линейные схемы из PDF и конвертируя их в SVG, часто получаешь слишком большой холст с лишними полями. Если цель — компактный, безрамочный SVG, сфокусированный на самом векторном содержимом, экспорт всей страницы оказывается избыточным и неудобным.

Постановка задачи

У вас уже есть рабочий экспорт в SVG с помощью PyMuPDF (fitz), но он захватывает всю страницу и сохраняет поля по краям в исходном масштабе. Задача — обрезать по границам диаграммы и сохранить в SVG только эту область.

Минимальный пример, воспроизводящий проблему

import fitz

pdf_obj = fitz.open(pdf_path)
first_page = pdf_obj[0]
svg_data = first_page.get_svg_image(matrix=fitz.Matrix(1, 1))

Так получается SVG на всю страницу, включая лишние поля по краям.

Почему SVG выходит с лишним полем

Экспорт опирается на текущий холст страницы. Если не менять видимую область, SVG покрывает всё внутри исходного прямоугольника страницы, включая пустые поля и рамочную графику. Чтобы убрать это лишнее пространство, перед экспортом область страницы нужно сузить до объединения всех векторных рисунков на странице.

Решение: обрезать по объединённым границам векторных элементов и экспортировать

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

import fitz

def make_svg_trimmed(src_pdf, out_svg, pg_idx=0):
    doc_obj = fitz.open(src_pdf)
    pg = doc_obj[pg_idx]

    draw_items = pg.get_drawings()

    rects = [itm["rect"] for itm in draw_items if itm["rect"].is_valid]
    if not rects:
        print("No vector drawings found")
        return

    union_box = rects[0]
    for bx in rects[1:]:
        union_box |= bx

    union_box = union_box + (-2, -2, 2, 2)

    pg.set_cropbox(union_box)

    svg_txt = pg.get_svg_image(matrix=fitz.Matrix(1, 1))
    with open(out_svg, "w", encoding="utf-8") as out_f:
        out_f.write(svg_txt)

    print(f"SVG saved to {out_svg}")

Так страница обрезается по фактическому векторному содержимому, а эффект рамки исчезает: холст ограничивается объединёнными границами рисунков с небольшим запасом.

Зачем это нужно

С векторными схемами аккуратно и плотно обрезанные SVG проще использовать дальше, они не отвлекают полями и соответствуют нужной видимой области. Экспорт фокусируется на самой диаграмме, а не на полном макете исходного PDF.

Итог

Если при экспорте SVG берётся вся PDF-страница, соберите векторные рисунки страницы, вычислите их общий ограничивающий прямоугольник, задайте область обрезки по нему и отрендерьте SVG. Так холст сужается до диаграммы, и исчезает масштаб полей, который иначе засоряет результат.