How to stroke-to-fill quality and edge cases, grounded in the code
- Step 1Reproduce the issue with a minimal SVG — Isolate the problem element into a tiny SVG (one shape) and run it. Because the rule is fixed and per-element, a minimal case tells you exactly why an element did or didn't convert without other shapes confusing the picture.
- Step 2Check open vs closed — If the element vanished, confirm it's an open path (no
Z, or aline/polyline). Open shapes have no fill area and disappear after the colour move. If it solidified unexpectedly, confirm it's a closed shape whose empty interior is now filled. Both are expected. - Step 3Check how the stroke is declared — If nothing converted, inspect whether the stroke is an attribute (
stroke="…") or comes from CSS/<style>/class. Only attribute strokes are detected. Inline the stroke as an attribute and re-run if needed. - Step 4Confirm the element type is in scope — Only
path,rect,circle,ellipse,polygon,polyline,lineare processed.<text>,<image>,<use>, and<g>are never inspected. If your stroke lives on a group, push it onto the child shapes first. - Step 5Read the metrics to confirm scope of change —
Elements converted: Nis the count of elements rewritten. Compare it to how many stroke-only shapes you expected. A mismatch points to CSS strokes, already-filled elements, or out-of-scope tags. - Step 6Route to the correct fix — Need a visible outline from an open stroke? Use a vector editor's Stroke to Path. Need the result cleaned? Run svg-pro-minifier. Need a recolour rather than a stroke removal? Use svg-monochrome-converter or svg-hex-swapper.
Behaviour matrix — every case
The complete decision the tool makes, per element. There is no tolerance and no geometry; the outcome is fully determined by these inputs.
| Input | Converted? | Visible result |
|---|---|---|
Closed shape, fill="none" stroke="#c" | Yes | Solid fill in colour #c |
Closed shape, stroke="#c", no fill attr | Yes | Solid fill in colour #c |
Open path/line, stroke="#c" (any fill=none) | Yes (counted) | Invisible — zero-area fill |
Any element, fill="#x" stroke="#c" | No (already filled) | Unchanged — fill #x and stroke #c kept |
stroke="none" or no stroke | No | Unchanged |
Stroke via CSS class / <style> / inline style | No (not detected) | Unchanged — still stroked |
<text>, <image>, <use>, <g> | No (out of scope) | Unchanged |
Myth vs reality
Quality concerns commonly attributed to stroke-to-fill that do NOT apply to this attribute-rewrite tool.
| Common claim | Reality for this tool |
|---|---|
| Sharp miter joins can overshoot and need clamping | N/A — no joins are generated; stroke-linejoin/stroke-miterlimit are deleted |
| Near-zero-radius curves lose precision when expanded | N/A — no curve sampling or offset is performed; path d is untouched |
| Tolerance/quality setting controls expansion accuracy | N/A — there are no options at all |
| Round caps become little half-discs in the fill | N/A — stroke-linecap is deleted; nothing is reconstructed |
| Self-intersections create incorrect winding after expansion | N/A — geometry isn't changed; the path data is identical, only paint moves |
Cookbook
Each case shown as input → output so 'unexpected' results become explainable. The path data never changes — only paint attributes move.
Closed shape: clean, expected solidify
The well-behaved case. A closed outline becomes a solid fill; nothing about the geometry changes.
In: <rect x="1" y="1" width="22" height="22"
fill="none" stroke="#16a34a" stroke-width="2"/>
Out: <rect x="1" y="1" width="22" height="22" fill="#16a34a"/>
Elements converted: 1 → solid green square.Open path: the invisible result
The most-reported 'bug' that is actually correct behaviour. Path data is preserved exactly; only the paint changed, and an open path's fill is invisible.
In: <path d="M3 12 C 8 4, 16 20, 21 12" fill="none"
stroke="#000" stroke-width="2"/>
Out: <path d="M3 12 C 8 4, 16 20, 21 12" fill="#000"/>
Elements converted: 1 → curve disappears (open path).
Note: the C curve is untouched; nothing was sampled.CSS stroke: nothing happens
A stroke applied through a style block isn't an attribute, so the tool doesn't see it. The element passes through unchanged and isn't counted.
In:
<style>.ln{stroke:#000;stroke-width:2;fill:none}</style>
<path class="ln" d="M2 2 H22"/>
Out: identical — Elements converted: 0
Fix: inline as <path d="M2 2 H22" fill="none" stroke="#000"/>Already-filled element: protected
Two-tone artwork is safe — an element with a real fill is skipped whole, stroke included.
In: <circle cx="12" cy="12" r="8"
fill="#fde68a" stroke="#92400e" stroke-width="2"/>
Out: identical → Elements converted: 0
The contrasting stroke is preserved on purpose.Mixed document: count tells the story
Several elements, mixed states. The converted count equals the number of stroke-only elements, regardless of whether they ended up visible.
In: <rect ... fill="none" stroke="#000"/> (closed → solid) <line ... stroke="#000"/> (open → invisible) <path d="...Z" fill="#000"/> (filled → skip) Elements converted: 2 Visible: only the rect (the line vanished).
Edge cases and what actually happens
Open path becomes invisible
By designAn open path, polyline, or line has no enclosed area, so after the stroke is removed and fill is set it paints nothing. This is the most common 'quality' complaint and it is correct behaviour for a colour-only move. For a visible outline, expand the stroke geometrically in a vector editor.
Closed outline solidifies into a blob
By designA ring or any closed outline becomes a solid shape because its interior is now filled. There is no annulus computation. Expect rings → discs and outlined squares → solid squares. Use true expansion if you need the hole preserved.
No miter-join / curve-precision controls exist
Not applicableBecause no geometry is generated, questions about miter overshoot, bevel approximation, or curve-sampling precision simply don't apply. There is no tolerance and no quality knob. The path's d attribute and all coordinates are left byte-for-byte unchanged.
Stroke declared in CSS or class
Not convertedThe tool reads getAttribute('stroke'). A stroke from a <style> rule, a class, or inline style="stroke:…" is not seen, so the element is skipped and the count doesn't increment. Inline the stroke as a presentation attribute first.
Stroke inherited from a parent group
Not converted<g> is out of scope and inheritance isn't resolved. If children rely on a stroke set on the group and carry none themselves, nothing converts. Push the stroke down to each shape element before running.
Element already has a real fill
PreservedAnything with fill set to a non-none value is skipped entirely; both its fill and stroke survive. This protects intentional two-tone artwork from being flattened. If you wanted the stroke removed there too, do it manually or with a different tool.
stroke="url(#gradient)" copied to fill
Copied as-isA paint-server reference is copied verbatim, so fill then references the same gradient/pattern. It will render, but the gradient was authored for a stroke band and may look different filling an area. Re-author the gradient if the appearance matters.
Document with no qualifying elements
ExpectedIf everything is already filled, has no stroke, or is out of scope, Elements converted is 0 and the output equals the input (modulo serializer formatting). That's a correct no-op, not a failure.
Invalid or non-SVG input
invalid (parse error)Pasted content that isn't valid SVG XML is rejected before processing with an 'Invalid SVG — could not parse' error; empty input prompts you to upload or paste. Fix the markup (a parsererror from the DOM parser means malformed XML) and retry.
File exceeds the tier limit / wrong tier
rejected (limit / tier)Files over the per-job limit (5 MB Free, 50 MB Pro, 2 GB Developer) throw a limit error. And because the tool's minimum tier is Developer, Free/Pro accounts hit an upgrade overlay before they can run anything at all.
Frequently asked questions
How does the tool actually expand strokes?
It doesn't expand them. For stroke-only elements it copies the stroke colour onto fill and removes stroke, stroke-width, stroke-linecap, stroke-linejoin, and stroke-miterlimit. No offset path is computed and the path's d data is left unchanged. The result panel notes the output is an approximation, not offset-path expansion.
How are sharp miter joins handled?
They aren't generated, so there is nothing to handle. stroke-linejoin and stroke-miterlimit are deleted. Miter overshoot is a concern for true geometric expanders; this tool doesn't create join geometry at all.
Do near-zero-radius curves lose precision?
No, because no curve sampling or offsetting occurs. The path data, including all curve commands, is preserved exactly. Precision-loss questions apply to offset-path tools, not to this colour-move rewrite.
Why did my curved or straight line disappear?
It was an open path. Open paths enclose no area, so once the stroke is removed the fill paints nothing. That's expected for a colour-only conversion. Use a vector editor's Stroke to Path to turn the line into a visible filled band.
Why didn't anything convert?
Likely one of: the strokes are declared in CSS/classes (not attributes, so not detected), every element already has a real fill (skipped), the stroke is on a <g> group (out of scope), or there's no stroke at all. The Elements converted: 0 metric confirms nothing matched.
Is there a quality or tolerance setting?
No. The tool has no options. The outcome is fully determined by each element's stroke/fill attributes and its tag. If you need adjustable expansion accuracy, that's a feature of geometric outliners, not this tool.
What happens to gradient or pattern strokes?
A stroke="url(#id)" value is copied verbatim to fill, so the fill references the same paint server. It renders, but a gradient authored for a stroke band may look different filling an area — re-author it if appearance matters.
Will it ever damage my filled artwork?
No. Elements with a real fill are skipped entirely; both fill and stroke are preserved. Only stroke-only elements are rewritten, so two-tone designs survive intact.
What does 'Elements converted' count?
The number of stroke-only elements that were rewritten — regardless of whether they remained visible afterwards. An open path that vanished still counts as converted. Compare the number to your expectation to spot CSS strokes or out-of-scope tags.
What if my SVG won't process?
Invalid XML is rejected with an 'Invalid SVG — could not parse' message (the DOM parser produced a parsererror); empty input asks you to upload or paste. Oversized files exceed the per-job limit (5 MB Free / 50 MB Pro / 2 GB Developer). And Free/Pro accounts must upgrade — the tool's minimum tier is Developer.
What should I use for genuine outlining or related cleanup?
For real outlines: a vector editor's Stroke to Path / Outline Stroke. To clean the result: svg-pro-minifier. To recolour rather than remove strokes: svg-monochrome-converter or svg-hex-swapper. To simplify polyline paths: svg-path-simplifier.
Is my file uploaded for processing?
No. Parsing and rewriting happen in your browser via DOMParser/XMLSerializer, shown by the 0 bytes uploaded badge. Only an anonymous run counter is recorded for signed-in dashboard stats — never file content.
Privacy first
Every JAD SVG tool runs entirely in your browser using the DOM API and Canvas. Your SVG files never leave your device — verified by zero outbound network requests during processing.