<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Usman Bashir</title>
    <description>The latest articles on DEV Community by Usman Bashir (@usman_basheers).</description>
    <link>https://dev.to/usman_basheers</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4065175%2F505f679e-e978-4c9a-9070-6a534301d15f.png</url>
      <title>DEV Community: Usman Bashir</title>
      <link>https://dev.to/usman_basheers</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/usman_basheers"/>
    <language>en</language>
    <item>
      <title>SVG to JPG: What Actually Changes During the Conversion</title>
      <dc:creator>Usman Bashir</dc:creator>
      <pubDate>Mon, 17 Aug 2026 14:46:53 +0000</pubDate>
      <link>https://dev.to/usman_basheers/svg-to-jpg-what-actually-changes-during-the-conversion-58e</link>
      <guid>https://dev.to/usman_basheers/svg-to-jpg-what-actually-changes-during-the-conversion-58e</guid>
      <description>&lt;p&gt;Converting SVG to JPG renders the vector artwork into a fixed grid of pixels, then compresses that grid using JPEG's lossy algorithm. Transparent areas become white, because JPEG has no alpha channel. Everything else about what changes during that process is a choice you can control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vector to raster
&lt;/h2&gt;

&lt;p&gt;An SVG is a set of instructions: paths, shapes, fills, strokes. A JPEG is a grid of pixels. The conversion is rasterization: the browser draws the SVG on a canvas at a specific pixel size, then encodes that canvas as a JPEG file.&lt;/p&gt;

&lt;p&gt;This is why you cannot simply rename an SVG to &lt;code&gt;.jpg&lt;/code&gt;. The two formats describe images in fundamentally different ways. Rasterization is a one-way operation: you can always go from SVG to JPEG, but you cannot recover the original vector data from a JPEG.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transparency becomes white
&lt;/h2&gt;

&lt;p&gt;JPEG does not support transparency. If your SVG has transparent areas, those areas become solid white in the JPEG output. This happens at the canvas level, before compression: the renderer fills the canvas with a white background, then draws the SVG on top.&lt;/p&gt;

&lt;p&gt;If white is not the right background for your use case, use PNG instead. PNG supports transparency and is the right format when the background matters. Use JPEG when you know the image will always sit on a white or light background, or when file size is a higher priority than transparency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quality and compression
&lt;/h2&gt;

&lt;p&gt;JPEG compression is lossy. It divides the image into blocks and discards information the eye is least likely to notice. A lower quality setting discards more, producing a smaller file but introducing visible artifacts: blocky patterns around sharp edges and colour banding in flat areas.&lt;/p&gt;

&lt;p&gt;Four quality settings you will commonly see (70%, 80%, 90%, 100%):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;70%:&lt;/strong&gt; smallest file; noticeable artifacts on shapes with sharp edges or solid fills. JPEG's compression was designed for photographs, where colour varies gradually. Geometric artwork from an SVG breaks that assumption, so artifacts appear more visibly at lower quality than they would on a photo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;80%:&lt;/strong&gt; reasonable for most screen use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;90%:&lt;/strong&gt; good quality with moderate file size reduction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;100%:&lt;/strong&gt; no quantization reduction applied. The DCT transform still runs, so JPEG at 100% is technically still lossy, but artifacts are not visible under normal viewing conditions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For SVG-derived content with solid colour fills and crisp geometric edges, the difference between quality settings is more visible than it would be for a photograph. If you see blocky artifacts or colour fringing, go higher.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scale and resolution
&lt;/h2&gt;

&lt;p&gt;The scale setting controls how many pixels to use when rasterizing. SVG Lab offers 1x, 2x, and 4x:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1x:&lt;/strong&gt; one output pixel per CSS pixel of the SVG's stated dimensions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2x:&lt;/strong&gt; double the width and height, four times the pixel count.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4x:&lt;/strong&gt; four times the width and height, sixteen times the pixel count.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For screen use, 2x is a reasonable default because it covers high density displays without an unnecessarily large file. 4x makes sense for print, for images that will be displayed much larger than the SVG's native dimensions, or when the reader is expected to zoom in.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use JPG vs PNG vs SVG
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keep SVG&lt;/strong&gt; for icons, logos, and illustrations that need to scale cleanly or will be embedded directly in HTML.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use PNG&lt;/strong&gt; when you need a raster file and the image has transparency or hard geometric edges. PNG is lossless; solid colours and crisp shapes compress without artifacts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use JPG&lt;/strong&gt; when you need a raster file, transparency is not required, and file size is a concern. JPG handles photographs and complex images well. For flat geometric artwork, PNG is a better choice because JPEG's compression algorithm was not designed for that kind of content.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick answers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why is my JPG white where the SVG was transparent?&lt;/strong&gt;&lt;br&gt;
JPEG does not support transparency. Any transparent area is filled with white when the SVG is drawn onto the canvas. Use PNG if you need to preserve transparency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What quality setting should I use?&lt;/strong&gt;&lt;br&gt;
80% is a reasonable default for most screen use. Go higher if you see visible artifacts, especially around sharp edges or solid colour fills. Go lower only if file size is a hard constraint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What scale should I use?&lt;/strong&gt;&lt;br&gt;
2x is a reasonable default for screen use. Use 4x for print or when the image will be displayed much larger than its native SVG dimensions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I convert back from JPG to SVG?&lt;/strong&gt;&lt;br&gt;
Not directly. The JPEG contains only pixels, not the original vector paths. Tracing a JPEG produces a new SVG as a reconstruction, not a round-trip conversion.&lt;/p&gt;

