PerfectVector
Por Irene Kim8 min de leitura

SVG para PNG com Sharp: tamanho e nitidez

Renderize SVGs em PNGs transparentes no tamanho final em pixels com Sharp. Veja um teste reproduzível 1x e 2x, quando density importa e por que ampliar um PNG falha.

Nesta página

Em um fluxo atual do Sharp, comece pelo SVG em cada exportação e chame resize() com a largura em pixels de que você realmente precisa. Se precisar de um recurso 2x, solicite o dobro de pixels de saída. Não crie um PNG pequeno primeiro para depois ampliá-lo. Também não copie uma correção antiga de density para todo fluxo: nosso teste com Sharp 0.34.4 renderizou os mesmos pixels com e sem uma densidade de entrada maior.

A resposta curta
  1. Guarde o SVG original como arquivo mestre.
  2. Renderize cada tamanho PNG diretamente desse SVG com Sharp.
  3. Inspecione as dimensões de saída e o canal alfa e veja o arquivo no tamanho pretendido de exibição.
  4. Mude o SVG ou investigue o renderizador se o resultado ainda diferir da origem. Uma configuração de densidade não cria detalhes dentro de um bitmap incorporado.

Renderize um PNG 1x e 2x a partir do SVG

O construtor do Sharp aceita um arquivo SVG ou buffer, e sua API de redimensionamento define as dimensões de saída. Para um ícone quadrado de 64 unidades que aparecerá com 256 pixels CSS, exporte um PNG de 256 pixels para uso 1x e um de 512 pixels para uso 2x:

import sharp from "sharp";
 
const source = "icon.svg";
 
await Promise.all([
  sharp(source).resize({ width: 256 }).png().toFile("icon-1x.png"),
  sharp(source).resize({ width: 512 }).png().toFile("icon-2x.png"),
]);

Omitir a altura preserva a proporção da origem no comportamento usual de redimensionamento do Sharp. Escolha larguras reais de saída para o destino; “2x” se refere às dimensões em pixels em relação ao tamanho pretendido de exibição CSS, não a um atributo SVG nem a uma promessa sobre todas as telas. Se quiser ver o ícone com 256 pixels CSS, compare os PNGs de 256 e 512 pixels nesse tamanho exibido e decida se o arquivo maior justifica seu peso.

Nosso ícone original de teste declarava width="64" height="64" viewBox="0 0 64 64". Renderizamos em 64, 512 e 1,024 pixels com o Sharp 0.34.4 fixado no repositório. As saídas de 512 e 1,024 pixels foram geradas do SVG, tinham cantos transparentes e centros pintados opacos e informaram as dimensões solicitadas. São observações deste exemplo e desta versão, não um teste comparativo de toda arte SVG.

Teste original de renderização Sharp 0.34.4
Teste original de renderização Sharp: a renderização direta do SVG em 512 pixels e a renderização SVG com densidade ajustada parecem idênticas, enquanto ampliar um PNG intermediário de 64 pixels fica borrado
O mesmo ícone SVG de 64 × 64 foi renderizado diretamente com 512 pixels, renderizado com densidade de entrada 576 e redimensionado para 512 pixels, ou primeiro rasterizado com 64 pixels e ampliado. Apenas o último caminho amplia uma grade de pixels existente.

A figura torna concreto o modo de falha: quando a primeira saída é um PNG de 64 pixels, um redimensionamento posterior interpola esses pixels. Não há mais traçados para redesenhar. Nossos resultados de 512 pixels direto e com densidade ajustada foram idênticos nos canais RGBA brutos neste teste. O SVG de origem, o script de renderização e os metadados do resultado estão guardados no pacote de pesquisa do artigo.

Você precisa de density no Sharp?

A opção density do construtor Sharp controla o DPI usado ao ler entradas SVG ou PDF; seu padrão documentado é 72. Ela é diferente de solicitar uma largura final em resize(). Você pode encontrar exemplos antigos do Sharp que aumentam a densidade de entrada para evitar uma ampliação borrada. Esses exemplos descrevem um sintoma histórico real: o problema Sharp #1421 relata um SVG de 32 pixels ficando pixelado ao ser ampliado para 512 pixels em 2018. Eles não provam que a solução ainda seja necessária em uma instalação atual.

Testamos exatamente este par no Sharp 0.34.4:

const direct = await sharp("icon.svg")
  .resize({ width: 512 })
  .png()
  .toBuffer();
 
const densityFirst = await sharp("icon.svg", { density: 576 })
  .resize({ width: 512 })
  .png()
  .toBuffer();

Para nosso ícone vetorial de 64 pixels, os dois fluxos produziram os mesmos pixels RGBA de 512 × 512. Defina densidade de entrada apenas quando um arquivo específico e a versão Sharp instalada demonstrarem necessidade, e repita a comparação após atualizações. Não trate um número DPI maior como um controle universal de nitidez. A primeira pergunta útil é se você ainda tem geometria vetorial no momento em que o Sharp renderiza o PNG final.

