Conversation
Copy flags/1x1 and flags/4x3 from lipis/flag-icons v7.5.0, replacing hyphens in filenames with underscores so the names stay valid Dart enum identifiers. Keep ac.svg / ta.svg (renamed to sh-ac / sh-ta upstream) in sync with their renamed sources, and keep ea.svg (removed upstream) as is. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add ARAB, ASEAN, EAC, ES_PV, PC, SH_AC, SH_HL and SH_TA to FlagsCode and baseFlagsCode, bringing the set to 274 codes. AC, TA and EA are kept even though upstream renamed or dropped them, since they are part of the public API. Bump to 7.1.0 and point the docs at flag-icons v7.5.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ag, ax, bi and bj were still carrying their v4.1.4 artwork in res/1x1 after the bulk sync. Replace them with the v7.5.0 sources so all 274 files in both res/1x1 and res/4x3 now match flag-icons v7.5.0 byte for byte. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
flag-icons v7.5.0 lays the 50 stars of us/um with a single guide path and `marker-mid`. Renderers that have no <marker> support drop the whole star field and leave a bare navy canton — flutter_svg (vector_graphics) is one of them, and it logs `unhandled element <marker/>` when it hits these two files. That makes this package's most used flag wrong on every Flutter app that upgrades to the v7.5.0 artwork. Expand the marker into a <defs> star plus one <use> per vertex. Marker placement here reduces to a plain translate — markerUnits defaults to strokeWidth, the guide path has no stroke-width so the scale is 1, and refX/refY default to 0 — so the geometry is unchanged. Verified with librsvg: the rewritten files render pixel-identical (AE = 0) to the marker version at 440x330 (4x3) and 400x400 (1x1), and flutter_svg now draws all 50 stars.
flutter_svg (vector_graphics) resolves transforms at compile time and keeps stroke-width a scalar, so a stroke under a non-uniform scale gets one averaged pen instead of an elliptical one. es_ct drew its four red stripes as a stroked path under `scale(.6321 .94815)`, so the red bands came out ~18% too thin (23px vs 28px at a 256px render) while the yellow ones grew to fill the gap -- the Senyera's nine bands were visibly unequal. Nothing failed: no parse error, no warning, goldens passed. Baking the scale into the path coordinates makes the pen uniform again, which every renderer agrees on. Verified against rsvg-convert 2.58.4 at 256px: RMSE 0.115 -> 0.006 (1x1) and 0.055 -> 0.007 (4x3), and the nine stripes now measure 28/28/28/27/28/27/28/28/28 px against the reference's 28/27/28/27/28/27/28/27/28. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Same root cause as the Catalonia fix: vector_graphics folds transforms into path coordinates at compile time but keeps stroke-width a scalar, so a stroke under a non-uniform scale gets one averaged pen instead of an elliptical one. vu drew its whole Y inside `scale(.56889 1.0779)` / `scale(.71111 1.01053)`, so every band was wrong at once -- on the horizontal arm the yellow ran 62% too wide (21px vs 13px at a 256px render) while the black fimbriation lost 27%, and on the diagonals the yellow lost 6% and the hoist triangle grew 8%. A hairline seam also showed up inside the yellow arm. Nothing failed: no parse error, no warning, goldens passed. Baking the scale in is not enough here, because the same path mixes diagonal and horizontal segments and no single stroke-width is correct for both. So the pall is now three nested filled polygons -- outer black, yellow inset by 30, inner black inset by 55 -- which is the construction the standard Vanuatu drawing uses and which no renderer can get wrong. Offsets are the exact miter geometry of the strokes they replace, so the artwork is unchanged; the clipPath is no longer needed and goes away with them. Verified with rsvg-convert 2.58.4 at 900px. Reference render before vs after the rewrite is unchanged (RMSE 0.006/0.005, antialiasing only), confirming the geometry is a faithful transcription rather than a redesign. flutter_svg against the reference goes 0.127 -> 0.005 (1x1) and 0.107 -> 0.005 (4x3), and the band runs now match the reference exactly: arm 56/46/56 px (1x1) and 41/35/41 px (4x3), diagonals 61/50/228/50/61 px at x=250. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…not exist Upstream flag-icons ships sh-ac.svg with 96 `url(#…)` references and exactly one defined id -- the whole `<defs>` block is missing, so 62 elements fill from a gradient that does not exist and 34 more mask against a mask that does not exist. Per spec an invalid paint reference makes the element unrenderable, so the Green Mountain of the crest, the green chevron of the shield and parts of the turtle supporters were simply absent. Every renderer drops them identically, which is why comparing two renderers never caught it and only a static id scan did. Not our regression: these four files are byte-identical to upstream, and the same 96 dangling references are present as far back as flag-icons 6.15.0 and in the GitHub source, not just the npm build. The gradients are gone rather than wrong, so there is nothing to repair -- the intended shading is unrecoverable and inventing 62 fills would be guesswork. The arms are instead taken from the public-domain "Flag of Ascension Island PD.svg" on Wikimedia Commons (own work by Shervinafshar; PD, no attribution required), which is pure `<path>` geometry: no gradients, masks, filters or embedded rasters, so there is nothing in it that flutter_svg can silently drop. The blue field and the Union Jack canton are kept from the existing MIT file untouched, so only the arms change. The commons file credited as the ordinary reference could not be used: it embeds 36 base64 rasters behind `<filter>` elements, and its own credit line derives it from a CC BY-SA 3.0 coat of arms. The arms are fitted to the exact bounding box the old arms occupied -- measured at 316.8,148.48 300.16x276.8 (4x3) and 245.25,198.66 250.37x230.4 (1x1) -- with a uniform scale, so the flag's layout is unchanged. Coordinates are baked and requantised to 0.1 viewBox units against the emitted point rather than the exact one, so rounding cannot accumulate along a path; round-trip error is 0.05 units and the render is within 0.003 RMSE of a lossless build. That takes the files from 143,373 to 116,334 bytes (4x3) and 140,519 to 114,620 (1x1), -19%; they stay large because the arms are genuinely 461 distinct paths with almost no two adjacent ones sharing a fill, so merging runs saves 19 bytes. Dangling `url(#…)` references across all 548 files: 384 -> 0, external references 0. flutter_svg renders the four files with no parser warning and within 0.008 RMSE of rsvg-convert 2.58.4. ac and sh_ac stay byte-identical. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The pall bands did not match the grid the Vanuatu State Flag and Armorial Bearings Public Declaration (19 March 1980) lays down. The declaration builds the flag on an 18x12 grid where the black fimbriation and the yellow pall are each 1/18 of the height and therefore equal; flag-icons draws the black wider than the yellow. Measured at 640x480 against the reference stretched to the same box, the horizontal arm read black 29 / yellow 24 / black 29 where the grid gives 26 / 26 / 26 -- black 14% over, yellow 5% under, the whole pall 7% over, and the tip of the hoist triangle 14% too far right. Worth recording that Commons carries two constructions that disagree: Flag_of_Vanuatu.svg (950x570) uses black 36 / yellow 30 / black 36, which is exactly what flag-icons draws, while Flag_of_Vanuatu_(official).svg is built on the 18x12 grid and cites the Declaration itself. The one citing the legal instrument is taken as authoritative here. Reported upstream as lipis/flag-icons#1465. Redrawn from the grid rather than nudged: diagonals at slope 5/9, each band 2/3 of a grid unit measured perpendicular, so the vertical offset along a diagonal is 2*sqrt(106)/27. Every vertex is derived from those constants and agrees with the reference file's own numbers to 6.4e-5 grid units, the reference being the rounded one. Drawn as four filled polygons with the grid baked into the coordinates -- no <marker>, no stroke under a non-uniform scale, nothing this audit has already been bitten by. The boar's tusk emblem is untouched. It needed no reseating: the redrawn inner black triangle reaches further right, so clearance at the emblem's right edge goes from 2.11 to 7.82 units (4x3) and 1.66 to 8.20 (1x1). Verified in both renderers and both ratios by column scan. Every run now matches the reference exactly -- 4x3 arm 26/26/26 and diagonal 29/29/106/29/29, 1x1 arm 27/28/27 and diagonal 30/31/112/31/30 -- read identically off rsvg-convert 2.58.4 and off flutter_svg. flutter_svg against rsvg is RMSE 0.0060 (4x3) and 0.0049 (1x1) with no parser warning. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The flags bundled in this package were still from flag-icons v4.1.4. This PR re-syncs every flag from flag-icons v7.5.0 — both
res/1x1/andres/4x3/— and extendsFlagsCode/baseFlagsCodeto match.Upstream also shrank most of the SVGs since v4.1.4, so a large part of the diff is just smaller artwork, not visual change.
FlagsCodeentriesNULL)NULL)res/1x1/andres/4x3/contain exactly the same 274 filenames.Then something unexpected happened. While checking the new artwork, I found that 6 flags were rendering wrong in Flutter — silently, with no error and with the test suite green. Those are fixed here too. The details are further down, written to be readable without SVG background.
New flag codes (8)
ARABASEANEACES_PVPCSH_ACSH_HLSH_TAWhy the filenames use
_instead of-Upstream ships these as
es-pv.svg,sh-ac.svg,gb-eng.svg, … butFlagbuilds its asset path straight from the enum name:A Dart enum identifier can't contain
-, so a hyphenated filename would be unreachable throughFlag.fromCode. Every-in an upstream filename is therefore rewritten to_on copy — the same convention this repo already used fores_ct,gb_engand friends. No name collisions.Why
AC,TAandEAare keptUpstream renamed or removed three codes that this package already exposes publicly. Dropping them would break existing users, so they stay and this remains a minor version bump:
ACsh-acsh-ac(soACandSH_ACrender the same flag)TAsh-tash-taEAThe rendering bugs I found (and fixed)
Everything in this section is about Flutter specifically. These files look correct in a browser. They were wrong only when
flutter_svgdrew them — which is exactly why nobody had noticed.How this started
The US flag lost all 50 stars.
The stars in
us.svgare drawn with an SVG tag called<marker>— think of it as "stamp this shape at these points".flutter_svgdoesn't support that tag, and instead of complaining, it just skips it. No error, no warning, and the existing golden tests still passed because the golden files had been generated from the same broken output.That is the scary part: a flag can be visibly wrong and every automated check stays green.
So I stopped trusting "it looks fine" and checked all of them properly.
How I checked (in plain terms)
I drew all 548 SVGs (274 codes × 2 aspect ratios) twice:
flutter_svg— what this package actually shows usersrsvg-convert(librsvg) — a mature, independent renderer used as the "second opinion"…then compared the two images pixel by pixel, at two sizes each (2,208 images in total).
Two renderers never agree perfectly — they smooth edges differently — so "any difference at all" is a useless alarm. The measurement that actually decides is: mark every pixel that differs by more than 15%, then shrink that mask by a pixel. Thin edge-smoothing seams disappear; anything with real area survives.
I validated the method before trusting it. I threw the known-broken pre-fix
us/uminto the same batch. Every metric separated them cleanly from the 548 current files:1x1/us(stars missing)4x3/us(stars missing)In other words: the check demonstrably catches the bug we already knew about. Then I opened the 48 worst-scoring files and looked at them by eye.
All 548 rendered with zero parser warnings. The only "unhandled element" message in the whole run came from the broken control file.
Bug class 1 — an unsupported tag is silently dropped
us,um— all 50 stars missing. Fixed in8899603by drawing the stars as ordinary shapes instead of using<marker>.Bug class 2 — squashed drawings get the wrong line thickness
This one needs one idea explained first.
Imagine drawing a line with a round pen, then squashing the whole drawing to 60% width but leaving the height alone. The line should get squashed too — the pen effectively becomes an oval, thinner sideways than up-down. That's what the SVG spec says should happen.
flutter_svgkeeps the pen round, using one averaged width. So any line drawn inside a non-uniform squash comes out the wrong thickness — and, again, with no error.es_ct(Catalonia) — the red stripes were 18% too thin → fixed in203afc1The Senyera is nine equal bands. Measured stripe widths at 256px:
28 27 28 27 28 27 28 27 2830 23 32 23 32 23 32 23 3028 28 28 27 28 27 28 28 28The fix bakes the squash into the coordinates so the pen is round again. The arithmetic is exact —
512/9 = 56.889and810 × 0.6321 = 512.0:Difference against the reference renderer drops 0.115 → 0.006, i.e. down into ordinary edge-smoothing noise.
vu(Vanuatu) — every band of the pall was wrong, plus a visible seam → fixed in74b805dSame root cause, worse. The yellow arm was 62% too wide while its black border lost 27%, and a hairline seam split the yellow arm in two — visible at normal size.
Baking in the squash wasn't enough here: the same path mixes diagonal and horizontal segments, so no single line width is right for both. So the pall is now filled shapes instead of lines — three nested polygons, outer black, yellow inset, inner black inset. There's no line left for a renderer to get wrong.
The vertices are the exact corner geometry of the lines they replace, not an eyeballed approximation.
vuwas the single worst file in the audit. It now sits at the median.One flag left alone:
4x3/gb_sctSame class, but the diagonal is only ~4% thin (55px vs 57px) — below what anyone can see. I scanned all 548 files for this pattern: 37 files have it, but only the ones above have an error big enough to matter. The rest are sub-pixel.
Worth noting it's
line width × how uneven the squash isthat hurts, not unevenness alone —omhas the most uneven squash of all (4.24×) and an error of 0.18px.Bug class 3 — the file points at paint that isn't there
ac/sh_ac(Ascension Island) → fixed inc68dbeaThese files contain 96 references to named pieces of paint (
url(#…)) and define exactly one of them. The whole<defs>block — every gradient, every mask — is simply missing:fill="url(#…)"pointing at a missing gradientmask="url(#…)"pointing at a missing maskPer spec, a shape whose paint can't be found isn't drawn at all. What's missing isn't decoration: the Green Mountain in the crest, the green chevron on the shield, and parts of the turtle supporters were all absent.
This is not something this PR introduced. The files here were byte-identical to upstream, and the same 96 dangling references are in every flag-icons release I checked back to 6.15.0, and in the GitHub source tree — so it isn't an npm packaging artefact. It looks like an id-cleanup pass that deleted "unused" definitions without scanning
url()inside attributes.Why the pixel comparison couldn't catch this: a broken reference is dropped identically by every renderer. So the two renderers agree perfectly, and the diff is clean —
1x1/acscored 0.0125, nowhere near the outliers. It only surfaced from a separate static scan for undefined ids. Plainly put: "the two renderers agree" is not the same as "the file is correct."The fix: the gradients are gone, not wrong, so there's nothing to repair and inventing 62 fills would be guesswork. The arms are taken from
Flag of Ascension Island PD.svgon Wikimedia Commons — public domain, own work, no attribution required — which is pure path geometry: 461 paths, no gradients, masks, filters or embedded images, so there's nothing left forflutter_svgto silently drop.The obvious alternative,
Flag_of_Ascension_Island.svg, was rejected on two grounds: it embeds 36 base64 images behind 36<filter>elements — neither supported byflutter_svg, so vendoring it would have recreated the exact bug being fixed — and its own credit line derives it from a CC BY-SA 3.0 coat of arms, which doesn't belong in an MIT package.The blue field and the Union Jack canton are kept from the existing MIT file untouched, so only the arms change, fitted to the exact box the old arms occupied. The files also got ~19% smaller.
A follow-up on
vu: conforming to the 1980 lawAfter the fix above, one difference from the "official" drawing remained. I originally wrote that this was upstream's own design choice and left it alone. That was wrong, and I'd rather correct it here than leave it buried in a comment thread.
The reason it was wrong: Wikimedia carries two Vanuatu constructions that disagree with each other, and I'd compared against the one that happens to match flag-icons. Only the other one cites the enabling law — the 18 × 12 grid of the State Flag and Armorial Bearings Public Declaration (19 March 1980).
On that grid the black border and the yellow pall are equal width. This repo drew the black wider:
Redrawn on the Declaration grid in
8f1f403. Diagonals at slope5/9, each band2/3of a grid unit measured perpendicular. Every vertex follows from those constants and agrees with the reference file's own numbers to6.4e-5grid units. The boar's tusk emblem is untouched — and clearance around it actually grows, so nothing needed reseating.Verification, scanning across columns of solid colour:
The
afterrows read identically off librsvg and off flutter_svg, in both aspect ratios, on all four scan lines.This one is also filed upstream, since that's the only durable home for it:
If that lands, this divergence disappears on the next sync.
Summary of findings
us/um— 50 stars missing8899603es_ct— stripes 18% thin203afc1vu— pall bands wrong + visible seam74b805dac/sh_ac— 96 references to missing paintc68dbeavu— pall off the 1980 Declaration grid8f1f403+ upstream PR4x3/gb_sct— diagonal ~4% thin12 files deliberately differ from upstream v7.5.0 —
us,um,es_ct,vu,ac,sh_ac, each in both aspect ratios. Everything else is still byte-for-byte upstream.Test plan
Run with the repo's pinned Flutter
3.35.3(via fvm).flutter analyze→ No issues found!flutter test→ all 282 tests pass (the existing widget test iteratesFlagsCode.values, so the 8 new codes are covered automatically)FlagsCodeminusNULLequalsbaseFlagsCodeexactly, same order, no duplicatesres/1x1/<code>.svgandres/4x3/<code>.svgac.svg/ta.svgare byte-identical tosh_ac.svg/sh_ta.svg;ea.svguntouchedurl(#…)references across all 548 files: 384 → 0; external references: 0dart formatwas applied only to the touched Dart file, to keep the diff reviewable.Caveats, stated plainly
flutter_svgrender — the same lesson this whole exercise kept producing.Happy to split any of the six artwork fixes into a separate PR if you'd rather keep this one to the version sync — just say which.