</description>
      <category>svg</category>
      <category>webdev</category>
      <category>images</category>
      <category>frontend</category>
    </item>
    <item>
      <title>How to Resize an SVG Without Distorting It</title>
      <dc:creator>Usman Bashir</dc:creator>
      <pubDate>Mon, 17 Aug 2026 14:46:41 +0000</pubDate>
      <link>https://dev.to/usman_basheers/how-to-resize-an-svg-without-distorting-it-30cb</link>
      <guid>https://dev.to/usman_basheers/how-to-resize-an-svg-without-distorting-it-30cb</guid>
      <description>&lt;p&gt;Resizing an SVG means changing the &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes on the root &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; element. If the SVG has a &lt;code&gt;viewBox&lt;/code&gt;, you usually leave it unchanged. The shapes and paths inside the file do not change at all.&lt;/p&gt;

&lt;p&gt;That is the short version. The reason it gets confusing is that SVG has two separate concepts involved: the size the file says it is, and the coordinate space its artwork was drawn in. They are related but not the same thing, and knowing the difference helps you resize with the result you actually want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Width and height: the display size
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes on the root &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; element tell a browser how much space to give the image by default. They are a rendering hint, not the fixed dimensions of the artwork:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"400"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"300"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 400 300"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- artwork here --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you change &lt;code&gt;width&lt;/code&gt; to 800 and &lt;code&gt;height&lt;/code&gt; to 600, the image renders twice as large on screen. The artwork inside scales to fill that space. Nothing about the coordinates of the shapes inside changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  ViewBox: the coordinate space
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;viewBox&lt;/code&gt; attribute defines the internal coordinate system the artwork was drawn in. Its four values are &lt;code&gt;min-x min-y width height&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;viewBox="0 0 400 300"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means the artwork occupies a space 400 units wide and 300 units tall, starting at the top-left corner. When you set the outer &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt;, the browser maps those internal units to pixels automatically.&lt;/p&gt;

&lt;p&gt;For a straight resize where you want the same artwork at a different display size, you only need to change the outer &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt;. The &lt;code&gt;viewBox&lt;/code&gt; stays the same, and the browser scales the artwork to match.&lt;/p&gt;

&lt;h2&gt;
  
  
  Changing viewBox is not the same as resizing
&lt;/h2&gt;

&lt;p&gt;Changing the &lt;code&gt;viewBox&lt;/code&gt; values without touching &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; changes what portion of the coordinate space is visible, which crops or zooms the artwork rather than scales it. For a standard resize, leave &lt;code&gt;viewBox&lt;/code&gt; alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens when an SVG has no viewBox
&lt;/h2&gt;

&lt;p&gt;Some SVGs are exported without a &lt;code&gt;viewBox&lt;/code&gt;. They have &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; but no coordinate system defined:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"400"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"300"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- artwork here --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without a &lt;code&gt;viewBox&lt;/code&gt;, scaling the file can produce unexpected results depending on how the SVG is embedded. The reliable fix is to add a &lt;code&gt;viewBox&lt;/code&gt; that matches the current &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt;, which establishes the coordinate space explicitly before any resize:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"400"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"300"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 400 300"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Aspect ratio
&lt;/h2&gt;

&lt;p&gt;If you change &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; independently without preserving the ratio between them, the artwork distorts. When using a resize tool with an aspect ratio lock, changing width recalculates height automatically to keep proportions intact. Without the lock, you are responsible for keeping the math consistent.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the resize operation actually touches
&lt;/h2&gt;

&lt;p&gt;A correct SVG resize edits only the root &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; element's &lt;code&gt;width&lt;/code&gt;, &lt;code&gt;height&lt;/code&gt;, and &lt;code&gt;viewBox&lt;/code&gt; attributes. It does not touch paths, shapes, groups, or styles inside the file. The artwork is identical before and after; only the size metadata changes.&lt;/p&gt;

&lt;p&gt;This is also why resizing an SVG does not degrade it. SVG is a vector format. No rasterization happens, no information is discarded, and the file size stays nearly the same. Resizing an SVG is not a way to reduce file weight; it is purely a metadata edit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick answers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does resizing an SVG reduce quality?&lt;/strong&gt;&lt;br&gt;
No. SVG is a vector format. Changing the attributes does not rasterize the file or discard any information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if my SVG looks squished after resizing?&lt;/strong&gt;&lt;br&gt;
You probably changed &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; independently without maintaining the aspect ratio. Keep the ratio consistent, or use a tool with an aspect ratio lock.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What about SVG files with no viewBox?&lt;/strong&gt;&lt;br&gt;
Add a &lt;code&gt;viewBox&lt;/code&gt; first, using the file's existing &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; values as the coordinate range. This makes the file behave predictably when you then change the display dimensions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do the paths inside the SVG change?&lt;/strong&gt;&lt;br&gt;
No. The resize only touches the root &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; element's attributes. Everything inside is unchanged.&lt;/p&gt;

</description>
      <category>svg</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>html</category>
    </item>
    <item>
      <title>Why Your SVG to PDF Export Rasterizes (and How to Stop It)</title>
      <dc:creator>Usman Bashir</dc:creator>
      <pubDate>Thu, 13 Aug 2026 04:52:43 +0000</pubDate>
      <link>https://dev.to/usman_basheers/why-your-svg-to-pdf-export-rasterizes-and-how-to-stop-it-3603</link>
      <guid>https://dev.to/usman_basheers/why-your-svg-to-pdf-export-rasterizes-and-how-to-stop-it-3603</guid>
      <description>&lt;p&gt;Print the SVG to PDF from a browser, or run it through a vector toolchain. Either way, the output can stay fully vector, because SVG and PDF are both path-based formats. The catch is that most of the common ways people do this rasterize part or all of the document without any error telling you it happened.&lt;/p&gt;

&lt;p&gt;The interesting question isn't "how do I convert SVG to PDF." It's "how do I convert it without turning vectors into a bitmap, and what specifically breaks when I do." That's what this post covers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Same model, different container
&lt;/h2&gt;

&lt;p&gt;SVG and PDF both describe drawings as paths, fills, strokes, and transforms. Converting one to the other is closer to re-encoding the same instructions than redrawing the artwork. Compare that to SVG to PNG, where you're committing to a pixel grid permanently. A correct SVG to PDF conversion keeps every path as a path down to the anchor points, not as sampled pixels.&lt;/p&gt;

&lt;p&gt;Whether you actually get that depends on the conversion route and what the SVG contains.&lt;/p&gt;

&lt;h2&gt;
  
  
  The browser print route, step by step
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Open the SVG in a browser tab as real rendered markup, not as a linked image.&lt;/li&gt;
&lt;li&gt;Trigger print, either via keyboard shortcut or programmatically:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Choose "Save as PDF" as the print destination instead of a physical printer.&lt;/li&gt;
&lt;li&gt;Save.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The browser's print pipeline renders SVG the same way it displays it on screen, so paths and (usually) text survive as vector data. What breaks isn't the print step, it's everything upstream of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The unit mismatch that crops your drawing
&lt;/h2&gt;

&lt;p&gt;SVG's default unit is the CSS pixel at 96 dpi. PDF and print contexts think in physical units. Without an explicit page size, you inherit the browser's default (commonly A4 or Letter) with its own margins, and your artwork gets shrunk, centered, or cropped inside it.&lt;/p&gt;

&lt;p&gt;The conversion is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;mm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;px&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;25.4&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;96&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An 800 by 600 unitless SVG works out to roughly 211.67mm by 158.75mm (800 * 25.4 / 96 = 211.666..., 600 * 25.4 / 96 = 158.75). Set &lt;code&gt;@page&lt;/code&gt; to that exact size with zero margin, and the PDF page becomes the artwork instead of a default sheet with the artwork floating inside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@page&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;211.67mm&lt;/span&gt; &lt;span class="m"&gt;158.75mm&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Skip this and you get a PDF that renders fine on screen but exports tiny, off-center, or clipped. It's a page size problem, not a rendering bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Text-to-paths is a real trade-off, not a free upgrade
&lt;/h2&gt;

&lt;p&gt;If a &lt;code&gt;&amp;lt;text&amp;gt;&lt;/code&gt; element references a font that isn't available at print time, the browser substitutes something else, and line wraps, overflow, and spacing all shift with it. Converting text to paths sidesteps that entirely: every glyph becomes a vector shape, so layout is locked in regardless of what fonts exist on whatever machine opens the PDF later.&lt;/p&gt;

&lt;p&gt;But converted text is no longer text. It doesn't select, it doesn't search, copy-paste from the PDF returns nothing useful. If the document needs to stay searchable, you have to solve font availability instead, you can't get both guarantees from the same export.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inline everything or it silently vanishes
&lt;/h2&gt;

&lt;p&gt;Images referenced by URL, external stylesheets loaded via &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; or &lt;code&gt;@import&lt;/code&gt;, anything the print context can't resolve synchronously: none of it errors, it just doesn't appear. Convert linked images to base64 data URIs and fold external CSS into an inline &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block before you print. Missing images in an otherwise-correct export is almost always this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Filters and masks are the real rasterization trigger
&lt;/h2&gt;

&lt;p&gt;This is the one that catches people who did the page size and fonts and inlining correctly. Filters, masks, clip paths, and blend modes commonly get flattened to a bitmap for just that region, even while the rest of the document stays vector. A Gaussian blur or a complex mask usually can't be expressed as PDF path data, so the renderer rasterizes that piece at whatever resolution the print pipeline picked. "My PDF is vector except this one blurred shape" is the expected outcome of using those effects, not a bug in your export.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verifying the output is actually vector
&lt;/h2&gt;

&lt;p&gt;Two checks, both fast:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zoom past 400 percent in a PDF viewer. Vector paths stay crisp at any zoom. Pixel edges or a soft stair-step mean that region was rasterized.&lt;/li&gt;
&lt;li&gt;Try selecting text with the viewer's text tool. If it highlights as text, it's still text. If nothing selects, it's paths (or wasn't text to start with).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where SVG Lab's PDF export sits in all this
&lt;/h2&gt;

&lt;p&gt;SVG Lab (the editor I built, at &lt;a href="https://svglab.app" rel="noopener noreferrer"&gt;svglab.app&lt;/a&gt;) has PDF in its export menu next to SVG and PNG. The implementation is exactly the print-to-PDF route above: it opens a print-only window with the SVG inlined, computes an &lt;code&gt;@page&lt;/code&gt; size from the document's own dimensions using the same px-to-mm math, then calls &lt;code&gt;window.print()&lt;/code&gt; so the browser dialog handles the actual "Save as PDF" step. Worth saying plainly: this is a print-to-PDF flow, not a server-side converter, and because it opens a new window, a pop-up blocker can stop it (the app surfaces a status message telling you to allow pop-ups when that happens). Export is gated behind sign-in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick answers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does the conversion lose quality?&lt;/strong&gt; Only where something rasterizes. Paths, fills, and strokes translate losslessly. Filters, masks, blend modes, and fixed-pixel rendering are the usual culprits when it doesn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need to install anything?&lt;/strong&gt; No. Any current browser can print an SVG to PDF via the built-in dialog. A CLI vector tool is only worth reaching for if you're batch-converting many files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why did I get a blank PDF?&lt;/strong&gt; Check for a blocked pop-up window first if your tool opens one, then check for external stylesheets or images that didn't resolve in the print context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why did my text reflow?&lt;/strong&gt; Font substitution: the referenced font wasn't available at print time. Fix the font availability, or convert text to paths and accept that it's no longer selectable or searchable.&lt;/p&gt;

