import { OrientationTypes, type ExifData } from 'ts-exif-parser'; import { formatNumberToFraction, roundToString } from './number'; import * as PiExif from 'piexifjs'; const OFFSET_REGEX = /[+-]\d\d:\d\d/; export const getOffsetFromExif = (data: ExifData) => Object.values(data.tags as any) .find((value: any) => typeof value === 'string' && OFFSET_REGEX.test(value) ) as string | undefined; export const getAspectRatioFromExif = (data: ExifData): number => { // Using '||' operator to handle `Orientation` unexpectedly being '0' const orientation = data.tags?.Orientation || OrientationTypes.TOP_LEFT; const width = data.imageSize?.width ?? 3.0; const height = data.imageSize?.height ?? 2.0; switch (orientation) { case OrientationTypes.TOP_LEFT: case OrientationTypes.TOP_RIGHT: case OrientationTypes.BOTTOM_RIGHT: case OrientationTypes.BOTTOM_LEFT: case OrientationTypes.LEFT_TOP: case OrientationTypes.RIGHT_BOTTOM: return width / height; case OrientationTypes.RIGHT_TOP: case OrientationTypes.LEFT_BOTTOM: return height / width; } }; export const formatAperture = (aperture?: number) => aperture ? `ƒ/${roundToString(aperture)}` : undefined; export const formatIso = (iso?: number) => iso ? `ISO ${iso.toLocaleString()}` : undefined; export const formatExposureTime = (exposureTime = 0) => exposureTime > 0 ? exposureTime < 1 ? `1/${Math.floor(1 / exposureTime)}s` : `${exposureTime}s` : undefined; export const formatExposureCompensation = (exposureCompensation?: number) => { if ( exposureCompensation && Math.abs(exposureCompensation) > 0.01 ) { return `${formatNumberToFraction(exposureCompensation)}ev`; } else { return undefined; } }; export const removeGpsFromFile = async ( fileBytes: ArrayBuffer ): Promise => { const base64 = Buffer.from(fileBytes).toString('base64'); const base64Url = `data:image/jpeg;base64,${base64}`; const exifObject = PiExif.load(base64Url) as Record; delete exifObject.GPS; const exifDataWithoutGps = PiExif.dump(exifObject); const data = PiExif.insert( exifDataWithoutGps, base64Url, ); return fetch(data, { cache: 'no-store' }).then(res => res.blob()); };