Refine EXIF text formatting
This commit is contained in:
parent
883fa7eeb3
commit
6cd0d0b285
29
__tests__/exif.test.ts
Normal file
29
__tests__/exif.test.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { formatExposureCompensation, formatExposureTime } from '@/utility/exif';
|
||||||
|
|
||||||
|
describe('EXIF', () => {
|
||||||
|
describe('formats', () => {
|
||||||
|
it('exposure time', () => {
|
||||||
|
expect(formatExposureTime(0)).toBe(undefined);
|
||||||
|
expect(formatExposureTime(undefined)).toBe(undefined);
|
||||||
|
expect(formatExposureTime(0.5)).toBe('1/2s');
|
||||||
|
expect(formatExposureTime(0.1)).toBe('1/10s');
|
||||||
|
expect(formatExposureTime(0.01)).toBe('1/100s');
|
||||||
|
expect(formatExposureTime(1)).toBe('1s');
|
||||||
|
expect(formatExposureTime(1.5)).toBe('1.5s');
|
||||||
|
});
|
||||||
|
it('exposure compensation', () => {
|
||||||
|
expect(formatExposureCompensation(-1)).toBe('-1ev');
|
||||||
|
expect(formatExposureCompensation(0)).toBe(undefined);
|
||||||
|
expect(formatExposureCompensation(0.33)).toBe('+1/3ev');
|
||||||
|
expect(formatExposureCompensation(0.333)).toBe('+1/3ev');
|
||||||
|
expect(formatExposureCompensation(0.5)).toBe('+1/2ev');
|
||||||
|
expect(formatExposureCompensation(0.66)).toBe('+2/3ev');
|
||||||
|
expect(formatExposureCompensation(0.67)).toBe('+2/3ev');
|
||||||
|
expect(formatExposureCompensation(0.015625)).toBe('+1/64ev');
|
||||||
|
expect(formatExposureCompensation(-0.015625)).toBe('-1/64ev');
|
||||||
|
expect(formatExposureCompensation(1)).toBe('+1ev');
|
||||||
|
expect(formatExposureCompensation(1.5)).toBe('+1 1/2ev');
|
||||||
|
expect(formatExposureCompensation(1.9960938)).toBe('+2ev');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -90,17 +90,20 @@ export default function PhotoLarge({
|
|||||||
{photo.focalLengthIn35MmFormatFormatted &&
|
{photo.focalLengthIn35MmFormatFormatted &&
|
||||||
<>
|
<>
|
||||||
{' '}
|
{' '}
|
||||||
<span className={cc(
|
<span
|
||||||
'text-gray-400/80',
|
className={cc(
|
||||||
'dark:text-gray-400/50',
|
'text-gray-400/80',
|
||||||
)}>
|
'dark:text-gray-400/50',
|
||||||
|
)}
|
||||||
|
title="35mm equivalent"
|
||||||
|
>
|
||||||
{photo.focalLengthIn35MmFormatFormatted}
|
{photo.focalLengthIn35MmFormatFormatted}
|
||||||
</span>
|
</span>
|
||||||
</>}
|
</>}
|
||||||
</li>
|
</li>
|
||||||
<li>{photo.fNumberFormatted}</li>
|
<li>{photo.fNumberFormatted}</li>
|
||||||
<li>{photo.isoFormatted}</li>
|
|
||||||
<li>{photo.exposureTimeFormatted}</li>
|
<li>{photo.exposureTimeFormatted}</li>
|
||||||
|
<li>{photo.isoFormatted}</li>
|
||||||
<li>{photo.exposureCompensationFormatted ?? '—'}</li>
|
<li>{photo.exposureCompensationFormatted ?? '—'}</li>
|
||||||
</ul>}
|
</ul>}
|
||||||
<div className={cc(
|
<div className={cc(
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { ExifData } from 'ts-exif-parser';
|
import type { ExifData } from 'ts-exif-parser';
|
||||||
|
import { formatNumberToFraction } from './number';
|
||||||
|
|
||||||
const OFFSET_REGEX = /[+-]\d\d:\d\d/;
|
const OFFSET_REGEX = /[+-]\d\d:\d\d/;
|
||||||
|
|
||||||
@ -18,33 +19,19 @@ export const formatAperture = (aperture?: number) =>
|
|||||||
export const formatIso = (iso?: number) =>
|
export const formatIso = (iso?: number) =>
|
||||||
iso ? `ISO ${iso}` : undefined;
|
iso ? `ISO ${iso}` : undefined;
|
||||||
|
|
||||||
export const formatExposureTime = (exposureTime?: number) =>
|
export const formatExposureTime = (exposureTime = 0) =>
|
||||||
exposureTime
|
exposureTime > 0
|
||||||
? `Shutter 1/${Math.floor(1 / (exposureTime ?? 1))}`
|
? exposureTime < 1
|
||||||
|
? `1/${Math.floor(1 / exposureTime)}s`
|
||||||
|
: `${exposureTime}s`
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const fractionForDecimal = (decimal: number, fractionCharacter?: boolean) => {
|
|
||||||
switch (Math.abs(Math.floor(decimal * 100))) {
|
|
||||||
case 33:
|
|
||||||
return fractionCharacter ? '⅓' : '1/3';
|
|
||||||
case 50:
|
|
||||||
return fractionCharacter ? '½' : '1/2';
|
|
||||||
case 66:
|
|
||||||
case 67:
|
|
||||||
return fractionCharacter ? '⅔' : '2/3';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const formatExposureCompensation = (exposureCompensation?: number) => {
|
export const formatExposureCompensation = (exposureCompensation?: number) => {
|
||||||
if (
|
if (
|
||||||
exposureCompensation &&
|
exposureCompensation &&
|
||||||
Math.abs(exposureCompensation) >= 0.33
|
Math.abs(exposureCompensation) > 0.01
|
||||||
) {
|
) {
|
||||||
const decimal = exposureCompensation % 1;
|
return `${formatNumberToFraction(exposureCompensation)}ev`;
|
||||||
const whole = Math.abs(exposureCompensation - decimal);
|
|
||||||
const fraction = fractionForDecimal(decimal);
|
|
||||||
const sign = exposureCompensation > 0 ? '+' : '-';
|
|
||||||
return `${sign}${whole ? `${whole} ` : ''}${fraction ?? ''} EV`;
|
|
||||||
} else {
|
} else {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,3 +5,46 @@ export const toFixedNumber = (
|
|||||||
const pow = Math.pow(base ?? 10, digits);
|
const pow = Math.pow(base ?? 10, digits);
|
||||||
return Math.round(number * pow) / pow;
|
return Math.round(number * pow) / pow;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const gcd = (a: number, b: number): number => {
|
||||||
|
if (b <= 0.0000001) {
|
||||||
|
return a;
|
||||||
|
} else {
|
||||||
|
return gcd(b, a % b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatDecimalToFraction = (decimal: number) => {
|
||||||
|
if (Math.abs(decimal - 0.33) < 0.011) {
|
||||||
|
return '1/3';
|
||||||
|
} else if (Math.abs(decimal - 0.66) <= 0.011) {
|
||||||
|
return '2/3';
|
||||||
|
} else {
|
||||||
|
const length = decimal.toString().length - 2;
|
||||||
|
|
||||||
|
let denominator = Math.pow(10, length);
|
||||||
|
let numerator = decimal * denominator;
|
||||||
|
|
||||||
|
const divisor = gcd(numerator, denominator);
|
||||||
|
|
||||||
|
numerator /= divisor;
|
||||||
|
denominator /= divisor;
|
||||||
|
|
||||||
|
return `${Math.floor(numerator)}/${Math.floor(denominator)}`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formatNumberToFraction = (number: number) => {
|
||||||
|
const decimal = (1 - number % 1) > 0.01
|
||||||
|
? number % 1
|
||||||
|
: 0;
|
||||||
|
const integer = Math.round(Math.abs(number - decimal));
|
||||||
|
const fraction = decimal !== 0
|
||||||
|
? formatDecimalToFraction(Math.abs(decimal))
|
||||||
|
: '';
|
||||||
|
const sign = number > 0 ? '+' : '-';
|
||||||
|
const whole = integer > 0
|
||||||
|
? fraction ? `${integer} ` : integer
|
||||||
|
: '';
|
||||||
|
return `${sign}${whole}${fraction}`;
|
||||||
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user