Se você também trabalha com PDFs, teste essa entrada separadamente: este experimento SVG não estabelece o comportamento de PDF. Da mesma forma, withDensity() na documentação mais recente do Sharp descreve metadados DPI de saída; essa é uma operação diferente da densidade de renderização da entrada SVG no construtor.

Confira dimensões, transparência e conteúdo de origem

Use a API de metadados do Sharp na saída em vez de adivinhar pelo zoom da prévia:

const oneX = await sharp("icon-1x.png").metadata();
const twoX = await sharp("icon-2x.png").metadata();
 
console.log({
  oneX: [oneX.width, oneX.height, oneX.hasAlpha],
  twoX: [twoX.width, twoX.height, twoX.hasAlpha],
});

Para o exemplo quadrado aqui, as dimensões esperadas são 256 × 256 e 512 × 512. hasAlpha indica um canal alfa, mas não prova que um canto específico seja transparente; inspecione o arquivo ou amostre pixels se a transparência importar na entrega. O alfa do canto do exemplo foi zero e o do centro foi 255 em todas as saídas testadas. Um retângulo branco dentro do SVG seria pintado em todos os PNGs, independentemente do formato PNG.

Se uma nova renderização grande ainda estiver pixelada, inspecione o conteúdo SVG. O padrão SVG permite um elemento <image> que incorpora ou vincula uma imagem raster. Um arquivo pode, portanto, ter extensão .svg enquanto a arte interna ainda é um PNG de baixa resolução. Nosso guia para encontrar imagens raster dentro de SVGs mostra como conferir os objetos e o XML. Aumentar a largura de saída do Sharp não transforma esses pixels incorporados em traçados.

Confira também o destino. Um arquivo de 512 pixels exibido com 1,024 pode parecer borrado. Um visualizador com zoom muito acima de 100% revela a grade PNG mesmo quando a exportação está correta. Compare no tamanho e fundo reais de renderização e decida se deve entregar um PNG maior ou o próprio SVG. Para entrega web, SVG embutido e SVG em um elemento de imagem oferecem escolhas diferentes de integração; nenhum exige PNG quando o destino suporta SVG.

Onde o PerfectVector se encaixa

O Sharp rasteriza um SVG; ele não recupera traçados editáveis de um PNG. Se sua arte mestre é apenas um ícone ou logo raster, crie e inspecione um SVG antes deste fluxo de exportação. O guia de ícone PNG para kit de interface SVG aborda geometria consistente e verificações de entrega em um conjunto. O fluxo de imagem para vetor do PerfectVector pode criar um candidato SVG a partir de arte apenas raster. Abra esse resultado, confira sua silhueta, pequenos detalhes e se contém traçados reais e renderize os PNGs 1x e 2x do SVG aprovado. Se você já tem um SVG original limpo, guarde-o e use Sharp diretamente.

Perguntas frequentes

Por que minha saída SVG para PNG do Sharp parece borrada? Confira se o fluxo primeiro criou um PNG pequeno e depois o ampliou. Em seguida, inspecione o SVG em busca de conteúdo raster incorporado e compare o PNG entregue no tamanho real de exibição. Em uma versão atual do Sharp, teste a renderização direta do SVG no tamanho final antes de adicionar uma solução de densidade de entrada.

density: 576 é necessário para ampliar oito vezes? Não foi necessário em nosso exemplo SVG de 64 para 512 pixels no Sharp 0.34.4: o redimensionamento direto e o redimensionamento com densidade ajustada produziram pixels RGBA idênticos. Seu SVG e renderizador instalado podem se comportar diferente, portanto teste essas entradas exatas em vez de adotar o número como regra.

Como crio um PNG 2x transparente? Renderize a partir do SVG no dobro das dimensões CSS pretendidas e exporte como PNG. Confirme largura e altura resultantes e inspecione uma área não pintada para verificar transparência. Uma área de desenho transparente não remove uma forma de fundo já presente no SVG.

Fontes

  1. Sharp — Construtor — Suporte a entrada SVG e opção density de entrada.
  2. Sharp — Redimensionamento de imagens — Comportamento de tamanho de saída usado nos exemplos.
  3. Sharp — Metadados de entrada — Verificações de dimensões e metadados alfa.
  4. Sharp GitHub — Problema de pixelização SVG para PNG #1421 — Sintoma histórico relatado, explicitamente separado do teste atual com versão fixada.
  5. Sharp — Opções de saída — Diferencia metadados mais recentes de densidade de saída da densidade de renderização de entrada.
  6. W3C — Conteúdo incorporado SVG 2 — Por que um SVG pode conter pixels raster.

Se o original existe apenas em raster, crie um candidato SVG com o PerfectVector, inspecione seus traçados e use Sharp para renderizar o arquivo aprovado em cada tamanho PNG necessário.

Mais do blog

Transforme seu arquivo de imagem em um
SVG editável