Da SVG a Canvas: carica la grafica prima di esportare PNG
Carica un SVG completo con Image.decode, disegnalo alle dimensioni bitmap scelte, prova CORS e lettura PNG e separa la sorgente modificabile dai pixel esportati.
In questa pagina
Per disegnare un SVG completo su canvas, caricalo come immagine, attendi image.decode() e passa l’immagine a drawImage(). Poi controlla separatamente l’esportazione PNG: un’immagine può apparire sul canvas mentre il browser impedisce di leggere o salvare i suoi pixel.
Questo percorso dipinge il documento SVG come immagine. Non fornisce al canvas tracciati SVG modificabili. Se vuoi invece ricostruire contorni selezionati, usa la guida SVG Path2D. Conserva l’SVG originale qualunque percorso di visualizzazione tu scelga.
Separa decodifica, pittura ed esportazione
Un HTMLImageElement è un input accettato da canvas drawImage(). Larghezza e altezza di destinazione controllano le dimensioni dipinte. Image.decode() restituisce una promise che si risolve quando l’immagine è decodificata; una richiesta fallita o un problema di decodifica possono rifiutarla.
La decodifica riuscita è solo il primo controllo. Un’immagine cross-origin caricata senza permesso CORS può essere disegnata, ma contamina il canvas. Lettura dei pixel ed esportazione poi falliscono. La guida MDN alle immagini abilitate CORS spiega la distinzione.

