PerfectVector
Por Irene Kim9 min de leitura

SVG no Matplotlib: gráfico e logo como traçados

Exporte o gráfico Matplotlib como SVG, componha traçados separados de logo com svgutils e confira pixels incorporados, posicionamento e mudanças de fonte no arquivo entregue.

Nesta página

Para manter um logo vetorial dentro de uma figura Matplotlib, exporte o gráfico como SVG e componha-o com a geometria SVG existente do logo. Inserir um PNG com OffsetImage pode produzir um SVG contendo uma imagem raster ao lado de linhas vetoriais do gráfico. A extensão, sozinha, não informa quais partes são traçados.

Este guia separa essas tarefas: o Matplotlib desenha o gráfico a partir dos dados, e o svgutils posiciona um emblema decorativo ao lado dele. Se a arte original já for vetorial, use-a. Se apenas uma ilustração raster adequada tiver restado, recupere essa ilustração separadamente. Mantenha marcas numéricas, eixos e rótulos na origem do gráfico.

O fluxo resumido

Exporte o gráfico, componha a arte separada, inspecione o SVG resultante e reabra o arquivo exato que você entregará. Guarde o script do gráfico e o original da arte para que a próxima revisão dos dados possa gerar a figura novamente.

Escolha o que pertence à origem do gráfico

Um logo é arte; uma marca de eixo faz parte do gráfico. Tratar ambos como uma única imagem dificulta revisões futuras.

Conteúdo da figuraOrigem preferidaO que verificar
Linhas, marcadores, eixos e rótulos dos dadosCódigo do gráfico MatplotlibValores, rótulos e escolhas de visualização
Logo ou ilustração vetorial existenteSeu original SVGTraçados, limites e dependências
Fotografia ou painel de microscopia rasterImagem raster originalResolução no tamanho final
Motivo decorativo simples cuja origem vetorial foi perdidaUm candidato de recuperação inspecionado separadamenteFidelidade das formas e regiões indesejadas

Uma figura inteiramente vetorial não é necessária quando alguns painéis são fotografias. Preserve-os como raster. O objetivo é manter a origem certa para cada componente e verificar o resultado montado.

A referência de savefig do Matplotlib documenta a seleção explícita de um formato de saída. Sua API OffsetImage descreve um contêiner de imagem, em vez de um importador de traçados SVG. Para o diagnóstico do arquivo, veja como encontrar imagens raster dentro de um SVG.

O que os arquivos entregues contêm

Executamos o exemplo com Python 3.12, Matplotlib 3.10.7 e svgutils 0.3.4. O emblema é um desenho original de dois traçados. Os quatro valores do gráfico são dados ilustrativos, não um experimento nem um teste comparativo de produto.

A comparação usa o mesmo gráfico nos dois arquivos. À esquerda, um emblema PNG de 32 × 32 é inserido com OffsetImage. À direita, seus traçados SVG são compostos com o gráfico exportado. As regiões ampliadas vêm desses arquivos reais de entrega.

Arquivos SVG reais do Matplotlib com regiões do emblema ampliadas: um emblema PNG incorporado à esquerda e traçados SVG do emblema compostos à direita
O gráfico permanece vetorial nos dois arquivos. Neste exemplo, a versão raster contém um elemento image; a versão composta adiciona dois traçados de arte e não contém nenhum elemento image.

A contagem dos elementos SVG produziu estes resultados:

ArquivoElementos imageElementos pathElementos text
Apenas o gráfico0815
Gráfico com emblema PNG1815
Gráfico com traçados SVG do emblema01015

Essas contagens se aplicam a este pequeno exemplo. A quantidade de traçados não é uma nota de qualidade, e zero elementos image não prova que todos os recursos SVG sobreviverão em outro aplicativo. Isso comprova que o emblema neste arquivo composto é geometria, em vez de um PNG incorporado.

Exporte um gráfico com limites previsíveis

Instale as versões usadas aqui em um ambiente do projeto:

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

Salve isto como plot.py e execute-o em uma pasta de trabalho vazia:

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)

Os eixos ocupam apenas parte da figura, deixando espaço para o emblema à direita. Essa exportação tem um viewBox de 0 0 432 216, com dimensões externas de 432pt por 216pt.

Mantenha esses limites estáveis ao posicionar arte externa. Adicionar bbox_inches="tight" muda os limites salvos, como explica a documentação de savefig. Se precisar de um recorte justo, inspecione o viewBox exportado e atualize depois as coordenadas da composição.

A configuração svg.fonttype="none" mantém os rótulos do exemplo como texto. A documentação de fontes do Matplotlib descreve as opções de saída SVG em texto e traçados. Texto editável ainda precisa de uma fonte adequada no ambiente de destino. Se precisar de formas de letras fixas, escolha deliberadamente a saída em traçados e guarde a origem do gráfico para editar os rótulos. O guia de mudanças de fonte em SVG aborda essa escolha.

Componha a arte sem rasterizá-la

Salve este emblema original como badge.svg ao lado de 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>

