import { PHOTO_MAX_PX, PHOTO_MAX_BYTES } from "../constants"; import { log } from "./log"; /* * Een gekozen bestand klaarmaken om mee te sturen met een brief. * * De foto wordt hier, op het toestel zelf, verkleind en opnieuw als JPEG gecodeerd. Dat is niet * alleen om de bytes: een opnieuw gecodeerde JPEG bevat geen EXIF meer, en daar zitten normaal de * gps-coördinaten in van waar de foto genomen is. Die verlaten het toestel dus nooit. De server * krijgt alleen nog pixels, en hoeft niets te bewerken. * * Geeft { data (base64), mime, width, height, previewUrl } — previewUrl is voor het voorbeeld in de * schrijfdialoog en moet met URL.revokeObjectURL() worden opgeruimd. */ /* Kwaliteitsstappen: net zo lang zakken tot de foto onder de limiet blijft */ const QUALITIES = [0.85, 0.75, 0.65, 0.5]; export async function preparePhoto(file) { if (!file.type.startsWith("image/")) throw new Error("Kies een afbeelding"); // imageOrientation: de draaiing uit de EXIF wordt in de pixels verwerkt vóór die EXIF verdwijnt, // anders zou een staande telefoonfoto liggend aankomen let bitmap; try { bitmap = await createImageBitmap(file, { imageOrientation: "from-image" }); } catch (e) { /* * De zin hierboven is wat de schrijver eraan heeft; wat de browser zei is wat wíj eraan hebben. * Een bestandsformaat dat deze webview niet kent (HEIC bijvoorbeeld) ziet er op het scherm uit * als elke andere onleesbare foto, en dat verschil is alleen hier nog te zien. */ log("✗ createImageBitmap", `type=${file.type}`, `bytes=${file.size}`, e?.message ?? String(e)); throw new Error("Deze afbeelding kon niet gelezen worden"); } const scale = Math.min(1, PHOTO_MAX_PX / Math.max(bitmap.width, bitmap.height)); const width = Math.max(1, Math.round(bitmap.width * scale)); const height = Math.max(1, Math.round(bitmap.height * scale)); const canvas = document.createElement("canvas"); canvas.width = width; canvas.height = height; const ctx = canvas.getContext("2d"); ctx.imageSmoothingQuality = "high"; ctx.drawImage(bitmap, 0, 0, width, height); bitmap.close?.(); let blob = null; for (const quality of QUALITIES) { blob = await toBlob(canvas, quality); if (blob.size <= PHOTO_MAX_BYTES) break; } if (!blob || blob.size > PHOTO_MAX_BYTES) throw new Error("Deze foto blijft te groot — probeer een andere"); return { data: await toBase64(blob), mime: "image/jpeg", width, height, previewUrl: URL.createObjectURL(blob) }; } const toBlob = (canvas, quality) => new Promise((resolve, reject) => canvas.toBlob((b) => (b ? resolve(b) : reject(new Error("Deze foto kon niet verwerkt worden"))), "image/jpeg", quality) ); /* Blob → kale base64 (zonder het "data:image/jpeg;base64," ervoor) */ const toBase64 = (blob) => new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(String(reader.result).split(",")[1]); reader.onerror = () => reject(new Error("Deze foto kon niet gelezen worden")); reader.readAsDataURL(blob); });