> [Site index](https://perfectvector.com/llms.txt) — available pages and descriptions.

# SVG ke Canvas: Muat Gambar Sebelum Mengekspor PNG

> Muat SVG lengkap melalui Image.decode, gambar pada ukuran bitmap pilihan, uji CORS dan pembacaan PNG, serta pisahkan sumber yang bisa diedit dari piksel hasil ekspor.

Source: https://perfectvector.com/id/blog/svg-canvas-image-export

Language: id
Author: Irene Kim
Published: 2026-10-06T08:00:00+09:00
Updated: 2026-10-06T08:00:00+09:00

Untuk menggambar SVG lengkap ke canvas, muat sebagai gambar, tunggu `image.decode()`, lalu teruskan gambar itu ke `drawImage()`. Kemudian periksa ekspor PNG secara terpisah: gambar bisa muncul pada canvas sementara peramban memblokir pembacaan atau penyimpanan pikselnya.

Jalur ini melukis dokumen SVG sebagai gambar. Jalur ini tidak memberikan path SVG yang bisa diedit kepada canvas. Jika ingin merekonstruksi kontur pilihan, gunakan [panduan SVG Path2D](https://perfectvector.com/id/blog/canvas-path2d-svg). Simpan SVG asli apa pun jalur tampilan yang dipilih.

## Pisahkan dekode, penggambaran, dan ekspor

`HTMLImageElement` merupakan masukan yang diterima [canvas drawImage()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage). Lebar dan tinggi tujuannya mengendalikan ukuran yang digambar. [Image.decode()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decode) mengembalikan promise yang selesai ketika gambar telah didekode; permintaan gagal atau masalah dekode bisa membuatnya ditolak.

Dekode berhasil hanyalah pemeriksaan pertama. Gambar lintas origin yang dimuat tanpa izin CORS masih bisa digambar, tetapi mencemari canvas. Pembacaan piksel dan ekspor kemudian gagal. [Panduan gambar berkemampuan CORS](https://developer.mozilla.org/en-US/docs/Web/HTML/How_to/CORS_enabled_image) MDN menjelaskan perbedaannya.

**Simpan sumber, periksa piksel**

![Ilustrasi konseptual wajik berlubang dan lingkaran sebagai kontur sumber, gambar hasil render yang mulus, serta keluaran kisi piksel](https://perfectvector.com/blog/svg-canvas-image-export/image-and-png.webp)

*Ilustrasi: kontur sumber dan piksel yang digambar merupakan representasi berbeda. Bingkai, panah, dan kisi kasar bersifat konseptual; hasil peramban di bawah berasal dari contoh uji asli yang dijalankan secara independen.*

## Buat SVG asli yang mandiri

Simpan sebagai `master.svg`. Berkas berisi satu path dan satu lingkaran, tanpa gambar tertanam, font, atau dependensi eksternal. Wajik dalam dan kanvas di sekelilingnya transparan pada sumber.

```svg
<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>
```

Kami memakai berkas lengkap sebagai gambar, termasuk kedua bentuk dan `viewBox`-nya. Berbeda dari hanya mengekstrak nilai `d` sebuah path, ini meminta renderer gambar SVG peramban untuk melukis dokumen.

Contoh ini sengaja mandiri. SVG yang dipakai sebagai gambar memiliki pembatasan, termasuk sumber daya eksternal dan skrip; lihat [panduan SVG sebagai gambar MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Guides/SVG_as_an_image). Jangan menganggap dekode berhasil pada berkas kecil ini membuktikan bahwa SVG sembarang akan memuat font, gaya, atau gambar tertaut eksternal. Untuk pilihan penyerahan, bandingkan [SVG inline dengan elemen gambar](https://perfectvector.com/id/blog/inline-svg-vs-img).

## Jalankan dua origin lokal dengan respons CORS berbeda

Origin mencakup port-nya. Server khusus lokal berikut menyajikan byte SVG yang sama melalui dua origin. Pada origin kedua, `/cors/master.svg` menyertakan `Access-Control-Allow-Origin: *`; `/plain/master.svg` tidak. Ini memisahkan izin respons dari gambar.

Simpan sebagai `serve-proof.py` di samping SVG dan HTML di bawah. Jalankan `python3 serve-proof.py`, lalu buka `http://127.0.0.1:8770/starter.html`. Kedua port harus kosong; jika diubah, perbarui server dan HTML bersama-sama. Tekan Enter di terminal untuk menghentikan kedua server.

```python
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()
```

Header permisif ini milik contoh uji lokal yang menyajikan gambar kami sendiri. Untuk host gambar produksi, konfigurasikan origin yang diizinkan sesuai kebutuhan pada server yang Anda kendalikan. Menetapkan atribut gambar tidak memberikan akses ke konten server lain.

## Dekode gambar sebelum menggambarnya

Simpan halaman lengkap ini sebagai `starter.html`. Setiap kasus memiliki canvas sendiri, sehingga kasus tercemar tidak bisa mencemari kasus yang berhasil. Tetapkan `crossOrigin` sebelum `src` untuk permintaan yang membutuhkan CORS, seperti dijelaskan [referensi crossOrigin MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/crossOrigin).

Bitmap canvas secara eksplisit `480 × 320`; ukuran tampilan CSS-nya `240 × 160`. Dimensi intrinsik gambar asli adalah `240 × 160`, dan `drawImage()` menskalakannya ke dimensi bitmap pilihan. Ini dimensi yang dipilih, bukan pengukuran rasio piksel perangkat.

```html
<!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>
```

Fungsi ekspor menangani pengecualian yang dilempar maupun hasil blob null. [Canvas.toBlob()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob) mendukung keluaran PNG dan bisa melempar `SecurityError` saat canvas tidak origin-clean. Kedua kasus berhasil menambahkan tautan Simpan PNG; laporan yang terlihat menghilangkan payload base64.

## Baca tahap kegagalan sebelum mengubah SVG

Kami menjalankan kode awal ini persis dalam satu lingkungan peramban Chrome, lalu membuka kembali kedua PNG tersimpan. Pengamatan ini berkaitan dengan berkas asli mandiri dan kedua origin lokal tersebut.

| Kasus                                | Permintaan gambar                   | Hasil gambar/pembacaan/ekspor yang diamati                             |
| ------------------------------------ | ----------------------------------- | ---------------------------------------------------------------------- |
| Origin sama                          | SVG lokal, tanpa atribut CORS       | Didekode pada 240 × 160; digambar; pembacaan dan PNG berhasil          |
| Lintas origin, izin diberikan        | `anonymous` ditambah header respons | Didekode; digambar; pembacaan dan PNG berhasil                         |
| Lintas origin, tanpa permintaan CORS | Tanpa atribut, tanpa header respons | Didekode dan digambar; pembacaan serta ekspor melempar `SecurityError` |
| Lintas origin, izin ditolak          | `anonymous`, tanpa header respons   | Dekode ditolak dengan `EncodingError`; penggambaran tidak dicoba       |
| Berkas tidak ada                     | 404 dari origin sama                | Dekode ditolak dengan `EncodingError`; penggambaran tidak dicoba       |

Kasus tanpa CORS memperlihatkan gambar secara nyata. Tampilan itu tidak membuktikan izin untuk membaca atau mengekspornya. Kasus CORS ditolak dan berkas hilang gagal lebih awal, sehingga menulis ulang geometri tidak akan mengatasi kedua kegagalan permintaan.

Kedua PNG hasil ekspor dibuka kembali pada `480 × 320`. Dalam eksekusi ini keduanya identik per byte, masing-masing 10.509 byte. Pada koordinat bitmap `(192,160)`, bukaannya `RGBA (0,0,0,0)`; sudut juga memiliki alfa nol. Sampel hijau kebiruan pada `(192,80)` adalah `(23,107,91,255)`. Sampel ini memeriksa bukaan dan fill berkas ini, bukan setiap piksel tepi atau setiap fitur SVG.

Persegi panjang putih di halaman peramban merupakan latar CSS di belakang canvas. Itu bukan cat putih yang ditambahkan ke PNG. Jika hasil memerlukan latar buram, gambar latar tersebut secara eksplisit sebelum menggambar citra dan verifikasi lagi berkas hasil ekspor.

## Pisahkan master yang bisa diedit dari PNG

PNG tersimpan berisi piksel. PNG tidak membawa path asli, lingkaran, atau kontur dalam yang bisa diedit. Simpan `master.svg` sebagai sumber vektor dan pilih dimensi bitmap canvas untuk tujuan yang benar-benar membutuhkan PNG. Memperbesar PNG yang ada nantinya tidak memulihkan geometri sumbernya.

Nama berkas SVG saja juga tidak membuktikan semua gambar di dalamnya berupa geometri vektor; gunakan [panduan pemeriksaan raster tertanam](https://perfectvector.com/id/blog/svg-contains-raster-image) saat berkas berisi gambar tertaut atau tertanam.

Jika gambar sumber hanya tersedia sebagai raster dan Anda membutuhkan kontur yang bisa diedit, [alur gambar-ke-vektor PerfectVector](https://perfectvector.com/id/image-to-vector) dapat menyiapkan master SVG. Periksa kontur, warna, dan bukaannya sebelum memakainya dalam prosedur pemuatan ini. Vektorisasi tidak memperbaiki header CORS atau memberikan izin pembacaan canvas.

## Pertanyaan umum

**Bisakah saya meneruskan markup SVG langsung ke drawImage()?**
Tidak. Contoh ini memuat berkas SVG ke HTMLImageElement dan meneruskan gambar yang sudah didekode ke drawImage(). String markup bukan objek gambar itu. Bedakan pemuatan dokumen dari pembuatan data path Path2D.

**Mengapa gambar muncul tetapi ekspor PNG gagal?**
Gambar lintas origin yang dimuat tanpa izin CORS dapat mencemari canvas setelah digambar. Dalam kasus tanpa CORS kami, penggambaran berhasil tetapi pembacaan piksel dan ekspor sama-sama melempar SecurityError. Periksa mode permintaan dan respons server alih-alih menganggap geometri SVG rusak.

**Apakah crossOrigin anonymous memperbaiki setiap URL gambar?**
Tidak. Tetapkan sebelum src, dan server gambar harus menyediakan respons CORS yang cocok. Gambar kami yang sama gagal didekode saat anonymous diminta dari endpoint tanpa header izin.

**Apakah mengekspor canvas sebagai PNG mempertahankan path SVG yang bisa diedit?**
Tidak. PNG adalah keluaran raster. Simpan SVG asli secara terpisah jika perlu mengedit path, bentuk, atau bukaan, dan periksa PNG pada ukuran penyerahan yang dimaksud.

## Sumber

1. [MDN: CanvasRenderingContext2D.drawImage()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage) — Objek gambar yang diterima, ukuran tujuan, dan dimensi intrinsik.
2. [MDN: HTMLImageElement.decode()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/decode) — Promise kesiapan gambar dan penolakan permintaan/dekode.
3. [MDN: Gambar berkemampuan CORS](https://developer.mozilla.org/en-US/docs/Web/HTML/How_to/CORS_enabled_image) — Pembatasan origin-clean pada pembacaan dan ekspor gambar yang digambar.
4. [MDN: HTMLImageElement.crossOrigin](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/crossOrigin) — Mode permintaan CORS untuk pemuatan gambar.
5. [MDN: HTMLCanvasElement.toBlob()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob) — Pembuatan blob PNG, keluaran null, dan SecurityError.
6. [MDN: SVG sebagai gambar](https://developer.mozilla.org/en-US/docs/Web/SVG/Guides/SVG_as_an_image) — Konteks gambar SVG dan pembatasan sumber daya eksternal.

***

Bekerja dari raster asli yang membutuhkan gambar yang bisa diedit? [Buat master SVG dengan PerfectVector](https://perfectvector.com/id/image-to-vector), periksa kontur dan bukaannya, lalu simpan master itu sambil memeriksa rendering canvas serta PNG hasil ekspor secara terpisah.

## Explore

- [Read the HTML article](https://perfectvector.com/id/blog/svg-canvas-image-export)
- [Ikon SVG MapLibre: Periksa Piksel dan Ukuran Simbol](https://perfectvector.com/id/blog/maplibre-svg-symbol-image.md)
- [Gambar SVG p5.js: Muat, Skalakan, dan Simpan Sumber](https://perfectvector.com/id/blog/p5js-svg-artwork.md)
- [Product facts](https://perfectvector.com/product-facts.md)
- [FAQ](https://perfectvector.com/faq.md)
- [Pricing and credits](https://perfectvector.com/pricing.md)
