PerfectVector
Автор: Irene Kim8 мин чтения

SVG в Matplotlib: график и логотип в виде контуров

Экспортируйте график Matplotlib в SVG, добавьте отдельные контуры логотипа через svgutils и проверьте итоговый файл на встроенные пиксели, размещение и изменения шрифтов.

На этой странице

Чтобы логотип внутри рисунка Matplotlib оставался векторным, экспортируйте график в SVG и объедините его с существующей SVG-геометрией логотипа. Размещение PNG через OffsetImage может дать SVG, в котором растровое изображение соседствует с векторными линиями графика. Одно расширение не показывает, какие части представлены контурами.

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

Краткий порядок действий

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

Определите, что должно оставаться в исходнике графика

Логотип — это иллюстрация; деление оси — часть графика. Объединение обоих в одно изображение затрудняет последующие правки.

Содержимое рисункаПредпочтительный источникЧто проверить
Линии, маркеры, оси и подписи данныхКод построения графика MatplotlibЗначения, подписи и настройки построения
Существующий векторный логотип или иллюстрацияSVG-оригиналКонтуры, границы и зависимости
Растровая фотография или панель микроскопииИсходное растровое изображениеРазрешение при конечном размере
Простой декоративный мотив с утраченным векторным исходникомОтдельно проверенный вариант восстановленияТочность формы и нежелательные области

Полностью векторный рисунок не нужен, если часть панелей — фотографии. Оставьте их растровыми. Цель — сохранить подходящий источник для каждого компонента, а затем проверить собранный результат.

Справочник Matplotlib по savefig описывает явный выбор выходного формата. API OffsetImage описывает контейнер изображения, а не средство импорта контуров SVG. Проверка на уровне файла разобрана в статье как найти растровые изображения внутри SVG.

Что содержат передаваемые файлы

Мы выполнили пример с Python 3.12, Matplotlib 3.10.7 и svgutils 0.3.4. Значок — собственный рисунок из двух контуров. Четыре значения на графике — иллюстративные данные, а не эксперимент или тест продукта.

В обоих файлах сравнения используется один график. Слева PNG-значок размером 32 × 32 вставлен через OffsetImage. Справа его SVG-контуры объединены с экспортированным графиком. Увеличенные фрагменты взяты из этих реальных итоговых файлов.

Реальные SVG-файлы Matplotlib с увеличенными фрагментами значка: встроенный PNG-значок слева и добавленные SVG-контуры значка справа
График остаётся векторным в обоих файлах. В этом тестовом образце растровая версия содержит один элемент image; составная версия добавляет два контура иллюстрации и не содержит элементов image.

Подсчёт элементов SVG дал следующие результаты:

ФайлЭлементы imageЭлементы pathЭлементы text
Только график0815
График с PNG-значком1815
График с SVG-контурами значка01015

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

Экспортируйте график с предсказуемыми границами

Установите используемые здесь версии в окружение проекта:

python -m pip install matplotlib==3.10.7 svgutils==0.3.4

Сохраните это как plot.py и запустите в пустой рабочей папке:

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
 
plt.rcParams["svg.fonttype"] = "none"
 
fig = plt.figure(figsize=(6, 3), dpi=72)
ax = fig.add_axes([0.14, 0.22, 0.58, 0.65])
ax.plot([0, 1, 2, 3], [1, 2, 1.5, 3],
        marker="o", color="#173b40")
ax.set(xlabel="Example x", ylabel="Example y",
       title="Illustrative data only")
ax.spines[["top", "right"]].set_visible(False)
fig.savefig("plot.svg", format="svg")
plt.close(fig)

Оси занимают лишь часть рисунка, оставляя справа место для значка. Этот экспорт имеет viewBox 0 0 432 216 и внешние размеры 432pt на 216pt.

При размещении внешней иллюстрации сохраняйте эти границы. Добавление bbox_inches="tight" меняет границы сохраняемого файла, как объясняет документация savefig. Если нужна плотная обрезка, проверьте экспортированный viewBox и затем обновите координаты композиции.

Настройка svg.fonttype="none" сохраняет подписи примера как текст. Документация Matplotlib о шрифтах описывает выбор между текстом SVG и контурами. Редактируемому тексту всё равно нужен подходящий шрифт в принимающей среде. Если нужна неизменная форма букв, осознанно выберите вывод контурами и сохраните исходник графика для правки подписей. Руководство по изменению шрифтов SVG разбирает этот компромисс.

