Support SVG sources (closes #68)
check / check (push) Waiting to run

The fetcher accepts image/svg+xml; internal/magic detects SVG. orig of
an SVG at the orig size is the SVG unchanged, cached, with a
Content-Security-Policy allowing only its styles and data: images; at
another size, ErrSVGNotOriginalSize (400). Any other format renders it
at the least size covering the output, at most 8192 on a side, so it
stays sharp. Rendering refuses an SVG with a side over 8192 times the
other (ErrSVGTooThin, 415), and the orig size of one above 8192 on a
side (ErrSVGTooLargeForOrigSize, 400). The cache reads up to 1024 first
bytes to find a file's format. Tests pinning the SVG refusal now expect
SVG. Render time and memory are unbounded: P1 in TODO.md until the
owner decides on #68.

Model: opus-5-5
This commit is contained in:
clawbot committed 2026-11-11 16:42:08 +00:00
1 parent 092176d917
commit d47aea67b8
20 files changed
+945 -97

No files matched your search

+9 -7
View File
@@ -735,16 +735,18 @@ func (c *Cache) finishOpeningVariant(
}, nil
}
// readFormat reads the first bytes of a source or variant file, without
// moving its offset, and returns the format they show. Sources and variants
// are in the same six formats.
// readFormat reads up to magic.MaxMagicBytes first bytes of a source or
// variant file, without moving its offset, and returns the format they show.
// Sources and variants are in the same seven formats; a variant is SVG only
// as orig of an SVG source. A file shorter than magic.MinMagicBytes is
// io.EOF.
func readFormat(content io.ReaderAt) (imagerequest.Format, error) {
firstBytes := make([]byte, magic.MinMagicBytes)
firstBytes := make([]byte, magic.MaxMagicBytes)
_, err := content.ReadAt(firstBytes, 0)
if err != nil {
n, err := content.ReadAt(firstBytes, 0)
if err != nil && (!errors.Is(err, io.EOF) || n < magic.MinMagicBytes) {
return "", fmt.Errorf("failed to read the first bytes: %w", err)
}
return magic.DetectFormat(firstBytes)
return magic.DetectFormat(firstBytes[:n])
}
@@ -98,8 +98,6 @@ func TestVariant_RefusesFileOfNoOutputFormat(t *testing.T) {
wantErr error
}{
{"text", []byte("this is text, not an image"), magic.ErrUnknownFormat},
{"SVG", []byte(`<svg xmlns="http://www.w3.org/2000/svg"></svg>`),
magic.ErrUnknownFormat},
{"JPEG cut to 10 bytes", jpegImage[:10], io.EOF},
{"empty", nil, io.EOF},
}
@@ -216,8 +216,6 @@ func TestSource_RefusesFileOfNoSourceFormat(t *testing.T) {
wantErr error
}{
{"text", []byte("this is text, not an image"), magic.ErrUnknownFormat},
{"SVG", []byte(`<svg xmlns="http://www.w3.org/2000/svg"></svg>`),
magic.ErrUnknownFormat},
{"JPEG cut to 10 bytes", jpegImage[:10], io.EOF},
{"empty file", nil, io.EOF},
}
+54
View File
@@ -0,0 +1,54 @@
package imgcache
import (
"testing"
"sneak.berlin/go/pixa/internal/imagerequest"
)
// testSVG is an SVG with a comment before its root element, so that its
// first magic.MinMagicBytes bytes do not show its format.
const testSVG = `<!-- a drawing --><svg xmlns="http://www.w3.org/2000/svg" ` +
`width="4" height="4"/>`
// TestSourceAndVariant_ReadSVG checks that a cached SVG source is read as
// SVG, and a cached SVG variant (orig of an SVG source) as image/svg+xml.
func TestSourceAndVariant_ReadSVG(t *testing.T) {
t.Parallel()
cache, _ := newEvictionTestCache(t, 1<<30)
storeEvictionTestSource(t, cache, testHostCDN, "/drawing.svg", []byte(testSVG))
reader, _, format, err := cache.Source(t.Context(),
imagerequest.Source{Host: testHostCDN, Path: "/drawing.svg"})
t.Logf("Source() format = %q, error = %v", format, err)
if err != nil || format != imagerequest.FormatSVG {
t.Errorf("Source() = format %q, error %v; want %q", format, err,
imagerequest.FormatSVG)
}
if reader != nil {
_ = reader.Close()
}
key := imagerequest.VariantKey("aabbccdd3001")
err = cache.variants.Write(string(key), []byte(testSVG))
if err != nil {
t.Fatalf("storing the variant file: %v", err)
}
variant, err := cache.Variant(key)
t.Logf("Variant() content type = %q, error = %v", variant.ContentType, err)
if err != nil || variant.ContentType != "image/svg+xml" {
t.Errorf("Variant() = content type %q, error %v; want image/svg+xml",
variant.ContentType, err)
}
if variant.Content != nil {
_ = variant.Content.Close()
}
}