svg-bbox
Version:
A set of tools to compute and to use a SVG bounding box you can trust (as opposed to the unreliable .getBBox() scourge)
1,486 lines (1,099 loc) • 57 kB
Markdown
<div align="center">
<img src="assets/SvgVisualBBox_logo_portrait.svg" alt="SvgVisualBBox" width="200" />
<h3>A JavaScript library for accurate SVG bounding box computation</h3>
<p><em>Finally, bounding boxes you can trust.</em></p>
</div>
<p align="center">
<a href="https://www.npmjs.com/package/svg-bbox"><img alt="npm version" src="https://img.shields.io/npm/v/svg-bbox?style=for-the-badge&logo=npm&logoColor=white&color=CB3837"></a>
<a href="https://www.npmjs.com/package/svg-bbox"><img alt="npm downloads" src="https://img.shields.io/npm/dm/svg-bbox?style=for-the-badge&logo=npm&logoColor=white&color=CB3837"></a>
<a href="https://bundlephobia.com/package/svg-bbox"><img alt="bundle size" src="https://img.shields.io/bundlephobia/minzip/svg-bbox?style=for-the-badge&logo=webpack&logoColor=white&label=minified"></a>
</p>
<p align="center">
<a href="https://github.com/Emasoft/SVG-BBOX/actions"><img alt="CI Status" src="https://img.shields.io/github/actions/workflow/status/Emasoft/SVG-BBOX/ci.yml?branch=main&style=for-the-badge&logo=github&logoColor=white&label=CI"></a>
<a href="./LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-yellow?style=for-the-badge&logo=opensourceinitiative&logoColor=white"></a>
<img alt="Node.js" src="https://img.shields.io/badge/node-%3E%3D18-brightgreen?style=for-the-badge&logo=nodedotjs&logoColor=white">
<img alt="TypeScript Ready" src="https://img.shields.io/badge/TypeScript-Ready-blue?style=for-the-badge&logo=typescript&logoColor=white">
</p>
<p align="center">
<a href="https://unpkg.com/svg-bbox@latest/SvgVisualBBox.min.js"><img alt="unpkg CDN" src="https://img.shields.io/badge/unpkg-CDN-blue?style=flat-square&logo=unpkg"></a>
<a href="https://cdn.jsdelivr.net/npm/svg-bbox@latest/SvgVisualBBox.min.js"><img alt="jsDelivr CDN" src="https://img.shields.io/badge/jsDelivr-CDN-orange?style=flat-square&logo=jsdelivr"></a>
<a href="https://github.com/Emasoft/SVG-BBOX"><img alt="GitHub" src="https://img.shields.io/badge/GitHub-Repository-black?style=flat-square&logo=github"></a>
</p>
---
## 📚 Table of Contents
- [The Problem with .getBBox()](#the-problem-with-getbbox)
- [Visual Comparison: Oval Badge with Dashed Stroke](#visual-comparison-oval-badge-with-dashed-stroke)
- [Installation](#-installation)
- [Platform Compatibility](#platform-compatibility)
- [What This Package Provides](#what-this-package-provides)
- [1. Core Library: SvgVisualBBox.js](#1-core-library-svgvisualbboxjs)
- [2. CLI Tools](#2-cli-tools)
- [Quickstart](#-quickstart)
- [Browser (CDN)](#browser-cdn)
- [Node.js / npm](#nodejs--npm)
- [More Usage Examples](#-more-usage-examples)
- [Library API Reference](#-library-api-reference)
- [Tools CLI Commands Usage](#tools-cli-commands-usage)
- [Renaming Workflow with the HTML Viewer](#-renaming-workflow-with-the-html-viewer)
- [Troubleshooting](#-troubleshooting)
- [Contributing](#-contributing)
- [License](#-license)
---
## The Problem with `.getBBox()`
The native SVG `.getBBox()` method is fundamentally broken:
| Feature | `.getBBox()` | **SvgVisualBBox** |
| --------------------------------------- | ------------ | ----------------------------- |
| Filters (blur, shadows, glows) | :x: Ignored | :white_check_mark: Measured |
| Stroke width | :x: Ignored | :white_check_mark: Included |
| Complex text (ligatures, RTL, textPath) | :x: Wrong | :white_check_mark: Accurate |
| `<use>`, masks, clipping paths | :x: Fails | :white_check_mark: Works |
| Transformed elements | :x: Garbage | :white_check_mark: Correct |
| Cross-browser consistency | :x: Varies | :white_check_mark: Consistent |
**Our approach:** Measure what the browser actually paints, pixel by pixel. No
geometry guesswork, no lies.
### Visual Comparison: Oval Badge with Dashed Stroke
Here's what happens when extracting an SVG element using three different bbox
methods:
| <img src="assets/inkscape-logo.svg" width="120" alt="Inkscape"> | <img src="assets/chrome-logo.svg" width="120" alt="Chrome"> | <img src="assets/SvgVisualBBox_logo_no_text_portrait.svg" width="120" alt="SvgVisualBBox"> |
| :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| **Inkscape BBox** | **Chrome `.getBBox()`** | **SvgVisualBBox** |
| <img src="assets/oval_badge_inkscape.png#gh-light-mode-only" height="100" alt="Inkscape Result"><img src="assets/oval_badge_inkscape-dark.png#gh-dark-mode-only" height="100" alt="Inkscape Result"> | <img src="assets/oval_badge_getbbox.png#gh-light-mode-only" height="100" alt="getBBox Result"><img src="assets/oval_badge_getbbox-dark.png#gh-dark-mode-only" height="100" alt="getBBox Result"> | <img src="assets/oval_badge_svgvisualbbox.png#gh-light-mode-only" height="100" alt="SvgVisualBBox Result"><img src="assets/oval_badge_svgvisualbbox-dark.png#gh-dark-mode-only" height="100" alt="SvgVisualBBox Result"> |
| ❌ **WRONG** | ❌ **WRONG** | ✅ **CORRECT** |
| [_(svg file here)_](assets/oval_badge_inkscape.svg) | [_(svg file here)_](assets/oval_badge_getbbox.svg) | [_(svg file here)_](assets/oval_badge_svgvisualbbox.svg) |
| Width: 554px<br/>Height: 379px<br/>_Undersized by ~48%_ | Width: 999px<br/>Height: 301px<br/>_Missing ~78px of stroke_ | Width: 1077px<br/>Height: 379px<br/>_Includes full visual bounds_ |
**Source:** [test_oval_badge.svg](assets/test_oval_badge.svg)
**Generate this comparison yourself:**
[examples/bbox-comparison.js](examples/bbox-comparison.js) - Run
`node examples/bbox-comparison.js assets/test_oval_badge.svg oval_badge` to
create your own comparison with timestamped output directory.
**Why the differences?**
- **Inkscape:** Truncates half the image!
- **`.getBBox()`:** Ignores stroke width - bbox is wrong!
- **SvgVisualBBox:** Perfect!
---
## 📦 Installation
### Quick Install (npx - No Installation Required!)
You can run any svg-bbox tool directly without installing:
```bash
# See all available commands
npx svg-bbox
# Run specific tools
npx sbb-getbbox myfile.svg
npx sbb-render myfile.svg output.png
npx sbb-extract myfile.svg --list
```
### Global Install (Recommended for Frequent Use)
```bash
# npm
npm install -g svg-bbox
# pnpm
pnpm add -g svg-bbox
# yarn
yarn global add svg-bbox
# After global install, run commands directly:
svg-bbox # Show all available commands
sbb-getbbox file.svg # Compute bounding box
sbb-render file.svg output.png
```
### Local Install (For Projects)
```bash
# npm
npm install svg-bbox
# pnpm
pnpm add svg-bbox
# yarn
yarn add svg-bbox
# Then use via npx or package.json scripts:
npx sbb-getbbox file.svg
```
### Via CDN (Browser - No Build Tools Required!)
For direct browser usage, use the minified UMD build from a CDN:
```html
<!-- Via unpkg (Recommended) -->
<script src="https://unpkg.com/svg-bbox@latest/SvgVisualBBox.min.js"></script>
<!-- Via jsdelivr -->
<script src="https://cdn.jsdelivr.net/npm/svg-bbox@latest/SvgVisualBBox.min.js"></script>
<!-- Then use the global SvgVisualBBox object -->
<script>
(async () => {
// Wait for fonts to load
await SvgVisualBBox.waitForDocumentFonts(document, 5000);
// Get bbox for an SVG element
const bbox =
await SvgVisualBBox.getSvgElementVisualBBoxTwoPassAggressive('my-svg-id');
console.log('BBox:', bbox);
})();
</script>
```
**File sizes:**
- Original: ~90 KB
- Minified (CDN): ~25 KB _(72% reduction)_
### Clone from GitHub
```bash
git clone https://github.com/Emasoft/SVG-BBOX.git
cd SVG-BBOX
pnpm install
# Run tools directly from source
node sbb-getbbox.cjs myfile.svg
```
> **Note:** In the documentation below, you'll see two command styles:
>
> - `npx sbb-getbbox` - Use this when installed via npm (recommended)
> - `node sbb-getbbox.cjs` - Use this when running from cloned source
>
> Both are equivalent. The npx style works after `npm install svg-bbox`.
After installation, the following CLI commands are available:
**Main Entry Point:**
- `svg-bbox` - **Start here!** Shows help and lists all available commands
**Core Tools (Recommended):**
- `sbb-getbbox` - Compute visual bounding boxes
- `sbb-chrome-getbbox` - Get bbox using Chrome's native .getBBox() (for
comparison)
- `sbb-chrome-extract` - Extract using Chrome's native .getBBox() (for
comparison)
- `sbb-extract` - List, extract, and export SVG objects
- `sbb-fix-viewbox` - Fix missing viewBox/dimensions
- `sbb-render` - Render SVG to PNG
- `sbb-comparer` - Compare two SVGs visually (pixel-by-pixel)
- `sbb-test` - Test library functions
**Inkscape Integration Tools** ⚠️ _(For comparison only - see warnings below)_:
- `sbb-inkscape-text2path` - Convert text to paths using Inkscape
- `sbb-inkscape-extract` - Extract objects by ID using Inkscape
- `sbb-inkscape-svg2png` - SVG to PNG export using Inkscape
> **⚠️ Accuracy Warning:** Inkscape tools have known issues with font bounding
> boxes. Use core tools for production. Inkscape tools are for comparison
> purposes only.
### From Source
```bash
git clone https://github.com/Emasoft/SVG-BBOX.git
cd svg-bbox
pnpm install
```
### Requirements
> **IMPORTANT**: You need **Node.js ≥ 18** and **Chrome or Chromium** installed.
>
> **⚠️ ONLY Chrome/Chromium are supported** — other browsers have poor SVG
> support. This library uses headless Chrome via Puppeteer for measurements, and
> visual verification must use the same browser engine to match results.
After installing, Puppeteer will automatically download a compatible Chromium
browser. Alternatively, you can use your system Chrome by setting the
`PUPPETEER_EXECUTABLE_PATH` environment variable.
---
## Platform Compatibility
✅ **Fully cross-platform compatible:**
- **Windows** 10/11 - All CLI tools work natively (PowerShell, CMD, Git Bash)
- **macOS** - All versions supported (Intel and Apple Silicon)
- **Linux** - All major distributions (Ubuntu, Debian, Fedora, etc.)
**Key features:**
- All file paths use Node.js `path` module (no hardcoded `/` or `\` separators)
- Platform-specific commands handled automatically (Chrome detection, file
opening)
- Works with file paths containing spaces on all platforms
- Pure Node.js CLI tools (no bash scripts required)
**Platform-specific notes:**
<details>
<summary><strong>Windows</strong></summary>
- Chrome/Chromium auto-detection works with default install locations
- File paths with spaces are properly handled
- Use PowerShell or CMD (no WSL required)
- Git Bash also supported
```powershell
# PowerShell example
sbb-getbbox "C:\My Files\drawing.svg"
```
</details>
<details>
<summary><strong>macOS</strong></summary>
- Detects Chrome in `/Applications/`
- Uses native `open` command for file viewing
- Works on both Intel and Apple Silicon Macs
```bash
# macOS example
chmod +x node_modules/.bin/sbb-* # Make executable (first time only)
sbb-getbbox ~/Documents/drawing.svg
```
</details>
<details>
<summary><strong>Linux</strong></summary>
- Auto-detects `google-chrome`, `chromium`, `chromium-browser`
- All standard Linux file paths supported
```bash
# Linux example
chmod +x node_modules/.bin/sbb-* # Make executable (first time only)
sbb-getbbox /home/user/drawings/test.svg
```
</details>
---
## What This Package Provides
### 1. Core Library: `SvgVisualBBox.js`
JavaScript library for accurate visual bounding box computation. Works in
browsers and Node.js (via Puppeteer).
**Available Functions:**
- `getSvgElementVisualBBoxTwoPassAggressive(target, options)` - Compute accurate
visual bbox for any element
- `getSvgElementsUnionVisualBBox(targets[], options)` - Union bbox for multiple
elements
- `getSvgElementVisibleAndFullBBoxes(target, options)` - Get both clipped
(viewBox-respecting) and unclipped bounds
- `showTrueBBoxBorder(target, options)` - Visual debugging overlay
- `waitForDocumentFonts(document, timeoutMs)` - Wait for fonts before measuring
**Capabilities:**
- Font-aware: Arabic, CJK, ligatures, RTL, textPath, custom fonts
- Filter-safe: Blur, shadows, masks, clipping
- Stroke-aware: Width, caps, joins, markers, patterns
### 2. CLI Tools
| Tool | <div align="center">Source</div> | Description | Example Usage |
| ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- | -------------------------------------------------------- |
| **Core Tools (Our Visual BBox Algorithm)** | | | |
| `sbb-getbbox` | <div align="center">[<ins>source</ins>](https://github.com/Emasoft/SVG-BBOX/blob/main/sbb-getbbox.cjs)</div> | Get bbox info using our pixel-accurate visual algorithm | `sbb-getbbox drawing.svg` |
| `sbb-extract` | <div align="center">[<ins>source</ins>](https://github.com/Emasoft/SVG-BBOX/blob/main/sbb-extract.cjs)</div> | List/rename/extract/export SVG objects with visual catalog | `sbb-extract sprites.svg --list` |
| `sbb-render` | <div align="center">[<ins>source</ins>](https://github.com/Emasoft/SVG-BBOX/blob/main/sbb-render.cjs)</div> | Render SVG to PNG with accurate bbox | `sbb-render input.svg output.png` |
| `sbb-fix-viewbox` | <div align="center">[<ins>source</ins>](https://github.com/Emasoft/SVG-BBOX/blob/main/sbb-fix-viewbox.cjs)</div> | Repair missing/broken viewBox using visual bbox | `sbb-fix-viewbox broken.svg fixed.svg` |
| `sbb-comparer` | <div align="center">[<ins>source</ins>](https://github.com/Emasoft/SVG-BBOX/blob/main/sbb-comparer.cjs)</div> | Visual diff between SVGs (pixel comparison) | `sbb-comparer a.svg b.svg diff.png` |
| `sbb-test` | <div align="center">[<ins>source</ins>](https://github.com/Emasoft/SVG-BBOX/blob/main/sbb-test.cjs)</div> | Test bbox accuracy across methods | `sbb-test sample.svg` |
| **Chrome Comparison Tools (Chrome's .getBBox())** | | | |
| `sbb-chrome-getbbox` | <div align="center">[<ins>source</ins>](https://github.com/Emasoft/SVG-BBOX/blob/main/sbb-chrome-getbbox.cjs)</div> | Get bbox info using Chrome's .getBBox() | `sbb-chrome-getbbox drawing.svg` |
| `sbb-chrome-extract` | <div align="center">[<ins>source</ins>](https://github.com/Emasoft/SVG-BBOX/blob/main/sbb-chrome-extract.cjs)</div> | Extract using Chrome's .getBBox() | `sbb-chrome-extract file.svg --id obj1 --output out.svg` |
| **Inkscape Comparison Tools (Inkscape CLI)** | | | |
| `sbb-inkscape-getbbox` | <div align="center">[<ins>source</ins>](https://github.com/Emasoft/SVG-BBOX/blob/main/sbb-inkscape-getbbox.cjs)</div> | Get bbox info using Inkscape's query commands | `sbb-inkscape-getbbox drawing.svg` |
| `sbb-inkscape-extract` | <div align="center">[<ins>source</ins>](https://github.com/Emasoft/SVG-BBOX/blob/main/sbb-inkscape-extract.cjs)</div> | Extract by ID using Inkscape | `sbb-inkscape-extract file.svg --id obj1` |
| `sbb-inkscape-text2path` | <div align="center">[<ins>source</ins>](https://github.com/Emasoft/SVG-BBOX/blob/main/sbb-inkscape-text2path.cjs)</div> | Convert text to paths using Inkscape | `sbb-inkscape-text2path input.svg output.svg` |
| `sbb-inkscape-svg2png` | <div align="center">[<ins>source</ins>](https://github.com/Emasoft/SVG-BBOX/blob/main/sbb-inkscape-svg2png.cjs)</div> | SVG to PNG export using Inkscape | `sbb-inkscape-svg2png input.svg output.png` |
**Naming Convention:**
- `sbb-[function]` = Our reliable visual bbox algorithm
- `sbb-chrome-[function]` = Chrome's .getBBox() method (for comparison)
- `sbb-inkscape-[function]` = Inkscape tools (for comparison)
Run `npx svg-bbox` or any tool with `--help` for detailed usage.
---
## 🚀 Quickstart
### 1. See All Available Commands
```bash
npx svg-bbox
```
This displays help with all available tools and usage examples.
---
### 2. Render an SVG to PNG at the correct size
```bash
npx sbb-render input.svg output.png --mode full --scale 4
```
- Detects the **full drawing extents**.
- Sets an appropriate `viewBox`.
- Renders to PNG at 4 px per SVG unit.
---
### 3. Fix an SVG that has no `viewBox` / `width` / `height`
```bash
npx sbb-fix-viewbox broken.svg fixed/broken.fixed.svg
```
- Computes the **full visual drawing box**.
- Writes a new SVG with:
- `viewBox="x y width height"`
- Consistent `width` / `height`.
---
### 4. List all objects visually & generate a rename JSON
```bash
npx sbb-extract sprites.svg --list --assign-ids --out-fixed sprites.ids.svg
```
This produces:
- `sprites.objects.html` — a visual catalog.
- `sprites.ids.svg` — a version where all objects have IDs like
`auto_id_path_3`.
Open `sprites.objects.html` in a browser to see previews and define new ID
names.
---
### 5. Extract one object as its own SVG
```bash
npx sbb-extract sprites.renamed.svg \
--extract icon_save icon_save.svg \
--margin 5
```
This creates `icon_save.svg` sized exactly to the **visual bounds** of
`#icon_save` (with 5 units of padding).
---
### 6. Export all objects as individual SVGs
```bash
npx sbb-extract sprites.renamed.svg \
--export-all exported \
--export-groups \
--margin 2
```
Each object / group becomes its own SVG, with:
- Correct viewBox
- Includes `<defs>` for filters, patterns, markers
- Ancestor transforms preserved
---
### 7. Library: `SvgVisualBBox.js`
#### Installation
```html
<!-- CDN -->
<script src="https://unpkg.com/svg-bbox@latest/SvgVisualBBox.js"></script>
<!-- Or via npm -->
<script src="./node_modules/svg-bbox/SvgVisualBBox.js"></script>
```
This library can be used in two ways:
1. **Node.js/CLI Tools** - Injected by Puppeteer in headless Chrome (used by all
CLI tools)
2. **Browser/Web Applications** - Loaded directly in webpages via `<script>` tag
or npm import
### 🌐 Browser (embed via CDN mirrors)
You can use `SvgVisualBBox.js` directly in webpages for accurate bounding box
computation and visual debugging.
```html
<script src="https://unpkg.com/svg-bbox@latest/SvgVisualBBox.min.js"></script>
<script>
(async () => {
// Get accurate bounding box for any SVG element
const bbox =
await SvgVisualBBox.getSvgElementVisualBBoxTwoPassAggressive(
'#myElement'
);
console.log(bbox); // {x: 10, y: 20, width: 100, height: 50}
// Visual debugging - show border around true bounds
const result = await SvgVisualBBox.showTrueBBoxBorder('#myElement');
setTimeout(() => result.remove(), 3000);
})();
</script>
```
#### Advanced Example with the showTrueBBoxBorder() function
```html
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/svg-bbox@latest/SvgVisualBBox.js"></script>
</head>
<body>
<svg viewBox="0 0 200 100" width="400">
<text id="greeting" x="100" y="50" text-anchor="middle" font-size="24">
Hello SVG!
</text>
</svg>
<script>
(async () => {
// Wait for fonts
await SvgVisualBBox.waitForDocumentFonts();
// Get accurate bounding box
const bbox =
await SvgVisualBBox.getSvgElementVisualBBoxTwoPassAggressive(
'#greeting'
);
console.log('BBox:', bbox); // {x, y, width, height}
// Show visual border for debugging
const result = await SvgVisualBBox.showTrueBBoxBorder('#greeting');
// Reframe viewBox to focus on element
await SvgVisualBBox.setViewBoxOnObjects('svg', 'greeting', {
aspect: 'stretch',
margin: '10px'
});
// Remove border after 3 seconds
setTimeout(() => result.remove(), 3000);
})();
</script>
</body>
</html>
```
### 8 Node.js (install via npm)
```bash
npm install svg-bbox
```
```javascript
// Use with Puppeteer for server-side SVG processing
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(`<html><body>${svgContent}</body></html>`);
await page.addScriptTag({ path: 'node_modules/svg-bbox/SvgVisualBBox.js' });
const bbox = await page.evaluate(async () => {
return await SvgVisualBBox.getSvgElementVisualBBoxTwoPassAggressive('svg');
});
```
### Functions of the SvgVisualBBox library
The library exposes all functions through the `SvgVisualBBox` namespace.
#### `waitForDocumentFonts(document, timeoutMs)`
Waits for fonts to be ready (or a timeout) before measuring text.
```js
await SvgVisualBBox.waitForDocumentFonts(document, 8000);
```
#### `getSvgElementVisualBBoxTwoPassAggressive(element, options)`
Compute a **visual** bounding box for an element (including stroke, filters,
etc.):
```js
const bbox = await SvgVisualBBox.getSvgElementVisualBBoxTwoPassAggressive(
element,
{
mode: 'unclipped', // ignore viewBox clipping when measuring
coarseFactor: 3, // coarse sampling
fineFactor: 24, // fine sampling
useLayoutScale: true // scale based on layout size
}
);
// bbox: { x, y, width, height } in SVG user units
```
#### `getSvgElementVisibleAndFullBBoxes(svgElement, options)`
Compute both:
- **visible** – what’s inside the current viewBox.
- **full** – the entire drawing, ignoring viewBox clipping.
Used by the fixer and renderer to choose between "full drawing" and "visible
area inside the viewBox".
#### `showTrueBBoxBorder(target, options)` ⭐ NEW
**Visual debug helper** - Displays a dotted border overlay around any SVG
element's true visual bounding box.
```js
// Show border with auto-detected theme
const result = await SvgVisualBBox.showTrueBBoxBorder('#myText');
// Force dark theme for light backgrounds
const result = await SvgVisualBBox.showTrueBBoxBorder('#myPath', {
theme: 'dark'
});
// Custom styling
const result = await SvgVisualBBox.showTrueBBoxBorder('#myElement', {
borderColor: 'red',
borderWidth: '3px',
padding: 10
});
// Remove border
result.remove();
```
**Features of showTrueBBoxBorder():**
- ✅ Auto-detects system dark/light theme
- ✅ Force theme with `theme: 'light'` or `'dark'` option
- ✅ Works with all SVG types (inline, `<object>`, `<iframe>`, sprites, dynamic)
- ✅ Non-intrusive overlay (doesn't modify SVG)
- ✅ Follows SVG on scroll/resize
- ✅ Easy cleanup with `remove()`
---
## 🔧 More Usage Examples
### In HTML Page
```html
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/svg-bbox@latest/SvgVisualBBox.min.js"></script>
</head>
<body>
<svg id="mySvg" viewBox="0 0 200 100">
<rect id="myRect" x="10" y="10" width="50" height="30" fill="blue" />
</svg>
<script>
(async () => {
// Get bounding box
const bbox =
await SvgVisualBBox.getSvgElementVisualBBoxTwoPassAggressive(
'#myRect'
);
console.log('BBox:', bbox);
// Show debug border
const border = await SvgVisualBBox.showTrueBBoxBorder('#myRect');
setTimeout(() => border.remove(), 3000); // Remove after 3s
})();
</script>
</body>
</html>
```
### In JavaScript/Node.js
```javascript
// Install: npm install svg-bbox puppeteer
const puppeteer = require('puppeteer');
const fs = require('fs');
const path = require('path');
async function getBBoxFromSVGFile(svgPath) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Load SVG file
const svgContent = fs.readFileSync(svgPath, 'utf-8');
await page.setContent(`
<!DOCTYPE html>
<html><body>${svgContent}</body></html>
`);
// Inject SvgVisualBBox library
const libPath = path.join(
__dirname,
'node_modules/svg-bbox/SvgVisualBBox.js'
);
await page.addScriptTag({ path: libPath });
// Get bounding box
const bbox = await page.evaluate(async () => {
const svg = document.querySelector('svg');
return await SvgVisualBBox.getSvgElementVisualBBoxTwoPassAggressive(svg);
});
await browser.close();
return bbox;
}
// Usage
getBBoxFromSVGFile('input.svg').then((bbox) => {
console.log('BBox:', bbox);
});
```
### In TypeScript
```typescript
// Install: npm install svg-bbox puppeteer @types/puppeteer
import puppeteer from 'puppeteer';
import { readFileSync } from 'fs';
import { join } from 'path';
interface BBox {
x: number;
y: number;
width: number;
height: number;
}
async function getBBoxFromSVGFile(svgPath: string): Promise<BBox | null> {
const browser = await puppeteer.launch();
const page = await browser.newPage();
const svgContent = readFileSync(svgPath, 'utf-8');
await page.setContent(`
<!DOCTYPE html>
<html><body>${svgContent}</body></html>
`);
const libPath = join(__dirname, 'node_modules/svg-bbox/SvgVisualBBox.js');
await page.addScriptTag({ path: libPath });
const bbox = await page.evaluate(async (): Promise<BBox | null> => {
const svg = document.querySelector('svg');
if (!svg) return null;
return await (
window as any
).SvgVisualBBox.getSvgElementVisualBBoxTwoPassAggressive(svg);
});
await browser.close();
return bbox;
}
// Usage
getBBoxFromSVGFile('input.svg').then((bbox) => {
console.log('BBox:', bbox);
});
```
### In Backend (Node.js File Processing)
```javascript
// Install: npm install svg-bbox
const { execFileSync } = require('child_process');
const path = require('path');
// Get path to CLI tool
const sbbGetBBox = path.join(__dirname, 'node_modules/.bin/sbb-getbbox');
const sbbRender = path.join(__dirname, 'node_modules/.bin/sbb-render');
const sbbFixer = path.join(__dirname, 'node_modules/.bin/sbb-fix-viewbox');
// Compute bounding box
function getBBox(svgFile) {
const output = execFileSync(sbbGetBBox, [svgFile, '--json', 'bbox.json']);
const result = JSON.parse(require('fs').readFileSync('bbox.json', 'utf-8'));
return result[svgFile]['WHOLE CONTENT'];
}
// Fix viewBox
function fixViewBox(inputSvg, outputSvg) {
execFileSync(sbbFixer, [inputSvg, outputSvg]);
}
// Render to PNG
function renderToPNG(svgFile, pngFile, width = 800) {
execFileSync(sbbRender, [
svgFile,
pngFile,
'--width',
width.toString(),
'--background',
'transparent'
]);
}
// Usage
const bbox = getBBox('input.svg');
console.log('BBox:', bbox);
fixViewBox('broken.svg', 'fixed.svg');
renderToPNG('input.svg', 'output.png', 1200);
```
### Using CLI Tools Programmatically
```javascript
// All CLI tools can be used programmatically via child_process
const { execFile } = require('child_process');
const { promisify } = require('util');
const execFilePromise = promisify(execFile);
// Example: Extract object
async function extractObject(inputSvg, objectId, outputSvg) {
const { stdout, stderr } = await execFilePromise('sbb-extract', [
inputSvg,
'--extract',
objectId,
outputSvg,
'--margin',
'10'
]);
return { stdout, stderr };
}
// Example: Compare SVGs
async function compareSVGs(svg1, svg2) {
const { stdout } = await execFilePromise('sbb-comparer', [
svg1,
svg2,
'--json'
]);
return JSON.parse(stdout);
}
// Usage
extractObject('sprites.svg', 'icon_home', 'home.svg').then(() =>
console.log('Extracted!')
);
compareSVGs('v1.svg', 'v2.svg').then((result) =>
console.log('Difference:', result.diffPercentage + '%')
);
```
---
## 📖 Library API Reference
### `getSvgElementVisualBBoxTwoPassAggressive(target, options)`
Compute accurate visual bounding box for any SVG element.
**Parameters:**
- `target` - CSS selector, ID string, or DOM element
- `options.mode` - `'clipped'` (respect viewBox) or `'unclipped'` (full drawing)
- `options.coarseFactor` - Coarse sampling resolution (default: 3)
- `options.fineFactor` - Fine sampling resolution (default: 24)
**Returns:** `{x, y, width, height}` in SVG user units
### `getSvgElementsUnionVisualBBox(targets[], options)`
Compute union bounding box of multiple elements.
**Parameters:**
- `targets[]` - Array of CSS selectors, ID strings, or DOM elements
- `options` - Same as above
**Returns:** `{x, y, width, height}`
### `getSvgElementVisibleAndFullBBoxes(target, options)`
Get both clipped (viewBox-respecting) and unclipped bounds.
**Returns:** `{visible: {x,y,width,height}, full: {x,y,width,height}}`
### `showTrueBBoxBorder(target, options)`
Visual debugging overlay showing true bounds.
**Options:** `theme`, `borderColor`, `borderWidth`, `padding`
**Returns:** Object with `remove()` method
### `waitForDocumentFonts(document, timeoutMs)`
Wait for fonts to load before measuring text.
**Default timeout:** 8000ms
See [API.md](./API.md) for comprehensive browser API documentation with examples
for:
- Computing accurate bounding boxes
- Working with complex text and transforms
- Handling multiple elements
- Visual debugging with borders
- Reframing viewBox to focus on objects
- Theme customization
- Error handling
- Performance tips
---
## Tools CLI commands usage
### Renderer: `sbb-render.cjs`
Render SVG → PNG using Chrome + `SvgVisualBBox`.
#### Syntax
```bash
node sbb-render.cjs input.svg output.png \
[--mode full|visible|element] \
[--element-id someId] \
[--scale N] \
[--width W --height H] \
[--background white|transparent|#rrggbb] \
[--margin N]
```
#### Modes
- `--mode full`
- Ignore the SVG’s viewBox when measuring.
- Render the **entire drawing** (full visual extent).
- `--mode visible` (default)
- Consider the viewBox as a clipping region.
- Crop to the **visible content inside the viewBox**.
- `--mode element --element-id ID`
- Hide everything except the element with that ID.
- Measure it visually and render a canvas just big enough for that element (+
margin).
#### Example
```bash
# Transparent PNG of what's actually visible in the viewBox
node sbb-render.cjs map.svg map.png \
--mode visible \
--margin 10 \
--background transparent
```
---
### Comparer: `sbb-comparer.cjs`
Compare two SVGs visually by rendering them to PNG and performing pixel-by-pixel
comparison.
#### Syntax
```bash
node sbb-comparer.cjs svg1.svg svg2.svg [options]
```
#### Options
- `--out-diff <file>` - Output diff PNG file (white=different, black=same)
- `--threshold <1-20>` - Pixel difference threshold (default: 1)
- Pixels differ if any RGBA channel differs by more than threshold/256
- `--alignment <mode>` - How to align the two SVGs
- `origin` - Align using respective SVG origins (0,0) [default]
- `viewbox-topleft` - Align using top-left corners of viewBox
- `viewbox-center` - Align using centers of viewBox
- `object:<id>` - Align using coordinates of specified object ID
- `custom:<x>,<y>` - Align using custom coordinates
- `--resolution <mode>` - How to determine render resolution
- `viewbox` - Use respective viewBox dimensions [default]
- `nominal` - Use respective nominal resolutions
- `full` - Use full drawing content (ignore viewBox)
- `scale` - Scale to match larger SVG (uniform)
- `stretch` - Stretch to match larger SVG (non-uniform)
- `clip` - Clip to match smaller SVG
- `--meet-rule <rule>` - Aspect ratio rule for 'scale' mode (default: xMidYMid)
- `--slice-rule <rule>` - Aspect ratio rule for 'clip' mode (default: xMidYMid)
- `--json` - Output results as JSON
- `--verbose` - Show detailed progress
#### Understanding preserveAspectRatio Values
The `--meet-rule` and `--slice-rule` options accept SVG `preserveAspectRatio`
alignment values. This diagram illustrates how different values affect
alignment:
<div align="center">
<img src="./assets/alignement_table_svg_presrveAspectRatio_attribute_diagram.svg" alt="preserveAspectRatio Alignment Diagram" width="100%">
</div>
- **meet mode**: Scales content to fit entirely within viewport (may have empty
space)
- `xMinYMin` - Align top-left
- `xMidYMid` - Align center (default)
- `xMaxYMax` - Align bottom-right
- **slice mode**: Scales content to fill viewport (may crop content)
- Same alignment options as meet mode
#### Examples
```bash
# Basic comparison
node sbb-comparer.cjs original.svg modified.svg
# Compare with custom threshold (more tolerant)
node sbb-comparer.cjs v1.svg v2.svg --threshold 5 --out-diff changes.png
# Align by viewBox centers, scale to match
node sbb-comparer.cjs icon1.svg icon2.svg \
--alignment viewbox-center \
--resolution scale
# JSON output for automation
node sbb-comparer.cjs test1.svg test2.svg --json
```
#### Output
Returns:
- Difference percentage (0-100%)
- Total pixels compared
- Number of different pixels
- Diff PNG image (white pixels = different, black = identical)
- **HTML comparison report** (automatically generated and opened in browser)
- Side-by-side SVG comparison with embedded images
- ViewBox and resolution details for each SVG
- Comparison settings summary
- Visual diff PNG with percentage
- Self-contained (can be shared without dependencies)
---
### Fixer: `sbb-fix-viewbox.cjs`
Fix missing/inconsistent viewBox and sizes.
#### Features
**ViewBox repair** Fixes SVGs with missing or inconsistent `viewBox`, `width`,
and `height` attributes. Uses pixel-accurate visual bounds to compute correct
values.
#### Syntax
```bash
node sbb-fix-viewbox.cjs input.svg [output.svg]
```
- If `output.svg` is omitted, writes `input.fixed.svg`.
- Uses `getSvgElementVisibleAndFullBBoxes` to find the full drawing bbox.
- Writes a new SVG that has:
- `viewBox="x y width height"`
- Reasonable `width`/`height` matching that aspect ratio.
#### Example
```bash
node sbb-fix-viewbox.cjs broken.svg fixed/broken.fixed.svg
```
---
### BBox Calculator: `sbb-getbbox.cjs`
CLI utility for computing visual bounding boxes using canvas-based measurement.
#### Syntax
**Single file:**
```bash
node sbb-getbbox.cjs <svg-file> [object-ids...] [--ignore-vbox] [--sprite] [--json <file>]
```
**Directory batch:**
```bash
node sbb-getbbox.cjs --dir <directory> [--filter <regex>] [--sprite] [--json <file>]
```
**List file:**
```bash
node sbb-getbbox.cjs --list <txt-file> [--sprite] [--json <file>]
```
#### Features
- **Whole SVG bbox**: Compute bbox for entire SVG content (respecting viewBox)
- **Multiple objects**: Get bboxes for specific elements by ID
- **Full drawing mode**: Use `--ignore-vbox` to measure complete drawing
(ignoring viewBox clipping)
- **Sprite sheet detection**: Use `--sprite` to automatically detect and process
icon sprites/stacks
- **Batch processing**: Process entire directories with optional regex filter
- **List files**: Process multiple SVGs with per-file object IDs from a text
file
- **JSON export**: Save results as JSON for programmatic use
- **Auto-repair**: Missing SVG attributes (viewBox, width, height,
preserveAspectRatio) are computed
#### Examples
```bash
# Compute whole SVG bbox
node sbb-getbbox.cjs drawing.svg
# Compute specific elements
node sbb-getbbox.cjs sprites.svg icon_save icon_load icon_close
# Get full drawing (ignore viewBox)
node sbb-getbbox.cjs drawing.svg --ignore-vbox
# Auto-detect and process sprite sheet
node sbb-getbbox.cjs sprite-sheet.svg --sprite
# Batch process directory with filter
node sbb-getbbox.cjs --dir ./icons --filter "^btn_" --json buttons.json
# Process from list file
node sbb-getbbox.cjs --list process-list.txt --json output.json
```
#### Objects List File Format
Each line: `<svg-path> [object-ids...] [--ignore-vbox]`
```
# Process whole SVG content
path/to/icons.svg
# Process specific objects
path/to/sprites.svg icon1 icon2 icon3
# Get full drawing bbox (ignore viewBox)
path/to/drawing.svg --ignore-vbox
```
#### Sprite Sheet Detection
When using the `--sprite` flag with no object IDs specified, the tool
automatically detects sprite sheets (SVGs used as icon stacks) and processes
each sprite/icon separately.
**Detection criteria:**
- **Size uniformity** - Coefficient of variation < 0.3 for widths, heights, or
areas
- **Grid arrangement** - Icons arranged in rows/columns with consistent spacing
- **Common naming patterns** - IDs matching `icon_`, `sprite_`, `symbol_`,
`glyph_`, or numeric patterns
- **Minimum count** - At least 3 child elements
**Example output:**
```
🎨 Sprite sheet detected!
Sprites: 6
Grid: 2 rows × 3 cols
Avg size: 40.0 × 40.0
Uniformity: width CV=0.000, height CV=0.000
Computing bbox for 6 sprites...
SVG: sprite-sheet.svg
├─ icon_1: {x: 5.00, y: 5.00, width: 40.00, height: 40.00}
├─ icon_2: {x: 80.00, y: 5.00, width: 40.00, height: 40.00}
├─ icon_3: {x: 150.00, y: 5.00, width: 40.00, height: 40.00}
└─ ... (remaining sprites)
```
#### Output Format
**Console:**
```
SVG: path/to/file.svg
├─ WHOLE CONTENT: {x: 0, y: 0, width: 100, height: 100}
├─ icon1: {x: 10, y: 10, width: 20, height: 20}
└─ icon2: {x: 50, y: 50, width: 30, height: 30}
```
**JSON** (with `--json`):
```json
{
"path/to/file.svg": {
"WHOLE CONTENT": { "x": 0, "y": 0, "width": 100, "height": 100 },
"icon1": { "x": 10, "y": 10, "width": 20, "height": 20 },
"icon2": { "x": 50, "y": 50, "width": 30, "height": 30 }
}
}
```
---
### SVG Objects Extractor `sbb-extract.cjs`
A versatile tool for **listing, renaming, extracting, and exporting** SVG
objects.
#### 1️⃣ List mode — `--list`
```bash
node sbb-extract.cjs input.svg --list \
[--assign-ids --out-fixed fixed.svg] \
[--out-html list.html] \
[--json]
```
**What it does:**
- Scans the SVG for "objects":
- `g`, `path`, `rect`, `circle`, `ellipse`,
- `polygon`, `polyline`, `text`, `image`, `use`, `symbol`.
- **Automatically detects sprite sheets** - identifies SVGs used as icon/sprite
stacks and provides helpful tips.
- Computes a **visual bbox** for each object.
- Generates an **HTML page**:
- Column `#`: row number (used in warnings).
- Column `OBJECT ID`: current `id` (empty if none).
- Column `Tag`: element name.
- Column `Preview`: small `<svg>` using the object’s bbox and
`<use href="#id">`.
- Column `New ID name`: text input + checkbox for renaming.
- With `--assign-ids`:
- Objects without `id` receive auto IDs (`auto_id_path_1`, …).
- If `--out-fixed` is given, a fixed SVG is saved with those IDs.
**HTML extras:**
- **Filters:**
- Regex filter (ID, tag, or group IDs).
- Tag filter (only paths, only groups, etc.).
- Group filter (only descendants of `someGroupId`).
- Area filter (objects whose bbox intersects a given rectangle).
- **Live rename validation:**
- Valid SVG ID syntax: `^[A-Za-z_][A-Za-z0-9_.:-]*$`
- No collision with existing IDs in the SVG.
- No collision with earlier rows’ new IDs.
- Invalid rows:
- Get a **subtle red background**.
- Show a red warning message under the input.
- “Save JSON with renaming” is disabled while any row is invalid.
- **JSON export:**
- Clicking **“Save JSON with renaming”** downloads a mapping file like:
```json
{
"sourceSvgFile": "input.svg",
"createdAt": "2025-01-01T00:00:00.000Z",
"mappings": [
{ "from": "auto_id_path_3", "to": "icon_save" },
{ "from": "auto_id_g_5", "to": "button_primary" }
]
}
```
#### 2️⃣ Rename mode — `--rename`
Apply renaming rules from a JSON mapping.
```bash
node sbb-extract.cjs input.svg --rename mapping.json output.svg [--json]
```
Accepted JSON forms:
- Full payload with `mappings` (as exported by HTML).
- Bare array: `[ { "from": "oldId", "to": "newId" } ]`.
- Plain object: `{ "oldId": "newId", "oldId2": "newId2" }`.
**What happens:**
- For each mapping (in order):
- Validates syntax & collisions.
- If valid:
- `id="from"` → `id="to"`.
- Updates `href="#from"` / `xlink:href="#from"` → `#to`.
- Updates `url(#from)` → `url(#to)` in all attributes (fills, filters,
masks, etc.).
- Invalid mappings are **skipped** and reported (reason included).
#### 3️⃣ Extract mode — `--extract`
Extract a **single object** into its own SVG.
```bash
node sbb-extract.cjs input.svg --extract someId output.svg \
[--margin N] \
[--include-context] \
[--json]
```
Two modes:
- **Default (no `--include-context`)** → _pure cut-out_:
- Keeps only:
- Target element.
- Its ancestor groups.
- `<defs>` (for filters, markers, etc.).
- No siblings or overlays.
- **With `--include-context`** → _cut-out with context_:
- Copies all children of the root `<svg>` (so overlays & backgrounds stay).
- Crops the root `viewBox` to the target object’s bbox (+ margin).
- Good when you want to see the object under the same filters/overlays but
cropped to its own rectangle.
#### 4️⃣ Export-all mode — `--export-all`
Export every object (and optionally groups) as separate SVGs.
```bash
node sbb-extract.cjs input.svg --export-all out-dir \
[--margin N] \
[--export-groups] \
[--json]
```
- Objects considered:
- `path`, `rect`, `circle`, `ellipse`,
- `polygon`, `polyline`, `text`, `image`, `use`, `symbol`.
- With `--export-groups`:
- `<g>` groups are also exported.
- Recursively exports children within groups.
- Even nested groups get their own SVG.
Each exported SVG:
- Has `viewBox = bbox (+ margin)`.
- Has matching `width` / `height`.
- Contains `<defs>` from the original.
- Includes the ancestor chain from the root to the object, with the object’s
full subtree.
---
### Inkscape Integration Tools: `sbb-inkscape-*` ⚠️
**⚠️ IMPORTANT ACCURACY WARNING**
The Inkscape-based tools (`sbb-inkscape-*`) are provided **for completeness and
comparison purposes only**. They use Inkscape's rendering engine which has
**known issues with font bounding box calculations** and may produce inaccurate
results, especially for text elements.
**For production use and accurate bounding box computation, always use the
native svg-bbox tools:**
- `sbb-render.cjs` - Native SVG rendering with accurate bbox
- `sbb-getbbox.cjs` - Precise bounding box calculation
- `sbb-extract.cjs` - Multi-tool with accurate visual bbox
These native tools use our custom algorithms that correctly handle font metrics
and provide reliable, cross-platform results.
---
#### Available Inkscape Tools
##### 1. `sbb-inkscape-text2path.cjs` - Convert Text to Paths
Convert text elements to path outlines using Inkscape.
```bash
node sbb-inkscape-text2path.cjs input.svg [options]
```
**Options:**
- `--output <file>` - Output SVG file (default: `<input>-paths.svg`)
- `--batch <file>` - Batch mode (one SVG path per line)
- `--overwrite` - Overwrite output file if it exists
- `--skip-comparison` - Skip automatic similarity check (faster)
- `--json` - Output results as JSON
- `--help` - Show help
- `--version` - Show version
**Example:**
```bash
# Convert text to paths
node sbb-inkscape-text2path.cjs document.svg
# Convert with custom output
node sbb-inkscape-text2path.cjs document.svg document-paths.svg
# Batch convert multiple files
node sbb-inkscape-text2path.cjs --batch files.txt
# Skip automatic comparison (faster)
node sbb-inkscape-text2path.cjs document.svg --skip-comparison
# Overwrite existing files
node sbb-inkscape-text2path.cjs document.svg --overwrite
```
##### 2. `sbb-inkscape-extract.cjs` - Extract Object by ID
Extract a single object from an SVG file by its ID.
```bash
node sbb-inkscape-extract.cjs input.svg --id <object-id> [options]
```
**Options:**
- `--id <id>` - Object ID to extract (required)
- `--output <file>` - Output SVG file (default: `<input>_<id>.svg`)
- `--margin <pixels>` - Margin around extracted object in pixels
- `--help` - Show help
- `--version` - Show version
**Example:**
```bash
# Extract specific object
node sbb-inkscape-extract.cjs sprite.svg --id icon_home
# Extract with custom output
node sbb-inkscape-extract.cjs sprite.svg --id icon_home --output home.svg
# Extract with margin
node sbb-inkscape-extract.cjs sprite.svg --id icon_home --margin 10
```
⚠️ **Note:** For more reliable object extraction with accurate bounding boxes,
use `sbb-extract.cjs --extract` instead.
##### 3. `sbb-inkscape-svg2png.cjs` - SVG to PNG Export
Comprehensive PNG export with full control over all Inkscape parameters.
```bash
node sbb-inkscape-svg2png.cjs input.svg [options]
```
**Dimension & Resolution Options:**
- `--width <pixels>`, `--height <pixels>` - Export dimensions
- `--dpi <dpi>` - Export DPI (default: 96)
- `--margin <pixels>` - Margin around export area
**Export Area Options:**
- `--area-drawing` - Bounding box of all objects (default)
- `--area-page` - Full SVG page/viewBox area
- `--area-snap` - Snap to nearest integer px (pixel-perfect)
- `--id <object-id>` - Export specific object by ID
**Color & Quality Options:**
- `--color-mode <mode>` - Gray_1-16, RGB_8-16, GrayAlpha_8-16, RGBA_8-16
- `--compression <0-9>` - PNG compression level (default: 6)
- `--antialias <0-3>` - Antialiasing level (default: 2)
**Background Options:**
- `--background <color>` - Background color (SVG color string)
- `--background-opacity <n>` - Opacity: 0.0-1.0 or 1-255
**Legacy File Handling:**
- `--convert-dpi <method>` - none/scale-viewbox/scale-document
**Batch Processing:**
- `--batch <file>` - Process multiple files (one SVG path per line)
**Other Options:**
- `--help` - Show help
- `--version` - Show version
**Examples:**
```bash
# High-quality export with maximum compression
node sbb-inkscape-svg2png.cjs logo.svg \
--width 1024 --height 1024 \
--antialias 3 --compression 9
# Export with white background
node sbb-inkscape-svg2png.cjs