Depois execute este script de composição:

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")

Isso segue a abordagem de carregar, transformar, anexar e salvar do tutorial de composição de figuras do svgutils. Usamos scale_x, o argumento compatível com svgutils 0.3.4; o tutorial mais antigo mostra outro argumento de escala.

A arte de 100 unidades do emblema passa a ter 64 unidades de largura. Seu canto superior esquerdo fica em (332, 76) dentro do sistema de coordenadas do gráfico. As dimensões externas explícitas e o viewBox tornam esse posicionamento inequívoco neste exemplo. Essas são coordenadas de página, não de dados: mover um ponto de dados no gráfico não moverá o emblema junto.

Para anotações vinculadas a um ponto de dados que muda, mantenha um mapeamento deliberado entre as coordenadas do gráfico e a página exportada, ou use uma ferramenta de composição que ofereça essa associação. Um posicionamento fixo na página é adequado para um logo no canto ou um painel ilustrativo separado.

O original SVG pode conter recursos além de traçados. Um SVG que já incorpora um PNG continua sendo conteúdo misto quando composto. Verifique também IDs referenciados, fontes e recursos vinculados externamente ao combinar arquivos mais complexos. Este exemplo evita essas dependências no emblema.

Inspecione o arquivo e teste uma revisão

Abra vector-logo.svg em um navegador ou no editor pretendido. Verifique a abertura central do emblema, os rótulos do gráfico e o espaço vazio entre eles. Inspecione o arquivo no tamanho em que realmente será usado e também ampliado.

Uma pequena verificação XML pode confirmar a estrutura sem depender da prévia:

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)))

Para o exemplo fornecido, isso imprime image 0, path 10 e text 15. A comparação raster imprime image 1, mantendo os oito traçados do gráfico. Nessa comparação, a referência da imagem contém dados PNG; é conteúdo raster autossuficiente, não um arquivo externo de logo ausente.

Agora mude o último valor y ilustrativo em plot.py, execute-o novamente e repita a composição. O gráfico deve mudar enquanto o emblema permanece em sua posição reservada na página. Esse teste de revisão identifica um fluxo que parece correto uma vez, mas depende de uma exportação antiga do gráfico.

Verifique a próxima etapa de entrega separadamente. Se a figura for inserida em um documento, o fluxo de SVG no LaTeX explica as opções de exportação e rótulos. Uma prévia correta no navegador, sozinha, não comprova conformidade com uma revista científica nem preservação no PDF final.

Recupere apenas a arte que precisa de recuperação

Quando a origem do gráfico existe, exporte a partir dela. A vetorização não restaura dados numéricos nem comandos de plotagem a partir de uma captura de tela.

O PerfectVector se encaixa no caso mais específico em que um logo decorativo ou uma ilustração simples existe apenas como imagem raster. Com seu fluxo de recuperação de arte científica, recorte essa arte, confira o candidato vetorial e inspecione aberturas, formas finas e regiões de fundo indesejadas. Deixe eixos, marcas de medição e rótulos fora do traçado. Componha o SVG de arte aprovado com o gráfico nativo e repita as verificações do arquivo entregue.

Use o original vetorial sempre que o tiver. Redesenhe uma marca simples manualmente quando o traçado perder um detalhe relevante. Mantenha painéis fotográficos como raster. A visão geral da vetorização de imagens explica o que o traçado pode recuperar antes de você escolher esse caminho.

Perguntas frequentes

Salvar uma figura Matplotlib como SVG torna um logo PNG vetorial? Não. No fluxo OffsetImage testado, o gráfico é exportado como geometria vetorial, enquanto o logo PNG permanece como um elemento image dentro do SVG.

Posso posicionar o logo SVG usando coordenadas dos dados? A composição mostrada aqui usa coordenadas da página exportada. Um logo vinculado a um ponto de dados precisa de um mapeamento explícito de coordenadas ou de uma ferramenta que mantenha essa associação.

Por que meus rótulos viraram traçados? A configuração de saída de fontes SVG do Matplotlib controla se os rótulos são exportados como texto ou traçados de glifos. Escolha essa configuração antes de exportar e guarde a origem do gráfico para mudanças posteriores no texto.

Fontes

  1. Matplotlib — savefig — Exportação SVG explícita e efeito de caixas delimitadoras ajustadas.
  2. Matplotlib — OffsetImage — Comportamento do contêiner de imagem usado na comparação raster.
  3. Matplotlib — Fontes no Matplotlib — Opções de saída SVG em texto e traçados de glifos.
  4. svgutils — Criação de figuras com qualidade de publicação — Exportação separada do gráfico, composição SVG, transformações e salvamento de uma figura montada.

Exporte o gráfico a partir de seu código e mantenha a arte separada. Se apenas um motivo decorativo raster tiver restado, prepare um candidato de arte SVG, inspecione suas aberturas e limites, componha-o com o gráfico e verifique pixels, posicionamento e mudanças de rótulos no arquivo exato de entrega.

Mais do blog

Recomece com um SVG mais limpo
e fácil de editar