Skip to content

Instantly share code, notes, and snippets.

@PyYoshi
Created November 20, 2025 11:18
Show Gist options
  • Select an option

  • Save PyYoshi/46d34b7d679df72475460a7a060002dd to your computer and use it in GitHub Desktop.

Select an option

Save PyYoshi/46d34b7d679df72475460a7a060002dd to your computer and use it in GitHub Desktop.

svg-to-png

A bash script to convert SVG files to high-resolution PNG images using headless Chrome.

Features

  • High-Fidelity Conversion: Uses Chrome's rendering engine for accurate SVG rendering
  • Automatic viewBox Addition: Automatically adds viewBox attribute if missing
  • Batch Processing: Convert multiple SVG files at once
  • Flexible Scaling: Control output resolution with scale factor
  • Safe Operation: Creates backups before modifying source files
  • Colorful Output: Easy-to-read colored log messages

Why This Tool?

Standard SVG conversion tools like rsvg-convert use librsvg, which has limited SVG feature support. This script uses Chrome's full-featured rendering engine (via Puppeteer) to ensure:

  • Complete SVG 1.1 specification support
  • Accurate rendering of complex filters and effects
  • Proper text rendering with custom fonts
  • GPU-accelerated rendering
  • Same quality as viewing in Chrome/Firefox browsers

Requirements

  • bash (version 4.0+)
  • Node.js (version 12.20.0+)
  • npx (comes with Node.js)
  • sed (GNU sed recommended)

Installation of Dependencies

macOS

# Install Node.js via Homebrew
brew install node

Ubuntu/Debian

# Install Node.js
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs

Other Linux Distributions

# Use nvm (Node Version Manager)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
nvm install --lts

Installation

Option 1: Local Installation

# Download the script
curl -O https://raw.githubusercontent.com/your-repo/svg-to-png/main/svg-to-png.sh
chmod +x svg-to-png.sh

# Run locally
./svg-to-png.sh input.svg

Option 2: Global Installation

# Install to /usr/local/bin
sudo curl -o /usr/local/bin/svg-to-png https://raw.githubusercontent.com/your-repo/svg-to-png/main/svg-to-png.sh
sudo chmod +x /usr/local/bin/svg-to-png

# Run from anywhere
svg-to-png input.svg

Usage

Basic Usage

# Convert with default settings (scale=4)
svg-to-png input.svg

# Specify scale factor
svg-to-png -s 2 input.svg
svg-to-png --scale 3 input.svg

Batch Conversion

# Convert multiple files
svg-to-png *.svg

# Convert with custom scale
svg-to-png -s 3 file1.svg file2.svg file3.svg

Custom Output Directory

# Save to specific directory
svg-to-png -o ./output input.svg

# Batch convert to directory
svg-to-png -s 4 -o ./high-res *.svg

Verbose Mode

# Enable detailed logging
svg-to-png -v -s 2 input.svg
svg-to-png --verbose --scale 3 *.svg

Options

Option Short Description Default
--scale -s Scale factor for output resolution 4
--output -o Output directory path Same as source
--verbose -v Enable verbose logging Disabled
--help -h Display help message -

Scale Factor Guide

Scale Use Case Quality File Size
1-2 Web display Standard Small
2-3 General printing High Medium
3-4 High-quality printing Very High Large
4-6 Posters/Large format Professional Very Large

Resolution Examples

For an SVG with dimensions 1684px × 1300.48px:

  • Scale 1: 1684 × 1300 pixels
  • Scale 2: 3368 × 2601 pixels
  • Scale 3: 5052 × 3901 pixels
  • Scale 4: 6736 × 5202 pixels

Examples

Example 1: Single File Conversion

svg-to-png -s 4 logo.svg

Output:

[INFO] Converting: logo.svg (scale=4)
[INFO] Conversion completed: logo.png

[INFO] Processing completed: 1 succeeded, 0 failed

Example 2: Batch Conversion with Output Directory

svg-to-png -s 3 -o ./png-output *.svg

Output:

[INFO] Converting: image1.svg (scale=3)
[INFO] Conversion completed: ./png-output/image1.png
[INFO] Converting: image2.svg (scale=3)
[INFO] Conversion completed: ./png-output/image2.png

[INFO] Processing completed: 2 succeeded, 0 failed

Example 3: Verbose Mode

svg-to-png -v -s 2 diagram.svg

Output:

[DEBUG] Checking dependencies...
[DEBUG] Dependency check completed
[INFO] Converting: diagram.svg (scale=2)
[DEBUG] Checking viewBox: diagram.svg
[DEBUG] viewBox added
[DEBUG] Executing command: CONVERT_SVG_LAUNCH_OPTIONS=... npx convert-svg-to-png-cli ...
[INFO] Conversion completed: diagram.png

[INFO] Processing completed: 1 succeeded, 0 failed

How It Works

  1. Dependency Check: Verifies that npx and sed are available
  2. viewBox Addition: Automatically adds viewBox attribute to SVG if missing
  3. Chrome Rendering: Uses headless Chrome (via convert-svg-to-png) to render SVG
  4. PNG Export: Captures screenshot at specified scale factor
  5. Output: Saves high-resolution PNG file

SVG Modification

The script modifies SVG files to ensure proper scaling:

Before:

<svg width="1684px" height="1300.48px" xmlns="...">

After:

<svg viewBox="0 0 1684 1300.48" width="1684px" height="1300.48px" xmlns="...">

This allows the --scale option to work correctly while maintaining compatibility.

Troubleshooting

"npx not found"

Solution: Install Node.js

# macOS
brew install node

# Ubuntu/Debian
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt-get install -y nodejs

"Unable to derive width and height from SVG"

Solution: The script automatically adds viewBox to fix this. If the error persists, check your SVG file for syntax errors.

Low Resolution Output

Solution: Increase the scale factor

# Try higher scale values
svg-to-png -s 6 input.svg

Memory Issues with Large Files

Solution: Increase Node.js memory limit

NODE_OPTIONS='--max-old-space-size=4096' svg-to-png -s 4 large-file.svg

Chromium Download Issues

The first run will download Chromium (~170-280MB depending on OS). If download fails:

# Manually install convert-svg-to-png
npm install -g convert-svg-to-png

# Then run the script
svg-to-png input.svg

Technical Background

Why Chrome/Chromium?

Modern web browsers have the most complete SVG implementations:

  • Chromium (Blink): Complete SVG DOM + Skia rendering engine
  • Firefox (Gecko): Complete SVG DOM + Cairo rendering engine
  • librsvg: Limited SVG support + Cairo rendering engine

The rendering quality hierarchy:

Chrome/Firefox > Inkscape > rsvg-convert > Basic SVG viewers

Architecture

SVG Input
    ↓
[svg-to-png script]
    ├─ Add viewBox (sed)
    └─ Calculate dimensions
        ↓
[convert-svg-to-png (Puppeteer)]
    ├─ Launch Headless Chrome
    ├─ Render SVG at scale
    └─ Capture screenshot
        ↓
High-Resolution PNG Output

Advanced Usage

Custom Chromium Flags

The script sets --no-sandbox by default for compatibility. To customize:

# Edit the script and modify this line:
CONVERT_SVG_LAUNCH_OPTIONS='{"args":["--no-sandbox","--disable-gpu"]}'

Integration with Other Tools

# Convert and optimize
svg-to-png -s 4 input.svg && optipng input.png

# Batch convert and rename
for f in *.svg; do
  svg-to-png -s 3 "$f"
  mv "${f%.svg}.png" "${f%.svg}-high-res.png"
done

# Convert specific files from a list
cat files.txt | xargs svg-to-png -s 2 -o ./output

License

MIT License - feel free to use this script in your projects.

Acknowledgments

  • convert-svg-to-png - The underlying conversion tool
  • Puppeteer - Headless Chrome automation
  • Chromium/Blink team - For the excellent SVG rendering engine

Related Projects

Support

If you encounter any issues or have questions, please:

  1. Check the Troubleshooting section
  2. Review convert-svg-to-png documentation
  3. Open an issue on GitHub
