mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
- Add ensureSharpCompat() helper for automatic HEIC detection and decode - Fix HEIC support in all 14 custom-route tools (image-to-pdf, split, barcode-read, compose, collage, stitch, compare, find-duplicates, color-palette, watermark-image, vectorize, favicon, info, branding) - Fix PdfPagePreview using store's decoded blobUrl instead of raw File - Add onError fallback in ImageViewer for unrenderable formats - Fix image-to-pdf progress bar with flushSync for reliable rendering - Add ExifTool backend for edit-metadata (GPS, keywords, IPTC, dates) - Rename Strip Metadata to Remove Metadata with interactive Leaflet map - Fix user-files thumbnail generation for stored HEIC files - Fix info tool stats() histogram for HEIC via decoded buffer - Skip HEIC preprocessing in batch route for metadata tools
116 lines
3.2 KiB
TypeScript
116 lines
3.2 KiB
TypeScript
/** Human-friendly labels for common EXIF keys */
|
|
export const EXIF_LABELS: Record<string, string> = {
|
|
Make: "Camera Make",
|
|
Model: "Camera Model",
|
|
Software: "Software",
|
|
DateTime: "Date/Time",
|
|
ModifyDate: "Date Modified",
|
|
DateTimeOriginal: "Date Taken",
|
|
CreateDate: "Date Created",
|
|
DateTimeDigitized: "Date Digitized",
|
|
ExposureTime: "Exposure Time",
|
|
FNumber: "F-Number",
|
|
ISO: "ISO",
|
|
ISOSpeedRatings: "ISO",
|
|
FocalLength: "Focal Length",
|
|
FocalLengthIn35mmFormat: "Focal Length (35mm)",
|
|
FocalLengthIn35mmFilm: "Focal Length (35mm)",
|
|
ExposureCompensation: "Exposure Bias",
|
|
ExposureBiasValue: "Exposure Bias",
|
|
MeteringMode: "Metering Mode",
|
|
Flash: "Flash",
|
|
WhiteBalance: "White Balance",
|
|
ExposureMode: "Exposure Mode",
|
|
SceneCaptureType: "Scene Type",
|
|
Contrast: "Contrast",
|
|
Saturation: "Saturation",
|
|
Sharpness: "Sharpness",
|
|
DigitalZoomRatio: "Digital Zoom",
|
|
ImageWidth: "Width",
|
|
ImageHeight: "Height",
|
|
ImageLength: "Height",
|
|
Orientation: "Orientation",
|
|
XResolution: "X Resolution",
|
|
YResolution: "Y Resolution",
|
|
ResolutionUnit: "Resolution Unit",
|
|
ColorSpace: "Color Space",
|
|
ExifImageWidth: "Pixel Width",
|
|
ExifImageHeight: "Pixel Height",
|
|
PixelXDimension: "Pixel Width",
|
|
PixelYDimension: "Pixel Height",
|
|
Artist: "Artist",
|
|
Copyright: "Copyright",
|
|
ImageDescription: "Description",
|
|
LensMake: "Lens Make",
|
|
LensModel: "Lens Model",
|
|
LensInfo: "Lens Info",
|
|
BodySerialNumber: "Body Serial",
|
|
CameraOwnerName: "Camera Owner",
|
|
// IPTC
|
|
ObjectName: "Title",
|
|
Headline: "Headline",
|
|
Keywords: "Keywords",
|
|
City: "City",
|
|
"Province-State": "State/Province",
|
|
"Country-PrimaryLocationName": "Country",
|
|
CopyrightNotice: "Copyright Notice",
|
|
"By-line": "Creator",
|
|
Caption: "Caption",
|
|
// XMP
|
|
Subject: "Subject/Keywords",
|
|
Title: "Title",
|
|
Description: "Description",
|
|
Creator: "Creator",
|
|
Rights: "Rights",
|
|
};
|
|
|
|
/** Keys to skip in display (internal/binary/redundant) */
|
|
export const SKIP_KEYS = new Set([
|
|
"ExifToolVersion",
|
|
"FileName",
|
|
"Directory",
|
|
"FileSize",
|
|
"FileModifyDate",
|
|
"FileAccessDate",
|
|
"FileInodeChangeDate",
|
|
"FilePermissions",
|
|
"FileType",
|
|
"FileTypeExtension",
|
|
"MIMEType",
|
|
"SourceFile",
|
|
"ExifByteOrder",
|
|
"ThumbnailImage",
|
|
"ThumbnailOffset",
|
|
"ThumbnailLength",
|
|
"PreviewImage",
|
|
"MakerNote",
|
|
"PrintImageMatching",
|
|
]);
|
|
|
|
export function formatExifValue(key: string, value: unknown): string {
|
|
if (value === null || value === undefined) return "N/A";
|
|
if (typeof value === "string") return value;
|
|
if (typeof value === "number") {
|
|
if (key === "ExposureTime" && value > 0 && value < 1) {
|
|
return `1/${Math.round(1 / value)}s`;
|
|
}
|
|
if (key === "FNumber") return `f/${value}`;
|
|
if (key === "FocalLength" || key === "FocalLengthIn35mmFormat") return `${value}mm`;
|
|
return String(value);
|
|
}
|
|
if (Array.isArray(value)) {
|
|
if (value.length <= 6) {
|
|
return value.map(String).join(", ");
|
|
}
|
|
return `[${value.length} values]`;
|
|
}
|
|
return String(value);
|
|
}
|
|
|
|
export function exifStr(exif: Record<string, unknown> | null | undefined, key: string): string {
|
|
const v = exif?.[key];
|
|
if (typeof v === "string") return v;
|
|
if (typeof v === "number") return String(v);
|
|
return "";
|
|
}
|