The picture is capped at 256 and Gravatar is asked for no more: the profile page already wants 288 #265
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Postulo/postulo#265
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
accounts/avatars.pynormalises every picture to exactly 256×256:AVATAR_SIZE = 256,decoded, straightened, cropped square with
ImageOps.fit, re-encoded as PNG. The Gravatarcopy is fetched at the same size —
gravatar_urlpasses?s=256from the same constant.The same reasoning as #264 applies, and here one part of it is not hypothetical.
The profile page is already short of pixels
accounts/profile.html:43renders{% avatar user "size-24 text-3xl" %}. That is 96 CSSpixels. On a phone at 3× device pixel ratio the browser wants 288 physical pixels and we
store 256, so the picture on the one page whose subject is the picture is being upscaled
today.
This is not the company logo's situation. There the largest surface is
size-14— 56 CSSpixels, 168 at 3×, comfortably inside 256 (#264 records that, and that the change there is
for surfaces not yet built). Here the ceiling is already touching.
Decided
Bounded by file size, not by dimensions, exactly as #264 settles it for logos:
AVATAR_SIZEstops being the output dimension;MAX_UPLOAD_BYTES(already 5 MB, and already the number #264 proposes aligning logos to)stays as the input cap, refusing before decode;
MAX_PIXELSstays. As on #264: it bounds what Pillow allocates while decoding, notwhat is stored. It is the decompression bomb guard and is untouched by a byte budget.
Ask Gravatar for more.
gravatar_url(email, size=AVATAR_SIZE)currently requests?s=256. Gravatar serves up to 2048, the fetch happens once, server-side, on opt-in, andd=404is unaffected. The requested size should follow whatever the new ceiling is ratherthan continuing to track a constant that no longer means anything. Cheap, and it is the
difference between a stored picture that can be shown large and one that never could.
The square crop stays.
ImageOps.fitrather thancontainis deliberate and correct: aface belongs cropped to the tile, the tile is square everywhere it appears, and the initials
fallback that stands in for a missing picture is a square tile too. Letterboxing a portrait
photograph inside that would look like a mistake. This differs from
jobs/logos.pyonpurpose — a wordmark must not be cropped, a face should be — and the two should not be
reconciled.
No SVG. #264's sanitiser is for logos. Nobody uploads a vector of their own face, so
extending it here would be attack surface bought for nothing. Explicitly out of scope, and
recorded so the two issues are not merged on the strength of both being about pictures.
Worth noting for later
No document theme or CV renders the person's photograph today —
documents/themes/,resume/andapplications/report_print.htmlhave none. But a European CV commonly carriesone, Postulo ships a
europassplugin, and a Europass CV carries one. A passport-style35 × 45 mm photograph at 300 dpi is about 413 × 531 pixels.
So if that is ever built, 256 was never going to be enough, and by then the originals are
gone — only the processed PNG is kept. That is the same trap #264 describes, and the reason
to lift the ceiling before rather than after.
Existing pictures stay 256² for the same reason: they can only be replaced by uploading
again, or — for a Gravatar copy — by re-fetching, which a raised
?s=makes worthwhile onits own.