From 48e18c16b623dc3af45bedfbc0fbc7e7f421fad2 Mon Sep 17 00:00:00 2001 From: lemon Date: Fri, 12 Jun 2026 22:59:28 -0700 Subject: [PATCH] Rasterize pasted images at a usable width for cropping A pasted SVG (or any small source) was rasterized by the proxy at its tiny intrinsic size, so the cropper showed a speck in the preview box. Request a target width (1500 banner / 1024 avatar) with fit=inside so small and vector sources are enlarged to a workable crop resolution. --- .../onboarding/VerifierIdentityStep.tsx | 6 ++++- src/lib/proxyImageUrl.ts | 22 +++++++++++++++---- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/components/onboarding/VerifierIdentityStep.tsx b/src/components/onboarding/VerifierIdentityStep.tsx index 953ae5c4..0ebebddb 100644 --- a/src/components/onboarding/VerifierIdentityStep.tsx +++ b/src/components/onboarding/VerifierIdentityStep.tsx @@ -110,7 +110,11 @@ export function VerifierIdentityStep({ let file: File; try { - file = await fetchImageAsFile(url, config.imageProxy); + file = await fetchImageAsFile( + url, + config.imageProxy, + field === 'banner' ? 1500 : 1024, + ); } catch (error) { toast({ title: t('onboarding.verifier.identity.pasteUrlFetchFailed'), diff --git a/src/lib/proxyImageUrl.ts b/src/lib/proxyImageUrl.ts index 1b0d1034..6e1bf021 100644 --- a/src/lib/proxyImageUrl.ts +++ b/src/lib/proxyImageUrl.ts @@ -80,17 +80,21 @@ export function proxyImageUrl( * @param proxyBaseUrl Base URL of a wsrv.nl-compatible proxy. Falls back to * `https://wsrv.nl` when empty, since a direct fetch of an * arbitrary origin would almost always fail CORS. + * @param width Target raster width in px. Small or vector (SVG) + * sources are *enlarged* to this so the cropper has a + * usable canvas instead of a tiny speck. Defaults to 1024. * @param filename Base filename for the returned `File`. */ export async function fetchImageAsFile( src: string, proxyBaseUrl: string, + width = 1024, filename = 'pasted-image', ): Promise { // data: URIs carry their own bytes — fetch directly, no proxy, no CORS. const fetchUrl = src.startsWith('data:') ? src - : proxyFetchUrl(src, proxyBaseUrl || 'https://wsrv.nl'); + : proxyFetchUrl(src, proxyBaseUrl || 'https://wsrv.nl', width); const res = await fetch(fetchUrl); if (!res.ok) { @@ -111,9 +115,11 @@ export async function fetchImageAsFile( /** * Build a proxy URL for *fetching bytes* (as opposed to an ``). * Unlike {@link proxyImageUrl} this forces SVGs through the proxy so they - * rasterize, and omits the width cap so the cropper gets full resolution. + * rasterize, and requests a target `width` so small or vector sources are + * enlarged to a usable crop resolution rather than handed to the cropper at + * a tiny intrinsic size. */ -function proxyFetchUrl(src: string, proxyBaseUrl: string): string { +function proxyFetchUrl(src: string, proxyBaseUrl: string, width: number): string { let parsedProxy: URL; try { parsedProxy = new URL(proxyBaseUrl); @@ -123,7 +129,15 @@ function proxyFetchUrl(src: string, proxyBaseUrl: string): string { if (parsedProxy.protocol !== 'https:') return src; const base = (parsedProxy.origin + parsedProxy.pathname).replace(/\/+$/, ''); - const params = new URLSearchParams({ url: src, output: 'png' }); + // `w` resizes to the target width. wsrv.nl enlarges smaller sources by + // default (no `we` flag), so a 32px SVG icon becomes a `width`-wide raster + // the cropper can actually work with. `fit=inside` preserves aspect ratio. + const params = new URLSearchParams({ + url: src, + output: 'png', + w: String(width), + fit: 'inside', + }); return `${base}/?${params.toString()}`; }