### Install Photobooth Frame Generator Source: https://github.com/ahdja/photobooth-frame-generator/blob/main/README.md Install the package using npm. This is the first step before using the engine. ```sh npm install @kotaksurat/photobooth-frame-generator ``` -------------------------------- ### Run Example with Vite Source: https://github.com/ahdja/photobooth-frame-generator/blob/main/README.md Serve the example implementation locally using Vite. Open the provided local link in your browser to interact with the demo. ```sh npx vite dev example ``` -------------------------------- ### Photobooth Frame Generator TypeScript Types Source: https://context7.com/ahdja/photobooth-frame-generator/llms.txt Reference for exported types used in TypeScript projects, including image sources, configuration options, slot definitions, and result types. Examples show the structure of `Slot` and `PhotoboothConfig` objects. ```typescript import type { ImageSource, PhotoboothConfig, Slot, SlotPhotoAssignment, RenderResult, SlotDetectionResult, } from '@kotaksurat/photobooth-frame-generator'; // Slot properties const slot: Slot = { cx: 400, // Center X in pixels cy: 300, // Center Y in pixels width: 280, // Bounding box width in pixels height: 210, // Bounding box height in pixels angle: 0.05, // Rotation in radians (small positive = slight clockwise tilt) }; // Full config shape with all defaults shown const config: PhotoboothConfig = { alphaThreshold: 10, minSlotSize: 50, outputFormat: 'image/png', quality: 0.92, fillEmptySlots: true, slotExpansion: 5, crossOrigin: 'anonymous', }; ``` -------------------------------- ### Detect Transparent Slots in a Frame Source: https://context7.com/ahdja/photobooth-frame-generator/llms.txt Use `detectSlots` to analyze a frame's alpha channel and get slot positions, sizes, and rotations. This is useful for UI previews or validating slot indices before compositing. ```typescript import { PhotoboothFrameGenerator, SlotDetectionResult, Slot } from '@kotaksurat/photobooth-frame-generator'; const engine = new PhotoboothFrameGenerator({ alphaThreshold: 10, minSlotSize: 50 }); async function previewSlots(frameFile: File): Promise { try { const result: SlotDetectionResult = await engine.detectSlots(frameFile); console.log(`Frame: ${result.frameWidth}x${result.frameHeight}`); console.log(`Found ${result.slots.length} slot(s):`); result.slots.forEach((slot: Slot, i: number) => { const angleDeg = (slot.angle * 180 / Math.PI).toFixed(1); console.log( ` Slot ${i}: center=(${slot.cx.toFixed(1)}, ${slot.cy.toFixed(1)}), ` + `size=${slot.width.toFixed(1)}x${slot.height.toFixed(1)}, ` + `angle=${angleDeg}°` ); // Example output: // Slot 0: center=(320.0, 180.0), size=280.0x210.0, angle=0.0° // Slot 1: center=(320.0, 480.0), size=280.0x210.0, angle=0.0° }); // Build a simple slot-picker UI const select = document.createElement('select'); result.slots.forEach((_, i) => { const opt = document.createElement('option'); opt.value = String(i); opt.textContent = `Slot ${i + 1}`; select.appendChild(opt); }); document.body.appendChild(select); } catch (error) { console.error('Slot detection failed:', error); } finally { engine.reset(); } } ``` -------------------------------- ### Detect Slots in a Frame Image Source: https://github.com/ahdja/photobooth-frame-generator/blob/main/README.md Use `detectSlots` to get the coordinates and dimensions of transparent slots within a frame image without processing user photos. Remember to call `engine.reset()` after use. ```typescript import { PhotoboothFrameGenerator } from 'photobooth-frame-generator'; const engine = new PhotoboothFrameGenerator(); const result = await engine.detectSlots(frameImage); console.log(`Frame: ${result.frameWidth}x${result.frameHeight}`); console.log(`Found ${result.slots.length} slot(s):`); result.slots.forEach((slot, i) => { console.log(` Slot ${i + 1}: center (${slot.cx.toFixed(1)}, ${slot.cy.toFixed(1)}), ` + `size ${slot.width.toFixed(1)}x${slot.height.toFixed(1)}, ` + `angle ${(slot.angle * 180 / Math.PI).toFixed(1)}°`); }); engine.reset(); ``` -------------------------------- ### Initialize and Create Image Source: https://github.com/ahdja/photobooth-frame-generator/blob/main/README.md Initializes the engine with output options and generates a composite image by placing user photos into a frame. It automatically detects transparent slots in the frame and fits the photos. The result includes the data URL of the generated image and the number of slots found. Remember to call `reset()` to clean up memory. ```APIDOC ## Initialize and Create Image ### Description Initializes the engine with output options and generates a composite image by placing user photos into a frame. It automatically detects transparent slots in the frame and fits the photos. The result includes the data URL of the generated image and the number of slots found. Remember to call `reset()` to clean up memory. ### Method `new PhotoboothFrameGenerator(options?: { outputFormat?: string; quality?: number })` `engine.create(frameFile: File, userPhotos: File[]): Promise<{ dataUrl: string; slotsFound: number }>` `engine.reset(): void` ### Parameters #### Constructor Options - **outputFormat** (string) - Optional - The desired output format (e.g., 'image/jpeg', 'image/png'). Defaults to 'image/png'. - **quality** (number) - Optional - The quality setting for JPEG output (0 to 1). Defaults to 0.95. #### `create` Method Parameters - **frameFile** (File) - Required - The template frame image file. - **userPhotos** (File[]) - Required - An array of user photo files to place into the frame. ### Request Example ```typescript import { PhotoboothFrameGenerator } from 'photobooth-frame-generator'; const engine = new PhotoboothFrameGenerator({ outputFormat: 'image/jpeg', quality: 0.95 }); async function processPhotos(frameFile: File, userPhotos: File[]) { try { const result = await engine.create(frameFile, userPhotos); console.log(`Generated image with ${result.slotsFound} slots!`); const img = new Image(); img.src = result.dataUrl; document.body.appendChild(img); } catch (error) { console.error('Processing failed:', error); } finally { engine.reset(); } } ``` ### Response #### Success Response - **dataUrl** (string) - The data URL of the generated composite image. - **slotsFound** (number) - The number of transparent slots detected and filled in the frame. ``` -------------------------------- ### new PhotoboothFrameGenerator(config?) Source: https://context7.com/ahdja/photobooth-frame-generator/llms.txt Instantiates the engine with an optional configuration object. The engine instance is reusable across multiple create/detectSlots calls. ```APIDOC ## new PhotoboothFrameGenerator(config?) ### Description Instantiates the engine with an optional configuration object. All options are optional and fall back to sensible defaults. The engine instance is reusable across multiple `create` / `detectSlots` calls. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Configuration Options - **alphaThreshold** (number) - Optional - Pixels with alpha < 15 are treated as transparent (default: 10) - **minSlotSize** (number) - Optional - Ignore transparent regions narrower than 80px (default: 50) - **outputFormat** (string) - Optional - 'image/png' | 'image/jpeg' | 'image/webp' (default: 'image/png') - **quality** (number) - Optional - JPEG/WebP quality 0.0–1.0 (default: 0.92) - **fillEmptySlots** (boolean) - Optional - Loop photos to fill all slots if fewer photos than slots (default: true) - **slotExpansion** (number) - Optional - Expand each slot edge by 5px to cover anti-aliasing gaps (default: 5) - **crossOrigin** (string | null) - Optional - img.crossOrigin for remote URLs — set null to disable (default: 'anonymous') ### Request Example ```typescript import { PhotoboothFrameGenerator } from '@kotaksurat/photobooth-frame-generator'; // Default configuration const engine = new PhotoboothFrameGenerator(); // Custom configuration const engineCustom = new PhotoboothFrameGenerator({ alphaThreshold: 15, minSlotSize: 80, outputFormat: 'image/jpeg', quality: 0.95, fillEmptySlots: true, slotExpansion: 5, crossOrigin: 'anonymous', }); ``` ### Response None directly from constructor. ``` -------------------------------- ### Instantiate PhotoboothFrameGenerator with Default Configuration Source: https://context7.com/ahdja/photobooth-frame-generator/llms.txt Instantiate the engine with default configuration options. The engine instance can be reused. ```typescript import { PhotoboothFrameGenerator } from '@kotaksurat/photobooth-frame-generator'; // Default configuration const engine = new PhotoboothFrameGenerator(); ``` -------------------------------- ### Instantiate PhotoboothFrameGenerator with Custom Configuration Source: https://context7.com/ahdja/photobooth-frame-generator/llms.txt Instantiate the engine with custom configuration options to control transparency threshold, minimum slot size, output format, quality, photo filling behavior, slot expansion, and cross-origin settings for remote images. ```typescript import { PhotoboothFrameGenerator } from '@kotaksurat/photobooth-frame-generator'; // Custom configuration const engineCustom = new PhotoboothFrameGenerator({ alphaThreshold: 15, // Pixels with alpha < 15 are treated as transparent (default: 10) minSlotSize: 80, // Ignore transparent regions narrower than 80px (default: 50) outputFormat: 'image/jpeg', // 'image/png' | 'image/jpeg' | 'image/webp' (default: 'image/png') quality: 0.95, // JPEG/WebP quality 0.0–1.0 (default: 0.92) fillEmptySlots: true, // Loop photos to fill all slots if fewer photos than slots (default: true) slotExpansion: 5, // Expand each slot edge by 5px to cover anti-aliasing gaps (default: 5) crossOrigin: 'anonymous', // img.crossOrigin for remote URLs — set null to disable (default: 'anonymous') }); ``` -------------------------------- ### Basic Photo Processing with Photobooth Frame Generator Source: https://github.com/ahdja/photobooth-frame-generator/blob/main/README.md Initialize the engine and process user photos into a frame. Ensure to call `engine.reset()` in a finally block to clean up memory. ```typescript import { PhotoboothFrameGenerator } from 'photobooth-frame-generator'; // 1. Initialize the engine const engine = new PhotoboothFrameGenerator({ outputFormat: 'image/jpeg', quality: 0.95 }); async function processPhotos(frameFile: File, userPhotos: File[]) { try { // 2. Generate the result const result = await engine.create(frameFile, userPhotos); console.log(`Generated image with ${result.slotsFound} slots!`); // Use result.dataUrl as the src for an HTML Image element const img = new Image(); img.src = result.dataUrl; document.body.appendChild(img); } catch (error) { console.error('Processing failed:', error); } finally { // 3. Clean up memory to avoid blobs piling up engine.reset(); } } ``` -------------------------------- ### Run Unit Tests with Vitest Source: https://github.com/ahdja/photobooth-frame-generator/blob/main/README.md Execute the entire test suite using npm and Vitest. This command discovers and runs assertion scripts within the test/ directory. ```sh npm run test ``` -------------------------------- ### engine.createWithAssignments Source: https://context7.com/ahdja/photobooth-frame-generator/llms.txt Places photos into specific slots by zero-based index. Explicitly assigned photos always stay on their designated slot; remaining empty slots are filled sequentially from fallbackPhotos. If fallbackPhotos is omitted, unassigned slots are left transparent. ```APIDOC ## `engine.createWithAssignments(frameSource, assignments, fallbackPhotos?)` ### Description Places photos into specific slots by zero-based index. Explicitly assigned photos always stay on their designated slot; remaining empty slots are filled sequentially from `fallbackPhotos`. If `fallbackPhotos` is omitted, unassigned slots are left transparent (use `image/png` or `image/webp` to preserve transparency). ### Parameters #### Path Parameters - **frameSource** (File | Blob | string) - Required - The source of the frame image. - **assignments** (SlotPhotoAssignment[]) - Required - An array of objects, where each object specifies a `slotIndex` and the `photo` to be placed in that slot. - **fallbackPhotos** (File[] | Blob[] | string[]) - Optional - An array of photos to fill any remaining slots not covered by `assignments`. ### Request Example ```typescript import { PhotoboothFrameGenerator, SlotPhotoAssignment } from '@kotaksurat/photobooth-frame-generator'; const engine = new PhotoboothFrameGenerator({ outputFormat: 'image/png' }); async function assignedComposition( frameFile: File, featuredPhoto: File, otherPhotos: File[] ): Promise { try { const assignments: SlotPhotoAssignment[] = [ { slotIndex: 2, photo: featuredPhoto } ]; const result = await engine.createWithAssignments(frameFile, assignments, otherPhotos); console.log(`Done! ${result.slotsFound} slots. Featured photo is at slot 2.`); const img = new Image(); img.src = result.dataUrl; document.body.appendChild(img); } catch (error) { console.error(error); } finally { engine.reset(); } } ``` ### Response #### Success Response (object) - **dataUrl** (string) - A data URL representing the generated image. - **slotsFound** (number) - The total number of slots detected in the frame. #### Error Handling - Throws if `slotIndex` is out of range or no slots were detected. ``` -------------------------------- ### engine.create(frameSource, userPhotos) Source: https://context7.com/ahdja/photobooth-frame-generator/llms.txt The primary method for compositing photos into a frame. Detects all transparent slots in the frame, then fills them sequentially with the provided photos (top-to-bottom, left-to-right). If `fillEmptySlots` is `true` and there are fewer photos than slots, photos are cycled. ```APIDOC ## engine.create(frameSource, userPhotos) ### Description The primary method for compositing photos into a frame. Detects all transparent slots in the frame, then fills them sequentially with the provided photos (top-to-bottom, left-to-right). If `fillEmptySlots` is `true` and there are fewer photos than slots, photos are cycled. Returns a `RenderResult` containing the composed image as a data URL. ### Method POST (conceptual, as it's a library method) ### Endpoint N/A (Client-side library method) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **frameSource** (File | string | URL) - Required - The frame template image. Accepts `File` objects, Base64 data URLs, or remote image URL strings. - **userPhotos** (Array) - Required - An array of user photos to composite into the frame. Accepts `File` objects, Base64 data URLs, or remote image URL strings. ### Request Example ```typescript import { PhotoboothFrameGenerator, RenderResult } from '@kotaksurat/photobooth-frame-generator'; const engine = new PhotoboothFrameGenerator({ outputFormat: 'image/jpeg', quality: 0.95, }); async function generatePhotobooth(frameFile: File, photoFiles: File[]): Promise { try { const result: RenderResult = await engine.create(frameFile, photoFiles); // result.dataUrl — base64-encoded image string, use as // result.slotsFound — number of transparent slots detected in the frame // result.width — output image width in pixels // result.height — output image height in pixels console.log(`Generated! ${result.slotsFound} slots filled. Size: ${result.width}x${result.height}`); const img = document.createElement('img'); img.src = result.dataUrl; document.body.appendChild(img); // Optionally trigger download const a = document.createElement('a'); a.href = result.dataUrl; a.download = 'photobooth-result.jpg'; a.click(); } catch (error) { console.error('Generation failed:', error); } finally { engine.reset(); // Always clean up blob URLs } } // Usage with file inputs const frameInput = document.getElementById('frame') as HTMLInputElement; const photosInput = document.getElementById('photos') as HTMLInputElement; frameInput.addEventListener('change', async () => { const photoFiles = Array.from(photosInput.files ?? []); if (frameInput.files?.[0] && photoFiles.length > 0) { await generatePhotobooth(frameInput.files[0], photoFiles); } }); ``` ### Response #### Success Response (200) - **dataUrl** (string) - Base64-encoded image string. - **slotsFound** (number) - The number of transparent slots detected in the frame. - **width** (number) - The output image width in pixels. - **height** (number) - The output image height in pixels. #### Response Example ```json { "dataUrl": "data:image/jpeg;base64,/...", "slotsFound": 4, "width": 800, "height": 600 } ``` ``` -------------------------------- ### Generate Photobooth Image with Sequential Photo Filling Source: https://context7.com/ahdja/photobooth-frame-generator/llms.txt Composites photos into a frame by detecting transparent slots and filling them sequentially. Handles fewer photos than slots by cycling photos if fillEmptySlots is true. The result includes the data URL, number of slots found, and image dimensions. Remember to call engine.reset() to clean up. ```typescript import { PhotoboothFrameGenerator, RenderResult } from '@kotaksurat/photobooth-frame-generator'; const engine = new PhotoboothFrameGenerator({ outputFormat: 'image/jpeg', quality: 0.95, }); async function generatePhotobooth(frameFile: File, photoFiles: File[]): Promise { try { const result: RenderResult = await engine.create(frameFile, photoFiles); // result.dataUrl — base64-encoded image string, use as // result.slotsFound — number of transparent slots detected in the frame // result.width — output image width in pixels // result.height — output image height in pixels console.log(`Generated! ${result.slotsFound} slots filled. Size: ${result.width}x${result.height}`); const img = document.createElement('img'); img.src = result.dataUrl; document.body.appendChild(img); // Optionally trigger download const a = document.createElement('a'); a.href = result.dataUrl; a.download = 'photobooth-result.jpg'; a.click(); } catch (error) { console.error('Generation failed:', error); } finally { engine.reset(); // Always clean up blob URLs } } // Usage with file inputs const frameInput = document.getElementById('frame') as HTMLInputElement; const photosInput = document.getElementById('photos') as HTMLInputElement; frameInput.addEventListener('change', async () => { const photoFiles = Array.from(photosInput.files ?? []); if (frameInput.files?.[0] && photoFiles.length > 0) { await generatePhotobooth(frameInput.files[0], photoFiles); } }); ``` -------------------------------- ### Assign Photos to Specific Slots Source: https://context7.com/ahdja/photobooth-frame-generator/llms.txt Use `createWithAssignments` to place photos into designated slots by index. Unassigned slots are filled from fallback photos or remain transparent if omitted. Ensure `outputFormat` supports transparency if needed. ```typescript import { PhotoboothFrameGenerator, SlotPhotoAssignment } from '@kotaksurat/photobooth-frame-generator'; const engine = new PhotoboothFrameGenerator({ outputFormat: 'image/png' }); async function assignedComposition( frameFile: File, featuredPhoto: File, otherPhotos: File[] ): Promise { try { // Pin the featured photo to slot index 2 (the 3rd detected slot) const assignments: SlotPhotoAssignment[] = [ { slotIndex: 2, photo: featuredPhoto } ]; // otherPhotos fill the remaining slots (slots 0, 1, 3, 4...) in order const result = await engine.createWithAssignments(frameFile, assignments, otherPhotos); console.log(`Done! ${result.slotsFound} slots. Featured photo is at slot 2.`); const img = new Image(); img.src = result.dataUrl; document.body.appendChild(img); } catch (error) { // Throws if slotIndex is out of range or no slots were detected // e.g., "Invalid slotIndex 5. Only 3 slot(s) were detected, so the valid range is 0 to 2." console.error(error); } finally { engine.reset(); } } // Fill only one slot, leave the rest transparent async function singleSlotOnly(frameFile: File, photo: File): Promise { const engine2 = new PhotoboothFrameGenerator({ outputFormat: 'image/png' }); try { // No fallbackPhotos — all other slots remain transparent const result = await engine2.createWithAssignments( frameFile, [{ slotIndex: 0, photo }] // fallbackPhotos omitted ); console.log('Single slot result:', result.dataUrl.substring(0, 60) + '...'); } finally { engine2.reset(); } } ``` -------------------------------- ### CORS Handling for Remote Image URLs Source: https://context7.com/ahdja/photobooth-frame-generator/llms.txt Guidance on handling Cross-Origin Resource Sharing (CORS) when using remote image URLs as input. ```APIDOC ## CORS Handling for Remote Image URLs When passing remote URL strings as `ImageSource`, the image server must include `Access-Control-Allow-Origin` headers. The engine sets `crossOrigin: 'anonymous'` by default to prevent a tainted canvas. Set `crossOrigin: null` to opt out. ```typescript // Remote URL — server must send Access-Control-Allow-Origin: * const engineRemote = new PhotoboothFrameGenerator({ crossOrigin: 'anonymous' }); // default const result = await engineRemote.create('https://cdn.example.com/frame.png', [ 'https://cdn.example.com/photo1.jpg', 'https://cdn.example.com/photo2.jpg', ]); // Same-origin / File / data URL — no CORS concerns, disable crossOrigin const engineLocal = new PhotoboothFrameGenerator({ crossOrigin: null }); const localResult = await engineLocal.create(frameFile, [ 'data:image/jpeg;base64,/9j/4AAQSkZJRgAB...', ]); // If CORS fails, the error message is descriptive: // "Failed to export canvas (likely CORS/tainted canvas). If you pass image URLs, // ensure the image host allows CORS (Access-Control-Allow-Origin)..." ``` ``` -------------------------------- ### Create Image with Specific Slot Assignments Source: https://github.com/ahdja/photobooth-frame-generator/blob/main/README.md Generates a composite image by assigning specific user photos to designated slots within the frame. Unassigned photos fill the remaining slots. This method provides more control over photo placement compared to the basic `create` method. You can optionally provide fallback photos for any remaining empty slots. ```APIDOC ## Create Image with Specific Slot Assignments ### Description Generates a composite image by assigning specific user photos to designated slots within the frame. Unassigned photos fill the remaining slots. This method provides more control over photo placement compared to the basic `create` method. You can optionally provide fallback photos for any remaining empty slots. ### Method `engine.createWithAssignments(frameFile: File, assignments: Array<{ slotIndex: number; photo: File }>, fallbackPhotos?: File[]): Promise<{ dataUrl: string; slotsFound: number }>` ### Parameters #### `createWithAssignments` Method Parameters - **frameFile** (File) - Required - The template frame image file. - **assignments** (Array<{ slotIndex: number; photo: File }>)- Required - An array of objects, where each object specifies the `slotIndex` (zero-based) and the `photo` (File) to be placed in that slot. - **fallbackPhotos** (File[]) - Optional - An array of photos to fill any remaining slots not covered by explicit assignments. ### Request Example ```typescript const result = await engine.createWithAssignments( frameFile, [{ slotIndex: 2, photo: featuredPhoto }], [backupPhoto1, backupPhoto2] ); // To not fill remaining slots automatically: const result = await engine.createWithAssignments( frameFile, [{ slotIndex: 2, photo: featuredPhoto }] ); ``` ### Response #### Success Response - **dataUrl** (string) - The data URL of the generated composite image. - **slotsFound** (number) - The number of transparent slots detected and filled in the frame. ### Behavior Notes - Assigned photos are placed in their requested slots. - Non-assigned photos fill the remaining slots in order. - `fillEmptySlots` (if applicable in a broader context, though not a direct parameter here) repeats non-assigned photos. - For ordered filling without specific assignments, use `create()`. - If only one slot is assigned and no `fallbackPhotos` are provided, other slots remain empty/transparent. - Use `image/png` or `image/webp` to preserve transparency in empty slots; `image/jpeg` does not support transparency. ``` -------------------------------- ### engine.detectSlots Source: https://context7.com/ahdja/photobooth-frame-generator/llms.txt Analyzes the frame's alpha channel and returns the position, size, and rotation of every detected transparent slot — without compositing any photos. Useful for rendering a slot-layout preview UI or validating slot indices before calling `createWithAssignments`. ```APIDOC ## `engine.detectSlots(frameSource)` ### Description Analyzes the frame's alpha channel and returns the position, size, and rotation of every detected transparent slot — without compositing any photos. Useful for rendering a slot-layout preview UI or validating slot indices before calling `createWithAssignments`. ### Parameters #### Path Parameters - **frameSource** (File | Blob | string) - Required - The source of the frame image. ### Request Example ```typescript import { PhotoboothFrameGenerator, SlotDetectionResult, Slot } from '@kotaksurat/photobooth-frame-generator'; const engine = new PhotoboothFrameGenerator({ alphaThreshold: 10, minSlotSize: 50 }); async function previewSlots(frameFile: File): Promise { try { const result: SlotDetectionResult = await engine.detectSlots(frameFile); console.log(`Frame: ${result.frameWidth}x${result.frameHeight}`); console.log(`Found ${result.slots.length} slot(s):`); result.slots.forEach((slot: Slot, i: number) => { const angleDeg = (slot.angle * 180 / Math.PI).toFixed(1); console.log( ` Slot ${i}: center=(${slot.cx.toFixed(1)}, ${slot.cy.toFixed(1)}), ` + `size=${slot.width.toFixed(1)}x${slot.height.toFixed(1)}, ` + `angle=${angleDeg}°` ); }); } catch (error) { console.error('Slot detection failed:', error); } finally { engine.reset(); } } ``` ### Response #### Success Response (SlotDetectionResult) - **frameWidth** (number) - The width of the frame. - **frameHeight** (number) - The height of the frame. - **slots** (Slot[]) - An array of detected slots. - **Slot** object properties: - **cx** (number) - The x-coordinate of the slot's center. - **cy** (number) - The y-coordinate of the slot's center. - **width** (number) - The width of the slot. - **height** (number) - The height of the slot. - **angle** (number) - The rotation angle of the slot in radians. ``` -------------------------------- ### Assign Photo to a Specific Slot Source: https://github.com/ahdja/photobooth-frame-generator/blob/main/README.md Use `createWithAssignments` to place a specific photo into a designated slot index. Optional fallback photos can fill remaining slots. ```typescript import { PhotoboothFrameGenerator } from 'photobooth-frame-generator'; const engine = new PhotoboothFrameGenerator(); const result = await engine.createWithAssignments( frameFile, [{ slotIndex: 2, photo: featuredPhoto }], [backupPhoto1, backupPhoto2] ); ``` -------------------------------- ### Detect Slots Only Source: https://github.com/ahdja/photobooth-frame-generator/blob/main/README.md Analyzes a frame image to detect and return the coordinates and dimensions of all transparent slots without processing any user photos. This is useful for previewing slot layouts or building custom UI elements for photo placement. ```APIDOC ## Detect Slots Only ### Description Analyzes a frame image to detect and return the coordinates and dimensions of all transparent slots without processing any user photos. This is useful for previewing slot layouts or building custom UI elements for photo placement. ### Method `engine.detectSlots(frameImage: File): Promise<{ frameWidth: number; frameHeight: number; slots: Slot[] }>` ### Parameters #### `detectSlots` Method Parameters - **frameImage** (File) - Required - The template frame image file. ### Response #### Success Response - **frameWidth** (number) - The width of the frame image in pixels. - **frameHeight** (number) - The height of the frame image in pixels. - **slots** (Slot[]) - An array of detected slot objects. ### Slot Object Structure Each `Slot` object contains the following properties: | Property | Type | Description | |----------|--------|------------------------------------------------| | `cx` | number | Center X coordinate in pixels | | `cy` | number | Center Y coordinate in pixels | | `width` | number | Bounding box width in pixels | | `height` | number | Bounding box height in pixels | | `angle` | number | Rotation angle in radians | ### Request Example ```typescript const result = await engine.detectSlots(frameImage); console.log(`Frame: ${result.frameWidth}x${result.frameHeight}`); console.log(`Found ${result.slots.length} slot(s):`); result.slots.forEach((slot, i) => { console.log(` Slot ${i + 1}: center (${slot.cx.toFixed(1)}, ${slot.cy.toFixed(1)}), ` + `size ${slot.width.toFixed(1)}x${slot.height.toFixed(1)}, ` + `angle ${(slot.angle * 180 / Math.PI).toFixed(1)}°`); }); engine.reset(); ``` ``` -------------------------------- ### TypeScript Types Reference Source: https://context7.com/ahdja/photobooth-frame-generator/llms.txt Reference for all exported TypeScript types available for use in your projects. ```APIDOC ## TypeScript Types Reference All exported types for use in TypeScript projects. ```typescript import type { ImageSource, // string (Base64 / URL) | File PhotoboothConfig, // Constructor options object Slot, // { cx, cy, width, height, angle } SlotPhotoAssignment, // { slotIndex: number; photo: ImageSource } RenderResult, // { dataUrl, slotsFound, width, height } SlotDetectionResult, // { slots: Slot[]; frameWidth: number; frameHeight: number } } from '@kotaksurat/photobooth-frame-generator'; // Slot properties const slot: Slot = { cx: 400, // Center X in pixels cy: 300, // Center Y in pixels width: 280, // Bounding box width in pixels height: 210, // Bounding box height in pixels angle: 0.05, // Rotation in radians (small positive = slight clockwise tilt) }; // Full config shape with all defaults shown const config: PhotoboothConfig = { alphaThreshold: 10, minSlotSize: 50, outputFormat: 'image/png', // | 'image/jpeg' | 'image/webp' quality: 0.92, fillEmptySlots: true, slotExpansion: 5, crossOrigin: 'anonymous', // | '' | 'use-credentials' | null }; ``` ``` -------------------------------- ### engine.reset() Source: https://context7.com/ahdja/photobooth-frame-generator/llms.txt Revokes all Blob/Object URLs created internally from File inputs, freeing memory. It's recommended to call this in a `finally` block after operations like `create`, `createWithAssignments`, or `detectSlots`. This method is safe to call multiple times. ```APIDOC ## `engine.reset()` ### Description Revokes all `Blob` / `Object` URLs created internally from `File` inputs, freeing memory held by the browser. Should always be called in a `finally` block after `create`, `createWithAssignments`, or `detectSlots` completes. Safe to call multiple times. ### Usage Examples **Pattern 1: Always reset in `finally`** ```typescript import { PhotoboothFrameGenerator } from '@kotaksurat/photobooth-frame-generator'; const engine = new PhotoboothFrameGenerator(); try { const result = await engine.create(frameFile, photos); displayResult(result.dataUrl); } catch (err) { handleError(err); } finally { engine.reset(); // Clears all tracked blob URLs → logs "PhotoboothFrameGenerator: Memory cleared." } ``` **Pattern 2: React `useEffect` cleanup** ```typescript import { useEffect, useRef } from 'react'; function PhotoboothWidget({ frameFile, photos }: { frameFile: File; photos: File[] }) { const engineRef = useRef(new PhotoboothFrameGenerator()); useEffect(() => { const engine = engineRef.current; engine.create(frameFile, photos).then(result => { // set state with result.dataUrl }); return () => { engine.reset(); // cleanup on unmount or re-render }; }, [frameFile, photos]); return null; } ``` ``` -------------------------------- ### Assign Photo to a Specific Slot Without Fallbacks Source: https://github.com/ahdja/photobooth-frame-generator/blob/main/README.md Assign a photo to a specific slot without providing fallback photos. Other slots will remain empty if not explicitly filled. ```typescript const result = await engine.createWithAssignments( frameFile, [{ slotIndex: 2, photo: featuredPhoto }] ); ``` -------------------------------- ### CORS Handling for Remote Image URLs Source: https://context7.com/ahdja/photobooth-frame-generator/llms.txt When using remote URLs as `ImageSource`, ensure the server includes `Access-Control-Allow-Origin` headers. The engine defaults to `crossOrigin: 'anonymous'` to prevent canvas tainting. Set `crossOrigin: null` to disable this for same-origin or data URLs. ```typescript // Remote URL — server must send Access-Control-Allow-Origin: * const engineRemote = new PhotoboothFrameGenerator({ crossOrigin: 'anonymous' }); // default const result = await engineRemote.create('https://cdn.example.com/frame.png', [ 'https://cdn.example.com/photo1.jpg', 'https://cdn.example.com/photo2.jpg', ]); // Same-origin / File / data URL — no CORS concerns, disable crossOrigin const engineLocal = new PhotoboothFrameGenerator({ crossOrigin: null }); const localResult = await engineLocal.create(frameFile, [ 'data:image/jpeg;base64,/9j/4AAQSkZJRgAB...', ]); // If CORS fails, the error message is descriptive: // "Failed to export canvas (likely CORS/tainted canvas). If you pass image URLs, // ensure the image host allows CORS (Access-Control-Allow-Origin)..." ``` -------------------------------- ### Resetting Engine Memory Source: https://context7.com/ahdja/photobooth-frame-generator/llms.txt Always call `engine.reset()` in a `finally` block after operations like `create` or `detectSlots` to free memory. It is safe to call multiple times. This pattern is also applicable in React component cleanup functions. ```typescript import { PhotoboothFrameGenerator } from '@kotaksurat/photobooth-frame-generator'; // Pattern 1: always reset in finally const engine = new PhotoboothFrameGenerator(); try { const result = await engine.create(frameFile, photos); displayResult(result.dataUrl); } catch (err) { handleError(err); } finally { engine.reset(); // Clears all tracked blob URLs → logs "PhotoboothFrameGenerator: Memory cleared." } ``` ```typescript import { useEffect, useRef } from 'react'; function PhotoboothWidget({ frameFile, photos }: { frameFile: File; photos: File[] }) { const engineRef = useRef(new PhotoboothFrameGenerator()); useEffect(() => { const engine = engineRef.current; engine.create(frameFile, photos).then(result => { // set state with result.dataUrl }); return () => { engine.reset(); // cleanup on unmount or re-render }; }, [frameFile, photos]); return null; } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.