Crea un SVG originale autonomo
Salvalo come master.svg. Contiene un tracciato e un cerchio, senza immagine incorporata, font o dipendenza esterna. Il rombo interno e la tela circostante sono trasparenti nella sorgente.
<svg xmlns="http://www.w3.org/2000/svg" width="240" height="160" viewBox="0 0 240 160">
<path d="M96 24 L152 80 L96 136 L40 80 Z M96 58 L118 80 L96 102 L74 80 Z" fill="#176b5b" fill-rule="evenodd"/>
<circle cx="200" cy="80" r="24" fill="#d4a53a"/>
</svg>Usiamo l’intero file come immagine, comprese entrambe le forme e il suo viewBox. Diversamente dall’estrarre solo il valore d di un tracciato, questo chiede al renderer di immagini SVG del browser di dipingere il documento.
L’esempio è volutamente autonomo. Un SVG usato come immagine ha restrizioni, anche su risorse esterne e script; vedi la guida MDN a SVG come immagine. Non considerare la decodifica riuscita di questo piccolo file come prova che un SVG qualsiasi caricherà font, stili o grafica collegata esterni. Per le scelte di consegna, confronta SVG inline e un elemento immagine.
Esegui due origini locali con risposte CORS diverse
Un’origine include la porta. Il seguente server solo locale espone gli stessi byte SVG tramite due origini. Nella seconda, /cors/master.svg include Access-Control-Allow-Origin: *; /plain/master.svg no. Questo isola il permesso della risposta dalla grafica.
Salvalo come serve-proof.py accanto all’SVG e all’HTML sotto. Esegui python3 serve-proof.py, poi apri http://127.0.0.1:8770/starter.html. Entrambe le porte devono essere libere; se le cambi, aggiorna insieme server e HTML. Premi Invio nel terminale per fermare entrambi i server.
from http.server import ThreadingHTTPServer, SimpleHTTPRequestHandler
from pathlib import Path
from threading import Thread
root = Path(__file__).parent
class Handler(SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=str(root), **kwargs)
def do_GET(self):
if self.path in ['/cors/master.svg', '/plain/master.svg']:
payload = (root / 'master.svg').read_bytes()
self.send_response(200)
self.send_header('Content-Type', 'image/svg+xml')
self.send_header('Cache-Control', 'no-store')
if self.path.startswith('/cors/'):
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
self.wfile.write(payload)
else:
super().do_GET()
servers = [ThreadingHTTPServer(('127.0.0.1', port), Handler) for port in (8770, 8783)]
for server in servers:
Thread(target=server.serve_forever, daemon=True).start()
try:
input('Original SVG proof on 127.0.0.1:8770 and :8783. Enter to stop.\n')
finally:
for server in servers:
server.shutdown()
server.server_close()Questa intestazione permissiva appartiene a un test locale che serve la nostra grafica. Per un host immagini di produzione, configura l’origine consentita appropriata su un server che controlli. Impostare un attributo dell’immagine non concede accesso ai contenuti di un altro server.
Decodifica l’immagine prima di disegnarla
Salva questa pagina completa come starter.html. Ogni caso ha il proprio canvas, così quello contaminato non può contaminare i casi riusciti. Imposta crossOrigin prima di src per le richieste che richiedono CORS, come descritto nel riferimento MDN su crossOrigin.
La bitmap del canvas è esplicitamente 480 × 320; la dimensione di visualizzazione CSS è 240 × 160. Le dimensioni intrinseche dell’immagine originale sono 240 × 160 e drawImage() la scala nelle dimensioni bitmap scelte. Sono dimensioni deliberate, non una misura del rapporto di pixel del dispositivo.
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<title>Original SVG to canvas export</title>
<style>
body { margin: 24px; font: 16px system-ui; background: #f6f2e8; }
main { display: grid; grid-template-columns: repeat(2, 280px); gap: 20px; }
figure { margin: 0; }
canvas { display: block; width: 240px; height: 160px; background: white; }
figcaption { margin: 6px 0; }
pre { white-space: pre-wrap; font-size: 12px; }
</style>
<h1>Decode, paint, then check PNG access</h1>
<main id="cases"></main>
<pre id="report"></pre>
<script>
function pngBlob(canvas) {
return new Promise((resolve, reject) => {
try { canvas.toBlob(blob => blob ? resolve(blob) : reject(new Error('Empty PNG')), 'image/png'); }
catch (error) { reject(error); }
});
}
function dataURL(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.readAsDataURL(blob);
});
}
async function render(test) {
const figure = document.createElement('figure');
const canvas = document.createElement('canvas');
canvas.width = 480;
canvas.height = 320;
const caption = document.createElement('figcaption');
figure.append(canvas, caption);
document.getElementById('cases').append(figure);
const result = { id: test.id, url: new URL(test.src, location.href).href,
crossOrigin: test.crossOrigin ?? null, canvasWidth: canvas.width, canvasHeight: canvas.height };
const image = new Image();
if (test.crossOrigin) image.crossOrigin = test.crossOrigin;
image.src = result.url;
try { await image.decode(); }
catch (error) {
result.stage = 'decode';
result.error = error.name;
caption.textContent = test.id + ': decode ' + error.name;
return result;
}
result.naturalWidth = image.naturalWidth;
result.naturalHeight = image.naturalHeight;
const context = canvas.getContext('2d');
context.drawImage(image, 0, 0, canvas.width, canvas.height);
result.stage = 'painted';
try {
result.opening = [...context.getImageData(192, 160, 1, 1).data];
result.fill = [...context.getImageData(192, 80, 1, 1).data];
result.corner = [...context.getImageData(0, 0, 1, 1).data];
} catch (error) { result.readbackError = error.name; }
try {
const blob = await pngBlob(canvas);
result.pngType = blob.type;
result.pngBytes = blob.size;
result.pngDataURL = await dataURL(blob);
result.export = 'ok';
const download = document.createElement('a');
download.href = result.pngDataURL;
download.download = test.id + '.png';
download.textContent = 'Save PNG';
figure.append(download);
} catch (error) { result.export = 'failed'; result.exportError = error.name; }
caption.textContent = test.id + ': ' + result.stage + ', PNG ' + result.export;
return result;
}
async function run() {
const tests = [
{ id: 'same-origin', src: 'master.svg' },
{ id: 'cors-allowed', src: 'http://127.0.0.1:8783/cors/master.svg', crossOrigin: 'anonymous' },
{ id: 'no-cors-image', src: 'http://127.0.0.1:8783/plain/master.svg' },
{ id: 'cors-denied', src: 'http://127.0.0.1:8783/plain/master.svg', crossOrigin: 'anonymous' },
{ id: 'missing-file', src: 'missing.svg' },
];
const results = [];
for (const test of tests) results.push(await render(test));
document.getElementById('report').textContent = JSON.stringify({ pageOrigin: location.origin, results }, (key, value) => key === 'pngDataURL' ? undefined : value, 2);
}
run();
</script>
</html>La funzione di esportazione gestisce sia un’eccezione lanciata sia un risultato blob nullo. Canvas.toBlob() supporta PNG e può lanciare SecurityError quando il canvas non è origin-clean. I due casi riusciti aggiungono un collegamento Save PNG; il rapporto visibile omette il contenuto base64.
Leggi la fase dell’errore prima di cambiare l’SVG
Abbiamo eseguito esattamente questo esempio in un ambiente browser Chrome, poi riaperto entrambi i PNG salvati. Le osservazioni riguardano il file originale autonomo e queste due origini locali.
| Caso | Richiesta immagine | Disegno/lettura/esportazione osservati |
|---|---|---|
| Stessa origine | SVG locale, nessun attributo CORS | Decodificato a 240 × 160; dipinto; lettura e PNG riusciti |
| Cross-origin, permesso concesso | anonymous più intestazione di risposta | Decodificato; dipinto; lettura e PNG riusciti |
| Cross-origin, nessuna richiesta CORS | Nessun attributo, nessuna intestazione di risposta | Decodificato e dipinto; lettura ed esportazione hanno lanciato SecurityError |
| Cross-origin, permesso negato | anonymous, nessuna intestazione di risposta | Decodifica rifiutata con EncodingError; nessun disegno tentato |
| File mancante | 404 della stessa origine | Decodifica rifiutata con EncodingError; nessun disegno tentato |
Il caso senza CORS mostrava visibilmente la grafica. Quell’aspetto non stabiliva il permesso di leggerla o esportarla. I casi CORS negato e file mancante fallivano prima, quindi riscrivere la geometria non risolverebbe nessuno dei due errori di richiesta.
Entrambi i PNG esportati si riaprivano a 480 × 320. In questa esecuzione erano identici nei byte, 10509 byte ciascuno. Alla coordinata bitmap (192,160), l’apertura era RGBA (0,0,0,0); anche l’angolo aveva alfa zero. Il campione verde petrolio a (192,80) era (23,107,91,255). Questi campioni verificano apertura e riempimento del file, non ogni pixel di bordo o funzione SVG.
I rettangoli bianchi nella pagina del browser sono sfondi CSS dietro i canvas. Non sono pittura bianca aggiunta al PNG. Se il risultato richiede uno sfondo opaco, dipingilo esplicitamente prima di disegnare l’immagine e verifica di nuovo il file esportato.
Mantieni il master modificabile separato dal PNG
Il PNG salvato contiene pixel. Non conserva tracciato, cerchio o contorno interno modificabile originali. Mantieni master.svg come sorgente vettoriale e scegli le dimensioni bitmap del canvas per la destinazione che richiede effettivamente PNG. Ingrandire successivamente un PNG esistente non ripristina la geometria sorgente.
Neppure il solo nome SVG dimostra che tutta la grafica interna sia geometria vettoriale; usa la guida all’ispezione dei raster incorporati quando un file contiene immagini collegate o incorporate.
Se la grafica sorgente esiste solo come immagine raster e ti servono contorni modificabili, il procedimento da immagine a vettore di PerfectVector può preparare un master SVG. Esamina contorno, colori e aperture prima di usarlo in questa routine di caricamento. La vettorializzazione non corregge intestazioni CORS e non concede il permesso di leggere il canvas.
Domande frequenti
Posso passare markup SVG direttamente a drawImage()? No. Questo esempio carica un file SVG in un HTMLImageElement e passa l’immagine decodificata a drawImage(). Una stringa di markup non è quell’oggetto immagine. Mantieni distinto il caricamento del documento dalla costruzione di dati dei tracciati Path2D.
Perché l’immagine appare ma l’esportazione PNG fallisce? Un’immagine cross-origin caricata senza permesso CORS può contaminare il canvas dopo il disegno. Nel nostro caso senza CORS, la pittura riusciva ma lettura dei pixel ed esportazione lanciavano SecurityError. Controlla modalità della richiesta e risposta del server invece di presumere che la geometria SVG sia danneggiata.
crossOrigin anonymous corregge ogni URL immagine? No. Impostalo prima di src e il server dell’immagine deve fornire una risposta CORS compatibile. La stessa nostra grafica falliva la decodifica quando anonymous era richiesto all’endpoint senza intestazione di permesso.
Esportare il canvas come PNG conserva tracciati SVG modificabili? No. PNG è output raster. Conserva l’SVG originale separatamente se devi modificare tracciati, forme o aperture e controlla il PNG alla dimensione di consegna prevista.
Fonti
- MDN: CanvasRenderingContext2D.drawImage() — Oggetti immagine accettati, dimensioni di destinazione e dimensioni intrinseche.
- MDN: HTMLImageElement.decode() — Promise di disponibilità dell’immagine e rifiuto della richiesta/decodifica.
- MDN: Immagini abilitate CORS — Restrizioni origin-clean sulla lettura ed esportazione delle immagini disegnate.
- MDN: HTMLImageElement.crossOrigin — Modalità di richiesta CORS per il caricamento immagini.
- MDN: HTMLCanvasElement.toBlob() — Generazione di blob PNG, output nullo e SecurityError.
- MDN: SVG come immagine — Contesti immagine SVG e restrizioni delle risorse esterne.
Parti da un originale raster che richiede grafica modificabile? Crea un master SVG con PerfectVector, esamina contorno e aperture, poi conserva il master mentre controlli separatamente rendering canvas e PNG esportato.
Altro dal blog

Icone SVG in MapLibre: controlla pixel e dimensioni
Decodifica un SVG per un livello simboli MapLibre, controlla densità e dimensione dell'icona e conserva un master modificabile. Verifica forma e apertura visualizzate.

Immagini SVG in p5.js: caricale e conserva la sorgente
Carica un SVG in p5.js 2.3.4 con setup asincrono, controlla l’apertura trasparente a tre dimensioni Canvas e separa i tracciati modificabili dall’immagine visualizzata.
