Criando imagens promocionais customizadas com Python, Cairosvg e Django

Thiago Ferreira
WhatsGood Dev
Published in
3 min readNov 26, 2021

Python é uma linguagem extremamente versátil e possui bibliotecas que facilitam muito o nosso dia a dia — Uma delas é o cairosvg, que nos possibilita gerar imagens customizadas a partir de templates .svg.

Como o svg é um arquivo de texto que define as características de uma imagem, podemos deixar algumas variáveis dentro do arquivo e substituí-las usando python para criar imagens customizadas 😍

Caso de uso

Nesse exemplo, nosso objetivo será:

  • Criar uma imagem customizada para cada cupom/cliente, com um código promocional único.
  • Integrar essa criação da imagem com o framework Django
  • Escrever testes unitários para o processo.

Pra isso, usaremos as tecnologias: Python, Django e cairosvg.

Dependências e template svg

Primeiro de tudo, precisamos instalar as dependências:

$ pip install cairosvg

Depois disso precisamos de um template svg, onde podemos deixar algumas strings demarcando onde o texto será customizado, como por exemplo as chaves _WIDTH, _HEIGHT, _PROMO_CODE:

<svg xmlns="http://www.w3.org/2000/svg" pointer-events="none"  width="_WIDTH" height="_HEIGHT"> 
<rect width="_WIDTH" height="_HEIGHT"></rect>
<text text-anchor="middle" y="50%" x="50%" dy="0.35em" pointer-events="auto" fill="#ffffff" font-family="arial">
Your promotional code is: _PROMO_CODE
</text>
</svg>

Lendo o arquivo svg e criando um arquivo png a partir dos argumentos passados

Agora que temos um template, podemos definir uma função que irá ler o template .svg, substituir as chaves demarcadas e então gerar um arquivo png:

Esse método irá fazer, em resumo:

  • Ler o template svg e remover as quebras de linha (\n)
  • Substituir todas as chaves passadas por parâmetro no svg. É aqui que acontece a substituição de _PROMO_CODE para o código real, customizado.
  • Chamar a função svg2png do cairo para converter o svg gerado em png.

Usando a função genérica e salvando um arquivo png

Esse exemplo é um pouco mais simples e pode ser customizado para qualquer framework ou processo python, pois vamos apenas usar python puro para gerar uma imagem:

O que nos dará o seguinte resultado 🎉:

Salvando a imagem customizada num modelo do Django

Caso você use o framework Django, é absurdamente simples salvar essa imagem em um campo ImageField. Fica assim:

Pontos de atenção:

  • No exemplo anterior, usamos um arquivo real para salvar o png. Nesse exemplo do Django, não queremos criar um arquivo real no sistema de arquivos. Nossa intenção é apenas criar um arquivo em memória e depois passar o arquivo para o Django, onde será feito o upload para o destino correto.
  • Esse processo de poder usar um arquivo real ou um arquivo em memória é possível porque no python existem os file-like objects (objetos do tipo arquivo). Esses objetos implementam os protocolos de como um arquivo deve se comportar, e por isso podemos variar o destino real do arquivo gerado.

Testes unitários

“Code without tests is broken as designed.” — Jacob Kaplan-Moss

Agora só falta a peça final: precisamos garantir que nosso código faz o que ele diz que faz e vamos usar testes unitários para garantir isso:

Algumas considerações sobre os testes:

  • Estamos fazendo um mock do cairosvg e definindo que o mock é um wrapper para a função real. Isso fará com que o código real do cairosvg seja chamado, mas que possamos “espiar” os valores passados pra poder testar apropriadamente.
  • Com isso, no teste da função genérica create_png_from_svg_template, conseguimos espiar se o svg foi gerado corretamente com as chaves passadas.
  • O segundo teste verifica se a imagem foi gerada corretamente e salva no banco de dados. Também verifica a URL final da imagem criada.

Considerações finais

Meu objetivo com esse artigo foi mostrar como juntar algumas pecinhas para gerar imagens customizadas em python, com uma abordagem de código limpo e testes unitários para garantir a qualidade do projeto.

Espero que tenha ficado claro e que seja útil pra vocês. Como sempre, estou à disposição para dúvidas e sugestões 🚀

--

--