ipolypad turns an image or SVG into a small, normalized polygon hull (16–32 points) ideal for CSS shape-outside and HTML/canvas text-wrap engines. Two implementations share one spec.

What it does

image / SVG  →  rasterize  →  silhouette mask  →  pad (dilate/erode)  →
                potrace  →  pick outer shell  →  convex hull  →
                simplify (RDP)  →  normalize  →  JSON / CSS

The output is intentionally low-fidelity: text-wrap algorithms don’t need a precise outline, they need a small polygon that hugs the visible shape with a sensible visual gap. ~32 vertices is the sweet spot.

Padding is real geometry, not a clipped inset trick. If the padded silhouette extends outside the source image rectangle, the emitted polygon can contain negative percentages or values above 100%. Negative padding erodes the silhouette for tighter wraps.

Pick your language

  • Python — reference. Smart input handling (colour-distance background detection, auto-contrast, adaptive thresholding), CLI via uvx ipolypad, emits json/css/svg/png/html.
  • JavaScript — browser-first. Strict subset, small bundle, assumes alpha-bearing input, emits json/css.

For the full algorithmic contract see the specification.

Try it

The interactive demo lets you drop in an SVG or PNG, runs ipolypad-js in the browser, and reflows a passage of text around the resulting polygon via pretext-flow.

Output

Default JSON:

{
  "src": "dragon.svg",
  "raster": { "width": 200, "height": 200, "pad": 6 },
  "max_points": 32,
  "n_points": 18,
  "points": [[0.500000, 0.020000], ...]
}

CSS:

.figure {
  shape-outside: polygon(-1.28% 55.64%, 50.00% -8.20%, 103.52% 61.10%, /* ... */);
  clip-path:     polygon(-1.28% 55.64%, 50.00% -8.20%, 103.52% 61.10%, /* ... */);
}

Asymmetric by design

The Python package is the smart one — colour analysis, auto-contrast, batch, multi-format output. The JavaScript package is intentionally a strict subset so it stays small enough to ship to a browser. They agree byte-for-byte on the overlapping parity surface; everything else is one-sided on purpose.

License

Apache-2.0. See the GitHub repo.