</description>
      <category>svg</category>
      <category>pdf</category>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Building an SVG Editor as a Non-Designer</title>
      <dc:creator>Usman Bashir</dc:creator>
      <pubDate>Tue, 11 Aug 2026 14:38:40 +0000</pubDate>
      <link>https://dev.to/usman_basheers/building-an-svg-editor-as-a-non-designer-4n2i</link>
      <guid>https://dev.to/usman_basheers/building-an-svg-editor-as-a-non-designer-4n2i</guid>
      <description>&lt;p&gt;I'm not a designer. I can picture a design clearly enough in my head, but getting an AI assistant like Claude or Codex to actually produce the SVG I was picturing was a grind: vague prompts, wrong outputs, round after round of "no, not like that" iterations that never quite closed the gap between what I imagined and what came back.&lt;/p&gt;

&lt;p&gt;Every time that happened, I was facing the same choice: keep spending days trying to prompt a design into existence, or spend an afternoon building a small tool that let me just do it myself, in minutes, exactly as imagined. I kept choosing the tool. That's how &lt;a href="https://svglab.app" rel="noopener noreferrer"&gt;SVG Lab&lt;/a&gt; happened: a free browser SVG editor, no install, that lets me draw whatever I'm picturing, place it anywhere, and drop it straight into whatever device-screen UI I'm building. That specific task, getting a design out of my head and into a real interface, had been genuinely hard to do through AI assistants alone, for someone without a design background.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then the SVGs needed to move
&lt;/h2&gt;

&lt;p&gt;Once I had static SVGs I was happy with, the next problem showed up immediately: they needed animation, and there was no tool that let me just try different animations on my own artwork to see what actually fit. I'd load an SVG somewhere and either get a fixed preset that didn't match, or a code-first animation tool that assumed I already knew what I was building toward.&lt;/p&gt;

&lt;p&gt;The real block wasn't motion design skill: it was vocabulary. I didn't know what the animations I was imagining were actually called. Was it a "stroke draw-in"? A "path morph"? I genuinely didn't know the term, which meant I couldn't even write a prompt for it. You can't ask an AI for something you can't name.&lt;/p&gt;

&lt;p&gt;So I built a small animator instead: load an SVG, click through a set of preset animations, watch each one play on your own artwork, and pick by eye. Discovery instead of vocabulary: you don't need the term if you can just look at it happening on the thing you actually made.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that stung the most
&lt;/h2&gt;

&lt;p&gt;The clearest version of this whole pattern showed up when I made the launch video for SVG Lab, a 25-second clip, mostly animated SVGs, built with HyperFrames through Codex. It took days. Same root cause as everything above: I didn't know which animations existed or what to call the motion I had in my head, so I couldn't direct the AI toward it.&lt;/p&gt;

&lt;p&gt;Eventually I gave up on getting my own vision out. I found someone else's launch video on X, showed it to Codex, and said "make something like this." What came back was fine. It worked. But it wasn't the video in my head, it was someone else's video, wearing my product's name.&lt;/p&gt;

&lt;p&gt;That one stuck with me more than the others, because it wasn't a drawing or an animation preset: it was the actual launch, the thing meant to represent the whole project, and I ended up shipping a compromise on it. The lesson I keep coming back to: people shouldn't have to give up on their own vision for something as important as how they introduce what they built. The marketing video is one of the last steps after the actual product is done, and it's exactly the kind of step where "just describe it to the AI" quietly stops working if you don't have the vocabulary, and most builders don't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this is going
&lt;/h2&gt;

&lt;p&gt;SVG Lab and the animator aren't meant to stay small utilities to me. The direction I want to take this is a platform that handles the whole design process, end to end, for people who can build software but can't design it: the same "thousands of new developers and vibe coders like me" I'd guess make up a lot of anyone reading this. App builders should be able to grab the animated SVG or its code directly for their project, with no separate video step in the way. Founders and marketers should be able to compose animated SVGs into scenes and render an actual launch video from inside the same tool, instead of hitting the wall I hit.&lt;/p&gt;

&lt;p&gt;That's still a direction, not a finished thing. Some of this is genuinely built today, some of it is still ahead. But the throughline hasn't changed since the first version: I can't always name what I want, and I shouldn't have to, to get it made.&lt;/p&gt;