#!/bin/bash
set -euo pipefail
# Color definitions
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Default values
SCALE=4
OUTPUT_DIR=""
VERBOSE=false
# Display usage
usage() {
cat << EOF
Usage: $(basename "$0") [options] <SVG files...>
Convert SVG files to high-resolution PNG images.
Automatically adds viewBox and converts using convert-svg-to-png.
Options:
-s, --scale <number> Scale factor (default: 4)
-o, --output <dir> Output directory (default: same as source)
-v, --verbose Enable verbose output
-h, --help Display this help message
Examples:
$(basename "$0") input.svg
$(basename "$0") -s 2 input.svg
$(basename "$0") -s 3 -o ./output *.svg
$(basename "$0") --scale 6 --output ./high-res input1.svg input2.svg
EOF
exit 0
}
# Log output
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1" >&2
}
log_verbose() {
if [ "$VERBOSE" = true ]; then
echo -e "[DEBUG] $1"
fi
}
# Check dependencies
check_dependencies() {
log_verbose "Checking dependencies..."
if ! command -v npx &> /dev/null; then
log_error "npx not found. Please install Node.js."
exit 1
fi
if ! command -v sed &> /dev/null; then
log_error "sed not found."
exit 1
fi
log_verbose "Dependency check completed"
}
# Add viewBox to SVG
add_viewbox() {
local svg_file="$1"
local backup_file="${svg_file}.bak"
log_verbose "Checking viewBox: $svg_file"
# Create backup
cp "$svg_file" "$backup_file"
# Skip if viewBox already exists
if grep -q 'viewBox=' "$svg_file"; then
log_verbose "viewBox already exists"
rm "$backup_file"
return 0
fi
# Add viewBox
if sed -i -E 's/width="([0-9.]+)px" height="([0-9.]+)px"/viewBox="0 0 \1 \2" width="\1px" height="\2px"/g' "$svg_file"; then
log_verbose "viewBox added"
rm "$backup_file"
return 0
else
log_error "Failed to add viewBox"
mv "$backup_file" "$svg_file"
return 1
fi
}
# Convert SVG to PNG
convert_svg() {
local svg_file="$1"
local output_path="$2"
log_info "Converting: $svg_file (scale=$SCALE)"
# Add viewBox
if ! add_viewbox "$svg_file"; then
log_error "Failed to add viewBox: $svg_file"
return 1
fi
# Set environment variables and execute conversion
local cmd=(npx convert-svg-to-png-cli "$svg_file" --scale "$SCALE")
# Add output path if specified
if [ -n "$output_path" ]; then
cmd+=(--output "$output_path")
fi
log_verbose "Executing command: CONVERT_SVG_LAUNCH_OPTIONS=... ${cmd[*]}"
if CONVERT_SVG_LAUNCH_OPTIONS='{"args":["--no-sandbox"]}' "${cmd[@]}"; then
log_info "Conversion completed: ${output_path:-${svg_file%.svg}.png}"
return 0
else
log_error "Conversion failed: $svg_file"
return 1
fi
}
# Main process
main() {
local svg_files=()
# Show usage if no arguments
if [ $# -eq 0 ]; then
usage
fi
# Parse options
while [ $# -gt 0 ]; do
case "$1" in
-h|--help)
usage
;;
-s|--scale)
SCALE="$2"
shift 2
;;
-o|--output)
OUTPUT_DIR="$2"
shift 2
;;
-v|--verbose)
VERBOSE=true
shift
;;
-*)
log_error "Unknown option: $1"
usage
;;
*)
svg_files+=("$1")
shift
;;
esac
done
# Check if SVG files are specified
if [ ${#svg_files[@]} -eq 0 ]; then
log_error "Please specify SVG files"
usage
fi
# Validate scale value
if ! [[ "$SCALE" =~ ^[0-9]+(\.[0-9]+)?$ ]] || (( $(echo "$SCALE <= 0" | bc -l) )); then
log_error "Scale must be a positive number: $SCALE"
exit 1
fi
# Create output directory
if [ -n "$OUTPUT_DIR" ]; then
mkdir -p "$OUTPUT_DIR"
OUTPUT_DIR=$(realpath "$OUTPUT_DIR")
log_verbose "Output directory: $OUTPUT_DIR"
fi
# Check dependencies
check_dependencies
# Process each file
local success_count=0
local fail_count=0
for svg_file in "${svg_files[@]}"; do
if [ ! -f "$svg_file" ]; then
log_warn "File not found: $svg_file"
((fail_count++))
continue
fi
# Determine output path
local output_path=""
if [ -n "$OUTPUT_DIR" ]; then
local basename=$(basename "$svg_file" .svg)
output_path="$OUTPUT_DIR/${basename}.png"
fi
# Execute conversion
if convert_svg "$svg_file" "$output_path"; then
((success_count++))
else
((fail_count++))
fi
done
# Result summary
echo ""
log_info "Processing completed: $success_count succeeded, $fail_count failed"
if [ $fail_count -gt 0 ]; then
exit 1
fi
}
# Execute script
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment