feat(draw): compare template + aspect-fit image painting (T-319)

compareTemplateHandler lowers an `images` array to an SVG of side-by-side
<image> cells, each with the per-object data-label/description caption and
data-lightbox; paths resolve to absolute up front (injected, honest error
on a miss). _paintImage now aspect-fits (contain, centered) so cells of
differing-shape images don't distort. Flutter-free handler + painter pixel
tests. Card image-loading + registration next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-29 13:57:47 +02:00
co-authored by Claude Opus 4.8
parent 281fb2a869
commit a651c9e026
6 changed files with 361 additions and 1 deletions
+12
View File
@@ -110,6 +110,18 @@ void main() {
expect(alpha(await argbAt(img, 5, 5)), 0);
});
test('a wide image is aspect-fit (contain) into a square cell, centered (T-319)', () async {
// 8x2 source into a 10x10 cell → scaled to 10x2.5, centered vertically
// (rows ~3.756.25). The center has the image; the top/bottom margins stay
// empty — proving contain-fit, not a stretched fill.
final wide = await solidImage(8, 2, 0xFF00FF00);
const svg = '<svg viewBox="0 0 10 10"><image x="0" y="0" width="10" height="10" href="pic"/></svg>';
final img = await render(svg, 10, 10, images: (href) => href == 'pic' ? wide : null);
expect(await argbAt(img, 5, 5), 0xFF00FF00); // center: image
expect(alpha(await argbAt(img, 5, 0)), 0); // top margin: empty
expect(alpha(await argbAt(img, 5, 9)), 0); // bottom margin: empty
});
test('renders the real d2 fixture without error and draws ink', () async {
final svg = File('test/svg/fixtures/d2_pipeline.svg').readAsStringSync();
final img = await render(svg, 200, 120);