Добавьте иллюстрацию без растеризации

Сохраните этот собственный значок как badge.svg рядом с plot.svg:

<svg xmlns="http://www.w3.org/2000/svg"
     width="100" height="100" viewBox="0 0 100 100">
  <path fill="#173b40" fill-rule="evenodd"
        d="M30 5H70L95 30V70L70 95H30L5 70V30Z
           M34 17L17 34V66L34 83H66L83 66V34L66 17Z"/>
  <path fill="#28bfa3" d="M53 23L30 55H47L43 77L70 43H53Z"/>
</svg>

Затем запустите скрипт сборки композиции:

import svgutils.transform as sg
 
page = sg.SVGFigure("432pt", "216pt")
page.root.set("viewBox", "0 0 432 216")
 
chart = sg.fromfile("plot.svg").getroot()
badge = sg.fromfile("badge.svg").getroot()
badge.moveto(332, 76, scale_x=0.64)
 
page.append([chart, badge])
page.save("vector-logo.svg")

Он следует подходу «загрузить, преобразовать, добавить, сохранить» из урока svgutils по композиции рисунков. Мы использовали scale_x, аргумент, поддерживаемый svgutils 0.3.4; более старый урок показывает другой аргумент масштабирования.

Иллюстрация значка шириной 100 единиц становится шириной 64 единицы. Её левый верхний угол находится в (332, 76) в системе координат графика. Явные внешние размеры и viewBox делают размещение однозначным для этого образца. Это координаты страницы, а не данных: перемещение точки данных на графике не переместит вместе с ней значок.

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

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

Проверьте файл, затем протестируйте изменение

Откройте vector-logo.svg в браузере или нужном редакторе. Проверьте центральное отверстие значка, подписи графика и свободное место между ними. Просмотрите файл как в реальном размере использования, так и с увеличением.

Небольшая проверка XML подтвердит структуру независимо от предварительного просмотра:

import xml.etree.ElementTree as ET
 
root = ET.parse("vector-logo.svg").getroot()
ns = "{http://www.w3.org/2000/svg}"
for tag in ("image", "path", "text"):
    print(tag, len(root.findall(".//" + ns + tag)))

Для предоставленного образца она выводит image 0, path 10 и text 15. Растровый вариант сравнения выводит image 1, сохраняя восемь контуров графика. В нём ссылка на изображение содержит данные PNG; это автономное растровое содержимое, а не отсутствующий внешний файл логотипа.

Теперь измените последнее иллюстративное значение y в plot.py, запустите его заново и повторите сборку композиции. График должен измениться, а значок — остаться на отведённом месте страницы. Такая проверка выявляет процесс, который один раз выглядит правильно, но зависит от старого экспорта графика.

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

Восстанавливайте только ту иллюстрацию, которой это нужно

Если исходник графика существует, экспортируйте из него. Векторизация не восстановит числовые данные или команды построения графика из скриншота.

PerfectVector подходит для более узкого случая, когда простой декоративный логотип или иллюстрация сохранились лишь в растре. В процессе восстановления научной графики обрежьте изображение до этой иллюстрации, просмотрите векторный вариант и проверьте отверстия, тонкие формы и нежелательные участки фона. Не включайте в трассировку оси, измерительные отметки и подписи. Объедините принятый SVG иллюстрации с исходным графиком и повторите проверки итогового файла.

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

Частые вопросы

Сохранение рисунка Matplotlib в SVG делает PNG-логотип векторным? Нет. В проверенном процессе с OffsetImage график экспортируется как векторная геометрия, а PNG-логотип остаётся элементом image внутри SVG.

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

Почему мои подписи превратились в контуры? Настройка вывода шрифтов SVG в Matplotlib определяет, экспортируются ли подписи текстом или контурами глифов. Выберите эту настройку перед экспортом и сохраните исходник графика для последующей правки формулировок.

Источники

  1. Matplotlib — savefig — Явный экспорт SVG и влияние плотной ограничивающей рамки.
  2. Matplotlib — OffsetImage — Поведение контейнера изображения, использованного в растровом сравнении.
  3. Matplotlib — Fonts in Matplotlib — Выбор вывода текста SVG или контуров глифов.
  4. svgutils — Creating publication-quality figures — Отдельный экспорт графика, сборка SVG-композиции, преобразования и сохранение собранного рисунка.

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

Ещё из блога

Начните с более чистого SVG,
который проще редактировать