&lt;p&gt;If any of this sounds familiar (you can build the thing but the design step keeps costing you days you don't have), &lt;a href="https://svglab.app" rel="noopener noreferrer"&gt;SVG Lab is free to try&lt;/a&gt;. Prefer to start with one task instead of the whole editor? The &lt;a href="https://svglab.app/svg-to-png" rel="noopener noreferrer"&gt;SVG to PNG converter&lt;/a&gt; and the other single-purpose tools sit outside the app too, for exactly that.&lt;/p&gt;




&lt;p&gt;More at &lt;a href="https://svglab.app" rel="noopener noreferrer"&gt;svglab.app&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>svg</category>
      <category>sideprojects</category>
      <category>webdev</category>
    </item>
    <item>
      <title>The SVG Color Cascade Nobody Explains (fill, stroke, currentColor, and why img src breaks it)</title>
      <dc:creator>Usman Bashir</dc:creator>
      <pubDate>Fri, 07 Aug 2026 17:53:57 +0000</pubDate>
      <link>https://dev.to/usman_basheers/the-svg-color-cascade-nobody-explains-fill-stroke-currentcolor-and-why-img-src-breaks-it-4a</link>
      <guid>https://dev.to/usman_basheers/the-svg-color-cascade-nobody-explains-fill-stroke-currentcolor-and-why-img-src-breaks-it-4a</guid>
      <description>&lt;p&gt;Change an SVG's color by editing &lt;code&gt;fill&lt;/code&gt; and &lt;code&gt;stroke&lt;/code&gt;, either as attributes or through CSS. Simple in theory. In practice there are three places a color can be declared in the same file, they follow the normal CSS cascade, and if you don't know that, "I changed the fill and nothing happened" turns into a twenty-minute debugging session.&lt;/p&gt;

&lt;p&gt;Here's the part of SVG color handling that usually doesn't get spelled out.&lt;/p&gt;

&lt;h2&gt;
  
  
  fill and stroke are separate properties
&lt;/h2&gt;

&lt;p&gt;Every shape has an inside (&lt;code&gt;fill&lt;/code&gt;) and an outline (&lt;code&gt;stroke&lt;/code&gt;), set independently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;"50"&lt;/span&gt; &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;"50"&lt;/span&gt; &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;"40"&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"#3366ff"&lt;/span&gt; &lt;span class="na"&gt;stroke=&lt;/span&gt;&lt;span class="s"&gt;"#000"&lt;/span&gt; &lt;span class="na"&gt;stroke-width=&lt;/span&gt;&lt;span class="s"&gt;"2"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unset &lt;code&gt;fill&lt;/code&gt; defaults to black. Unset &lt;code&gt;stroke&lt;/code&gt; defaults to none. If an icon is pure &lt;code&gt;fill&lt;/code&gt; with no stroke at all (most converted icon-font SVGs are), editing &lt;code&gt;stroke-width&lt;/code&gt; is never going to do anything visible, and that's usually the first dead end people hit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cascade is the actual bug source
&lt;/h2&gt;

&lt;p&gt;A color can come from three places, and they don't have equal priority:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A presentation attribute: &lt;code&gt;&amp;lt;path fill="red" /&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;An inline &lt;code&gt;style&lt;/code&gt; attribute: &lt;code&gt;&amp;lt;path style="fill: red;" /&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block or external stylesheet: &lt;code&gt;path { fill: red; }&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Normal CSS specificity applies: &lt;code&gt;style&lt;/code&gt; attribute beats stylesheet, stylesheet beats presentation attribute. Edit the &lt;code&gt;fill="red"&lt;/code&gt; attribute directly, and if a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block elsewhere in the same file also targets that path, your edit is overridden and nothing changes on screen. No error, no warning, it just loses.&lt;/p&gt;

&lt;p&gt;If a color edit isn't sticking, grep the file for &lt;code&gt;&amp;lt;style&lt;/code&gt; before assuming your tool, or your edit, is broken. This one thing accounts for most "the SVG editor is buggy" reports that are actually the cascade working exactly as designed.&lt;/p&gt;

&lt;h2&gt;
  
  
  currentColor: SVG's inheritance trick
&lt;/h2&gt;

&lt;p&gt;Set &lt;code&gt;fill="currentColor"&lt;/code&gt; and the shape stops carrying its own color and instead inherits whatever &lt;code&gt;color&lt;/code&gt; is set to on an ancestor element, the same mechanism that makes text inherit color:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.icon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ff0000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;fill=&lt;/span&gt;&lt;span class="s"&gt;"currentColor"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change &lt;code&gt;.icon&lt;/code&gt;'s &lt;code&gt;color&lt;/code&gt; and the SVG updates with zero edits to the SVG itself. It's why most icon libraries ship &lt;code&gt;currentColor&lt;/code&gt; by default: one file, infinite colors, controlled entirely by CSS at the call site. If you're publishing your own icon set, this is the property to reach for instead of hardcoding hex values into every export.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one that actually surprises people: &lt;code&gt;&amp;lt;img src&amp;gt;&lt;/code&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"icon.svg"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The moment an SVG is referenced this way, the browser treats it as an opaque raster-like image. There's no DOM access, no CSS targeting &lt;code&gt;fill&lt;/code&gt;, and &lt;code&gt;currentColor&lt;/code&gt; resolves to nothing because there's no inheritance path across the document boundary. &lt;code&gt;filter: invert()&lt;/code&gt; gets you a rough approximation at best, not a real color swap.&lt;/p&gt;

&lt;p&gt;Three actual fixes, in order of how much they cost you:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inline the SVG in the HTML.&lt;/strong&gt; Once the markup is literally on the page, it's a normal element and every CSS trick above works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use it as a mask instead of a source:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.icon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;currentColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;mask&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url(icon.svg)&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;contain&lt;/span&gt; &lt;span class="nb"&gt;no-repeat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;-webkit-mask&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url(icon.svg)&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="n"&gt;contain&lt;/span&gt; &lt;span class="nb"&gt;no-repeat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The shape becomes a stencil, the visible color comes from &lt;code&gt;background-color&lt;/code&gt;, and you never touch the SVG file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use a sprite with &lt;code&gt;&amp;lt;use&amp;gt;&lt;/code&gt;&lt;/strong&gt;, setting fill on the &lt;code&gt;&amp;lt;use&amp;gt;&lt;/code&gt; element, assuming the source paths don't already have a hardcoded fill fighting you (see the cascade section above, same rule applies here too).&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-path SVGs need more than one edit
&lt;/h2&gt;

&lt;p&gt;A two-tone logo is rarely one shape. It's usually four or five &lt;code&gt;&amp;lt;path&amp;gt;&lt;/code&gt; elements, each carrying its own &lt;code&gt;fill&lt;/code&gt;. Changing one and expecting the whole icon to shift is the second-most common gotcha after the cascade issue. Either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;put &lt;code&gt;fill="currentColor"&lt;/code&gt; on every path that should track the parent's text color, or&lt;/li&gt;
&lt;li&gt;set &lt;code&gt;fill&lt;/code&gt; on a wrapping &lt;code&gt;&amp;lt;g&amp;gt;&lt;/code&gt;, keeping in mind any child path with its own explicit &lt;code&gt;fill&lt;/code&gt; still wins over the group value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Past two or three paths this gets tedious fast when you're doing it by hand in a text editor, hunting &lt;code&gt;fill=&lt;/code&gt; occurrences one at a time with no visual feedback on which shape you're actually touching.&lt;/p&gt;

&lt;h2&gt;
  
  
  CSS custom properties work fine inside SVG
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    :root { --icon-color: #3366ff; }
    .body-fill { fill: var(--icon-color); }
  &lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;path&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"body-fill"&lt;/span&gt; &lt;span class="na"&gt;d=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One variable, referenced by every shape that shares it. If the SVG is inlined (not loaded via &lt;code&gt;img&lt;/code&gt;), the page's own CSS can override that variable directly, giving a multi-part graphic a single external color hook instead of duplicated hex values scattered across paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing this visually
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://svglab.app" rel="noopener noreferrer"&gt;SVG Lab&lt;/a&gt; is a free browser SVG editor I built: visual fill and stroke editing plus path and node editing (bezier control points, anchor manipulation), no install. It doesn't replace knowing how the cascade works, it just means you click the shape you want to change instead of hunting through markup for it.&lt;/p&gt;

</description>
      <category>svg</category>
      <category>css</category>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>The SVG Path Data Format, Explained (M, L, C, Q, A, Z)</title>
      <dc:creator>Usman Bashir</dc:creator>
      <pubDate>Fri, 07 Aug 2026 17:53:56 +0000</pubDate>
      <link>https://dev.to/usman_basheers/the-svg-path-data-format-explained-m-l-c-q-a-z-52k1</link>
      <guid>https://dev.to/usman_basheers/the-svg-path-data-format-explained-m-l-c-q-a-z-52k1</guid>
      <description>&lt;p&gt;If you've opened a &lt;code&gt;&amp;lt;path d="..."&amp;gt;&lt;/code&gt; string and had no idea what you were looking at, here's the short version: it's a tiny drawing language. A pen moves around a coordinate space, and each letter in the string is an instruction telling it what to do next.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;M&lt;/code&gt;/&lt;code&gt;m&lt;/code&gt; moves the pen, &lt;code&gt;L&lt;/code&gt;/&lt;code&gt;l&lt;/code&gt; draws a straight line, &lt;code&gt;C&lt;/code&gt;/&lt;code&gt;c&lt;/code&gt; and &lt;code&gt;Q&lt;/code&gt;/&lt;code&gt;q&lt;/code&gt; draw bezier curves, &lt;code&gt;A&lt;/code&gt;/&lt;code&gt;a&lt;/code&gt; draws an arc, &lt;code&gt;Z&lt;/code&gt;/&lt;code&gt;z&lt;/code&gt; closes the shape.&lt;/li&gt;
&lt;li&gt;Uppercase is absolute coordinates, lowercase is relative to the pen's current position.&lt;/li&gt;
&lt;li&gt;A visual path editor drags the exact same numbers you'd type by hand, it just shows you the curve instead of making you compute it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Reading a path string
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;path d="M10 10 L90 10 L90 90 Z" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Broken down: move to (10, 10), draw a line to (90, 10), draw a line to (90, 90), close the path back to the start. That's a right triangle. Every path, no matter how complex, is this same pattern: a command letter followed by however many numbers that command needs, repeated.&lt;/p&gt;

&lt;h2&gt;
  
  
  The command set
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;What it takes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;M&lt;/code&gt; / &lt;code&gt;m&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Move to&lt;/td&gt;
&lt;td&gt;x, y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;L&lt;/code&gt; / &lt;code&gt;l&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Line to&lt;/td&gt;
&lt;td&gt;x, y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;C&lt;/code&gt; / &lt;code&gt;c&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Cubic bezier&lt;/td&gt;
&lt;td&gt;control1 x/y, control2 x/y, end x/y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Q&lt;/code&gt; / &lt;code&gt;q&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Quadratic bezier&lt;/td&gt;
&lt;td&gt;control x/y, end x/y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;A&lt;/code&gt; / &lt;code&gt;a&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Arc&lt;/td&gt;
&lt;td&gt;rx, ry, rotation, large-arc-flag, sweep-flag, end x/y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Z&lt;/code&gt; / &lt;code&gt;z&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Close path&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;C&lt;/code&gt; and &lt;code&gt;Q&lt;/code&gt; are both bezier curves, the difference is one control point (&lt;code&gt;Q&lt;/code&gt;) vs two (&lt;code&gt;C&lt;/code&gt;). Two control points give you more independent influence over each end of the curve; one control point gives you a simpler, more symmetric curve. There are also shorthand continuations (&lt;code&gt;S&lt;/code&gt;/&lt;code&gt;s&lt;/code&gt;, &lt;code&gt;T&lt;/code&gt;/&lt;code&gt;t&lt;/code&gt;) for chaining smooth curves without repeating a control point, but the six above are what you'll hit constantly.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;A&lt;/code&gt; is the one people avoid writing by hand. Six parameters, two of which are flags (0 or 1) that determine which of four possible arcs you get for the same radii and endpoints. Flip one and you're not slightly off, you're on the opposite side of the ellipse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Absolute vs relative is the part that bites
&lt;/h2&gt;

&lt;p&gt;Every command above has an uppercase and lowercase form, and it's not cosmetic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- absolute: go to (90, 10) --&amp;gt;
&amp;lt;path d="M0 0 L90 10" /&amp;gt;

&amp;lt;!-- relative: go 90 right, 10 down, from wherever the pen is --&amp;gt;
&amp;lt;path d="M0 0 l90 10" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example both produce the same triangle, because the pen starts at the origin. They stop agreeing the moment you edit one segment in the middle of a longer relative path: every command after your edit is relative to a starting point that just moved, so the rest of the shape shifts with it. This is the single most common cause of "I changed one line and now the whole icon is wrong" when hand-editing SVG.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this gets painful fast
&lt;/h2&gt;

&lt;p&gt;None of the commands above are individually hard. What breaks down is scale. A real icon or illustration is often one unbroken string with dozens of commands, no line breaks, no comments, and bezier control points that don't even sit on the visible curve, they pull it from outside. Reading four numbers and predicting the resulting curve shape in your head is not something most people can do reliably, so editing path data by hand usually turns into: change a number, re-render, squint, change it again.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a visual editor is actually doing
&lt;/h2&gt;

&lt;p&gt;Worth being precise here, since it's easy to assume there's something clever happening under the hood. There isn't. A path editor renders two things you can drag:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Anchor points&lt;/strong&gt;: the actual M/L/curve-endpoint coordinates in the string.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control handles&lt;/strong&gt;: the control points belonging to C and Q commands, shown as draggable lines coming off an anchor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drag an anchor, you change where a segment starts or ends. Drag a handle, you change the curvature of the segment it belongs to, without touching the anchor. Every drag is a rewrite of the same numbers you'd otherwise be typing into the &lt;code&gt;d&lt;/code&gt; string yourself. The editor's only job is closing the loop between "I changed a number" and "here's what that number now looks like," instantly, instead of making you simulate it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotchas worth knowing before you debug one of these
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Z&lt;/code&gt; draws a line back to the start. An unexpected extra segment in a shape is often just &lt;code&gt;Z&lt;/code&gt; doing exactly that.&lt;/li&gt;
&lt;li&gt;Arc flags (&lt;code&gt;large-arc-flag&lt;/code&gt;, &lt;code&gt;sweep-flag&lt;/code&gt;) are booleans, not tuning knobs. Wrong flag, wrong side of the ellipse, not a small deviation.&lt;/li&gt;
&lt;li&gt;Editing inside a relative-coordinate path shifts everything downstream of the edit. If you're scripting path transformations, absolute coordinates are usually safer to reason about.&lt;/li&gt;
&lt;li&gt;Aggressively rounding coordinates to shrink file size can leave visible seams where two paths are supposed to meet exactly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  If you'd rather drag than compute
&lt;/h2&gt;

&lt;p&gt;For small, deliberate edits, working in the raw &lt;code&gt;d&lt;/code&gt; string is fine once you know the command set. For anything with more curves than you can hold in your head, dragging nodes and watching the render update is faster than mentally running the bezier math.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://svglab.app" rel="noopener noreferrer"&gt;SVG Lab&lt;/a&gt; is a free, no-install browser editor with real node and bezier handle editing on top of the raw path data, plus fill and stroke color editing. Not the only way to do this, just one option if you want to see the curve instead of computing it.&lt;/p&gt;

</description>
      <category>svg</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>css</category>
    </item>
    <item>
      <title>How SVG-to-PNG Conversion Actually Works in the Browser</title>
      <dc:creator>Usman Bashir</dc:creator>
      <pubDate>Thu, 06 Aug 2026 07:55:03 +0000</pubDate>
      <link>https://dev.to/usman_basheers/how-svg-to-png-conversion-actually-works-in-the-browser-5307</link>
      <guid>https://dev.to/usman_basheers/how-svg-to-png-conversion-actually-works-in-the-browser-5307</guid>
      <description>&lt;p&gt;If you've ever tried to convert an SVG to a PNG entirely in the browser (no server round-trip, no ImageMagick, just JavaScript), you've probably hit a point where the output looked almost right and you couldn't figure out why it wasn't quite right. The mechanics are simple in outline and full of small traps in practice. This is a walkthrough of how the conversion actually works, and where it usually breaks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The basic pipeline
&lt;/h2&gt;

&lt;p&gt;There's no native "SVG to PNG" API. The browser gives you two lower-level primitives and expects you to combine them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Load the SVG as an &lt;code&gt;Image&lt;/code&gt; object (or draw it via a data URL / object URL).&lt;/li&gt;
&lt;li&gt;Draw that image onto a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element.&lt;/li&gt;
&lt;li&gt;Read the canvas back out as a PNG via &lt;code&gt;canvas.toBlob()&lt;/code&gt; or &lt;code&gt;canvas.toDataURL()&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;svgBlob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Blob&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;svgString&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/svg+xml;charset=utf-8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;svgBlob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;canvas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;targetWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;targetHeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetHeight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBlob&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;blob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// blob is your PNG&lt;/span&gt;
    &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;revokeObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;image/png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole mechanism. Everything else in this post is about the ways this innocent-looking code produces a wrong-looking image.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rasterization: canvas doesn't know about "vector"
&lt;/h2&gt;

&lt;p&gt;The moment &lt;code&gt;drawImage&lt;/code&gt; runs, the SVG stops being a vector graphic. Canvas is a bitmap surface: it rasterizes the image at whatever pixel dimensions the canvas has, right then, and every pixel is committed. There's no going back to redraw it sharper later without re-running the whole pipeline from the source SVG at a new size. This is why "export at 2x" isn't a checkbox on the PNG after the fact; it has to happen before rasterization, by drawing into a larger canvas from the start.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scale factors and devicePixelRatio
&lt;/h2&gt;

&lt;p&gt;This is the single most common cause of blurry exports. A CSS pixel is not a physical pixel. On a standard display, &lt;code&gt;devicePixelRatio&lt;/code&gt; is 1 and they match. On most modern laptop and phone screens, it's 2 or 3, meaning the OS is rendering 2-3 physical pixels for every logical CSS pixel to get a sharp image.&lt;/p&gt;

&lt;p&gt;If you size your canvas using the SVG's logical width and height directly, then display or save that canvas at "actual size," you'll get an export that looks soft on any high-density display, because you only rasterized at 1x worth of detail.&lt;/p&gt;

&lt;p&gt;The fix is to multiply the canvas dimensions by your target scale factor before drawing, then scale the drawing context to match:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;devicePixelRatio&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// or a fixed export scale like 2 or 3&lt;/span&gt;
&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;targetWidth&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;targetHeight&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetHeight&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that &lt;code&gt;devicePixelRatio&lt;/code&gt; reflects the &lt;em&gt;viewing&lt;/em&gt; device, not necessarily the scale you want for an &lt;em&gt;exported&lt;/em&gt; file. For export tooling, it's usually better to expose scale as an explicit user choice (1x / 2x / 3x, or a target pixel width) rather than silently inheriting whatever screen the export happened to run on.&lt;/p&gt;

&lt;h2&gt;
  
  
  viewBox vs width/height: which dimensions actually apply
&lt;/h2&gt;

&lt;p&gt;SVG has two, occasionally conflicting, ideas of "size":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;viewBox&lt;/code&gt; attribute defines the internal coordinate system: the drawing's own units.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;width&lt;/code&gt;/&lt;code&gt;height&lt;/code&gt; attributes (or equivalent CSS) define how large the SVG renders in the document.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When both are present, &lt;code&gt;viewBox&lt;/code&gt; sets the aspect ratio and coordinate space, and &lt;code&gt;width&lt;/code&gt;/&lt;code&gt;height&lt;/code&gt; set the actual rendered size: the SVG scales its contents to fit. When an SVG has a &lt;code&gt;viewBox&lt;/code&gt; but no explicit &lt;code&gt;width&lt;/code&gt;/&lt;code&gt;height&lt;/code&gt;, browsers typically fall back to a default intrinsic size (often 300×150), which is rarely what you want for an export. This is a common source of "why did my PNG come out as 300x150" bug reports: the source SVG had a &lt;code&gt;viewBox&lt;/code&gt; but nothing telling the rasterizer what pixel size to target, so something upstream picked the default.&lt;/p&gt;

&lt;p&gt;For predictable exports, it's worth explicitly setting the target width and height on the canvas yourself, calculated from the &lt;code&gt;viewBox&lt;/code&gt; aspect ratio, rather than trusting the SVG's own &lt;code&gt;width&lt;/code&gt;/&lt;code&gt;height&lt;/code&gt; attributes to be present or correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  External image references don't resolve
&lt;/h2&gt;

&lt;p&gt;An SVG can reference external raster images via &lt;code&gt;&amp;lt;image href="..."&amp;gt;&lt;/code&gt;. This works fine when the SVG renders directly in an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag or inline in the DOM, because the browser fetches the reference normally. It breaks in the canvas pipeline for a specific reason: once you draw an image containing external references onto a canvas, the canvas becomes "tainted" if any of those references were loaded cross-origin without proper CORS headers, and &lt;code&gt;toDataURL()&lt;/code&gt;/&lt;code&gt;toBlob()&lt;/code&gt; will throw a security error rather than silently failing.&lt;/p&gt;

&lt;p&gt;Even when CORS isn't the issue, external references add a dependency on network availability and load timing that a self-contained SVG doesn't have: if the referenced image hasn't finished loading when the SVG is rasterized, you get a blank spot where it should be. The most reliable fix for browser-side conversion is to inline external raster references as base64 data URIs before rasterizing, so the SVG carries everything it needs with no additional fetch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fonts: embedded vs linked
&lt;/h2&gt;

&lt;p&gt;Text in an SVG is the other common failure point. If an SVG uses &lt;code&gt;&amp;lt;text&amp;gt;&lt;/code&gt; with a font that isn't embedded in the SVG itself (just referenced by font-family name, or linked via an external stylesheet/&lt;code&gt;@font-face&lt;/code&gt;), the rasterizer falls back to whatever font is available on the system running the conversion, which may not match what the SVG's author intended, and will definitely not match if the conversion runs somewhere the font isn't installed at all (a different machine, a headless environment, etc.).&lt;/p&gt;

&lt;p&gt;The robust options are converting text to outlined paths before export (so there's no font dependency left at all), or embedding the font data directly in the SVG as a base64 &lt;code&gt;@font-face&lt;/code&gt; inside a &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; block. Linked fonts are the fragile option: they depend on the environment doing the rasterizing having network access and the same font available, which is exactly the kind of thing that works on your machine and breaks the moment the conversion runs somewhere else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it together
&lt;/h2&gt;

&lt;p&gt;None of these problems are exotic. They're all consequences of the same basic fact: canvas rasterization is a one-shot, environment-dependent process, and an SVG can carry dependencies (external images, linked fonts, ambiguous dimensions) that don't survive that process cleanly. Handling them means: pick an explicit scale factor rather than trusting device defaults, resolve the &lt;code&gt;viewBox&lt;/code&gt;/&lt;code&gt;width&lt;/code&gt;/&lt;code&gt;height&lt;/code&gt; ambiguity explicitly rather than trusting fallbacks, inline external image references, and either outline or embed fonts before rasterizing.&lt;/p&gt;

&lt;p&gt;If you'd rather not wire this pipeline up by hand, SVG Lab's &lt;a href="https://svglab.app/svg-to-png" rel="noopener noreferrer"&gt;SVG-to-PNG tool&lt;/a&gt; handles the scale factor, dimension, and font-embedding cases above automatically, worth a look as a working example of the same pipeline described here, running entirely in the browser.&lt;/p&gt;

</description>
      <category>svg</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>css</category>
    </item>
  </channel>
</rss>
