Skip to content

Commit

Permalink
feat: TakenAt (#244)
Browse files Browse the repository at this point in the history
  • Loading branch information
rhea-so committed May 2, 2024
2 parents 2afa42d + 60056e3 commit a332c99
Show file tree
Hide file tree
Showing 16 changed files with 155 additions and 16 deletions.
2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.4.69",
"version": "0.4.70",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
Binary file added web/public/fonts/poxel.ttf
Binary file not shown.
2 changes: 2 additions & 0 deletions web/src/core/exif-metadata/exif-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class ExifMetadata {
public iso: string | undefined;
public exposureTime: string | undefined;
public thumbnail: string | undefined;
public takenAt: string | undefined;

constructor(metadata: Tags) {
console.log(metadata);
Expand All @@ -28,6 +29,7 @@ class ExifMetadata {
this.iso = metadata?.ISOSpeedRatings?.value ? 'ISO' + metadata?.ISOSpeedRatings?.value?.toString() : undefined;
this.exposureTime = metadata?.ExposureTime?.description ? metadata?.ExposureTime?.description + 's' : undefined;
this.thumbnail = metadata?.Thumbnail?.base64 ? 'data:image/jpg;base64,' + metadata?.Thumbnail?.base64 : undefined;
this.takenAt = metadata?.DateCreated?.description;
}
}

Expand Down
8 changes: 8 additions & 0 deletions web/src/core/photo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ class Photo {
public get exposureTime(): string {
return this.metadata.exposureTime || '';
}

/**
* Returns the date the photo was taken.
* @example '2021-01-01T00:00:00.000+09:00'
*/
public get takenAt(): string {
return this.metadata.takenAt || '';
}
}

export default Photo;
1 change: 1 addition & 0 deletions web/src/fonts/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
enum Font {
Digital7 = 'digital-7',
Poxel = 'poxel',
DINAlternateBold = 'din-alternate-bold',
}

Expand Down
1 change: 1 addition & 0 deletions web/src/locales/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"aperture": "Aperture",
"shutter": "Shutter Speed",
"iso": "ISO",
"taken-at": "Taken At",

"root.tab.convert": "Convert",
"root.tab.theme-settings": "Theme Settings",
Expand Down
1 change: 1 addition & 0 deletions web/src/locales/translations/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"aperture": "絞り値",
"shutter": "シャッタースピード",
"iso": "ISO感度",
"taken-at": "撮影日時",

"root.tab.convert": "変換",
"root.tab.theme-settings": "テーマ設定",
Expand Down
1 change: 1 addition & 0 deletions web/src/locales/translations/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"aperture": "조리개",
"shutter": "셔터 속도",
"iso": "ISO",
"taken-at": "촬영 일시",

"root.tab.convert": "프레임 씌우기",
"root.tab.theme-settings": "테마 설정",
Expand Down
3 changes: 3 additions & 0 deletions web/src/pages/convert/components/override-metadata.popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const OverrideMetadataPopup = () => {
const [aperture, setAperture] = useState(overrideMetadataTarget?.metadata.fNumber || '');
const [iso, setIso] = useState(overrideMetadataTarget?.metadata.iso || '');
const [shutter, setShutter] = useState(overrideMetadataTarget?.metadata.exposureTime || '');
const [takenAt, setTakenAt] = useState(overrideMetadataTarget?.metadata.takenAt || '');

useEffect(() => {
if (overrideMetadataTarget) setMake(overrideMetadataTarget.metadata.make || '');
Expand All @@ -25,6 +26,7 @@ const OverrideMetadataPopup = () => {
if (overrideMetadataTarget) setAperture(overrideMetadataTarget.metadata.fNumber || '');
if (overrideMetadataTarget) setIso(overrideMetadataTarget.metadata.iso || '');
if (overrideMetadataTarget) setShutter(overrideMetadataTarget.metadata.exposureTime || '');
if (overrideMetadataTarget) setTakenAt(overrideMetadataTarget.metadata.takenAt || '');
}, [overrideMetadataTarget]);

return (
Expand Down Expand Up @@ -52,6 +54,7 @@ const OverrideMetadataPopup = () => {
<ListInput label={t('aperture')} type="text" value={aperture} onChange={(e) => (setAperture(e.target.value), (overrideMetadataTarget!.metadata.fNumber = e.target.value))} />
<ListInput label={t('iso')} type="text" value={iso} onChange={(e) => (setIso(e.target.value), (overrideMetadataTarget!.metadata.iso = e.target.value))} />
<ListInput label={t('shutter')} type="text" value={shutter} onChange={(e) => (setShutter(e.target.value), (overrideMetadataTarget!.metadata.exposureTime = e.target.value))} />
<ListInput label={t('taken-at')} type="text" value={takenAt} onChange={(e) => (setTakenAt(e.target.value), (overrideMetadataTarget!.metadata.takenAt = e.target.value))} />
</List>
</Page>
</Popup>
Expand Down
4 changes: 3 additions & 1 deletion web/src/themes/03_ONE_LINE/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const ONE_LINE_OPTIONS: ThemeOption[] = [
{ id: 'FONT_SIZE', type: 'number', default: 70, description: 'px' },
{ id: 'FONT_FAMILY', type: 'select', options: ['Barlow', ...Object.values(Font)], default: 'Barlow', description: 'ex. din-alternate-bold, digital-7, Barlow, Arial, sans-serif' },
{ id: 'TOP_LABEL', type: 'string', default: '', description: 'ex. @username' },
{ id: 'DIVIDER', type: 'string', default: '|', description: 'ex. ∙' },
];

const ONE_LINE_FUNC: ThemeFunc = (photo: Photo, input: ThemeOptionInput, store: Store) => {
Expand All @@ -35,6 +36,7 @@ const ONE_LINE_FUNC: ThemeFunc = (photo: Photo, input: ThemeOptionInput, store:
const FONT_SIZE = input.get('FONT_SIZE') as number;
const FONT_FAMILY = (input.get('FONT_FAMILY') as string).trim();
const TOP_LABEL = (input.get('TOP_LABEL') as string).trim();
const DIVIDER = (input.get('DIVIDER') as string).trim();

const canvas = sandbox(photo, {
targetRatio: store.ratio,
Expand All @@ -56,7 +58,7 @@ const ONE_LINE_FUNC: ThemeFunc = (photo: Photo, input: ThemeOptionInput, store:
[photo.make, photo.model, photo.lensModel, ...(store.disableExposureMeter ? [] : [`${photo.iso}`, `${photo.focalLength}`, `${photo.fNumber}`, `${photo.exposureTime}`])]
.filter(Boolean)
.map((value) => value!.trim())
.join(' | '),
.join(` ${DIVIDER} `),
TEXT_ALIGN === 'left' ? PADDING_LEFT : TEXT_ALIGN === 'center' ? canvas.width / 2 : canvas.width - PADDING_RIGHT,
canvas.height - PADDING_BOTTOM / 2
);
Expand Down
6 changes: 4 additions & 2 deletions web/src/themes/04_TWO_LINE/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const TWO_LINE_OPTIONS: ThemeOption[] = [
{ id: 'FONT_SIZE', type: 'number', default: 70, description: 'px' },
{ id: 'FONT_FAMILY', type: 'select', options: ['Barlow', ...Object.values(Font)], default: 'Barlow', description: 'ex. din-alternate-bold, digital-7, Barlow, Arial, sans-serif' },
{ id: 'TOP_LABEL', type: 'string', default: '', description: 'ex. @username' },
{ id: 'DIVIDER', type: 'string', default: '|', description: 'ex. ∙' },
];

const TWO_LINE_FUNC: ThemeFunc = (photo: Photo, input: ThemeOptionInput, store: Store) => {
Expand All @@ -35,6 +36,7 @@ const TWO_LINE_FUNC: ThemeFunc = (photo: Photo, input: ThemeOptionInput, store:
const FONT_SIZE = input.get('FONT_SIZE') as number;
const FONT_FAMILY = (input.get('FONT_FAMILY') as string).trim();
const TOP_LABEL = (input.get('TOP_LABEL') as string).trim();
const DIVIDER = (input.get('DIVIDER') as string).trim();

const canvas = sandbox(photo, {
targetRatio: store.ratio,
Expand All @@ -55,14 +57,14 @@ const TWO_LINE_FUNC: ThemeFunc = (photo: Photo, input: ThemeOptionInput, store:
[photo.make, photo.model, photo.lensModel]
.filter(Boolean)
.map((value) => value!.trim())
.join(' | '),
.join(` ${DIVIDER} `),
TEXT_ALIGN === 'left' ? PADDING_LEFT : TEXT_ALIGN === 'center' ? canvas.width / 2 : canvas.width - PADDING_RIGHT,
canvas.height - PADDING_BOTTOM / 2 - FONT_SIZE / 1.5
);

if (!store.disableExposureMeter) {
context.fillText(
[`${photo.iso}`, `${photo.focalLength}`, `${photo.fNumber}`, `${photo.exposureTime}`].filter(Boolean).join(' | '),
[`${photo.iso}`, `${photo.focalLength}`, `${photo.fNumber}`, `${photo.exposureTime}`].filter(Boolean).join(` ${DIVIDER} `),
TEXT_ALIGN === 'left' ? PADDING_LEFT : TEXT_ALIGN === 'center' ? canvas.width / 2 : canvas.width - PADDING_RIGHT,
canvas.height - PADDING_BOTTOM / 2 + FONT_SIZE / 1.5
);
Expand Down
24 changes: 20 additions & 4 deletions web/src/themes/07_STRAP/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ supportLogo.set('SONY_LIGHT', loadLogo('/maker/light/sony.png'));
supportLogo.set('SONY_DARK', loadLogo('/maker/dark/sony.png'));

const STRAP_OPTIONS: ThemeOption[] = [
{ id: 'ARTIST', type: 'string', default: 'Your Name', description: 'your name' },
{ id: 'ARTIST', type: 'string', default: '', description: 'your name' },
{ id: 'DARK_MODE', type: 'boolean', default: false, description: 'enable to use dark mode' },
{ id: 'SECONDARY_TEXT_FONT_WEIGHT', type: 'range-slider', min: 100, max: 900, step: 100, default: 300, description: '100 - 900' },
{ id: 'PADDING_TOP', type: 'number', default: 0, description: 'px' },
Expand Down Expand Up @@ -96,9 +96,25 @@ const STRAP_FUNC: ThemeFunc = (photo: Photo, input: ThemeOptionInput, store: Sto
}

// Shot by
context.font = `normal ${SECONDARY_TEXT_FONT_WEIGHT} ${FONT_SIZE}px Barlow`;
context.fillStyle = SECONDARY_TEXT_COLOR;
context.fillText(`Shot by © ${ARTIST}`, FONT_SIZE, canvas.height - PADDING_BOTTOM / 2 + FONT_SIZE / 2);
if (ARTIST) {
context.font = `normal ${SECONDARY_TEXT_FONT_WEIGHT} ${FONT_SIZE}px Barlow`;
context.fillStyle = SECONDARY_TEXT_COLOR;
context.fillText(`Shot by © ${ARTIST}`, FONT_SIZE, canvas.height - PADDING_BOTTOM / 2 + FONT_SIZE / 2);
} else {
if (photo.takenAt) {
context.font = `normal ${SECONDARY_TEXT_FONT_WEIGHT} ${FONT_SIZE}px Barlow`;
context.fillStyle = SECONDARY_TEXT_COLOR;
const takenAt = new Date(photo.takenAt);
context.fillText(
`${takenAt.getFullYear()}${(takenAt.getMonth() + 1).toString().padStart(2, '0')}${takenAt.getDate().toString().padStart(2, '0')}${takenAt
.getHours()
.toString()
.padStart(2, '0')}:${takenAt.getMinutes().toString().padStart(2, '0')}:${takenAt.getSeconds().toString().padStart(2, '0')}`,
FONT_SIZE,
canvas.height - PADDING_BOTTOM / 2 + FONT_SIZE / 2
);
}
}

// RIGHT SECOND
context.textAlign = 'right';
Expand Down
25 changes: 20 additions & 5 deletions web/src/themes/08_FILM/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { Store } from '../../store';
import sandbox from '../../core/drawing/sandbox';
import { ThemeFunc } from '../../core/drawing/theme';
import { ThemeOption, ThemeOptionInput } from '../../pages/theme/types/theme-option';
import Font from '../../fonts';

const FILM_OPTIONS: ThemeOption[] = [
{ id: 'FONT_FAMILY', type: 'select', options: ['Barlow', ...Object.values(Font)], default: 'digital-7', description: 'ex. din-alternate-bold, digital-7, Barlow, Arial, sans-serif' },
{ id: 'TEXT_COLOR', type: 'color', default: '#FFA500', description: 'default is orange hex code' },
{ id: 'BACKGROUND_COLOR', type: 'color', default: '#000000', description: '#ffffff is white, #000000 is black' },
{ id: 'PADDING_TOP', type: 'number', default: 0, description: 'px' },
Expand All @@ -14,6 +16,7 @@ const FILM_OPTIONS: ThemeOption[] = [
];

const FILM_FUNC: ThemeFunc = (photo: Photo, input: ThemeOptionInput, store: Store) => {
const FONT_FAMILY = (input.get('FONT_FAMILY') as string).trim();
const TEXT_COLOR = input.get('TEXT_COLOR') as string;
const BACKGROUND_COLOR = (input.get('BACKGROUND_COLOR') as string).trim();
const PADDING_TOP = input.get('PADDING_TOP') as number;
Expand All @@ -40,18 +43,18 @@ const FILM_FUNC: ThemeFunc = (photo: Photo, input: ThemeOptionInput, store: Stor
];

context.textAlign = 'right';
context.font = `100px digital-7`;
context.font = `100px ${FONT_FAMILY}`;
for (let i = 0; i < datas.length; i++) {
const data = datas[i];
context.fillText(data.value, canvas.width - 100, canvas.height - 100 - i * 100);
const width = context.measureText(data.value).width;
context.font = `70px digital-7`;
context.fillText(data.key, canvas.width - 100 - width - 20, canvas.height - 105 - i * 100);
context.font = `100px digital-7`;
context.font = `60px ${FONT_FAMILY}`;
context.fillText(data.key, canvas.width - 100 - width - 20, canvas.height - 110 - i * 100);
context.font = `100px ${FONT_FAMILY}`;
}
}

context.font = `70px digital-7`;
context.font = `70px ${FONT_FAMILY}`;
context.textAlign = 'left';
context.fillText(
[store.showLensModel ? store.overrideLensModel || photo.lensModel : null]
Expand All @@ -69,6 +72,18 @@ const FILM_FUNC: ThemeFunc = (photo: Photo, input: ThemeOptionInput, store: Stor
100,
canvas.height - 205
);
if (photo.takenAt) {
context.font = `50px ${FONT_FAMILY}`;
const takenAt = new Date(photo.takenAt);
context.fillText(
`${takenAt.getFullYear()}-${(takenAt.getMonth() + 1).toString().padStart(2, '0')}-${takenAt.getDate().toString().padStart(2, '0')} ${takenAt.getHours().toString().padStart(2, '0')}:${takenAt
.getMinutes()
.toString()
.padStart(2, '0')}:${takenAt.getSeconds().toString().padStart(2, '0')}`,
100,
canvas.height - 305
);
}

return canvas;
};
Expand Down
14 changes: 11 additions & 3 deletions web/src/themes/10_LIGHTROOM/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { ThemeOption, ThemeOptionInput } from '../../pages/theme/types/theme-opt
import Font from '../../fonts';

const LIGHTROOM_OPTIONS: ThemeOption[] = [
{ id: 'ARTIST', type: 'string', default: 'Your Name', description: 'your name' },
{ id: 'BACKGROUND_COLOR', type: 'color', default: '#1f1f1f', description: '#ffffff is white, #000000 is black' },
{ id: 'PADDING_TOP', type: 'number', default: 50, description: 'px' },
{ id: 'PADDING_BOTTOM', type: 'number', default: 150, description: 'px' },
Expand All @@ -20,7 +19,6 @@ const LIGHTROOM_OPTIONS: ThemeOption[] = [
];

const LIGHTROOM_FUNC: ThemeFunc = (photo: Photo, input: ThemeOptionInput, store: Store) => {
const ARTIST = (input.get('ARTIST') as string).trim();
const BACKGROUND_COLOR = (input.get('BACKGROUND_COLOR') as string).trim();
const PADDING_TOP = input.get('PADDING_TOP') as number;
const PADDING_BOTTOM = input.get('PADDING_BOTTOM') as number;
Expand Down Expand Up @@ -60,7 +58,17 @@ const LIGHTROOM_FUNC: ThemeFunc = (photo: Photo, input: ThemeOptionInput, store:
);

context.textAlign = 'right';
context.fillText(ARTIST, canvas.width - PADDING_RIGHT, canvas.height - PADDING_BOTTOM / 2);
if (photo.takenAt) {
const takenAt = new Date(photo.takenAt);
context.fillText(
`${takenAt.getFullYear()}${(takenAt.getMonth() + 1).toString().padStart(2, '0')}${takenAt.getDate().toString().padStart(2, '0')}${takenAt.getHours().toString().padStart(2, '0')}:${takenAt
.getMinutes()
.toString()
.padStart(2, '0')}:${takenAt.getSeconds().toString().padStart(2, '0')}`,
canvas.width - PADDING_RIGHT,
canvas.height - PADDING_BOTTOM / 2
);
}

return canvas;
};
Expand Down
77 changes: 77 additions & 0 deletions web/src/themes/16_SIMPLE/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import Photo from '../../core/photo';
import { Store } from '../../store';
import sandbox from '../../core/drawing/sandbox';
import { ThemeFunc } from '../../core/drawing/theme';
import { ThemeOption, ThemeOptionInput } from '../../pages/theme/types/theme-option';
import Font from '../../fonts';

const SIMPLE_OPTIONS: ThemeOption[] = [
{ id: 'LABEL', type: 'string', default: '@username', description: 'ex. @username' },
{ id: 'FONT_FAMILY', type: 'select', options: ['Barlow', ...Object.values(Font)], default: 'Barlow', description: 'ex. din-alternate-bold, digital-7, Barlow, Arial, sans-serif' },
{ id: 'PADDING_INSIDE', type: 'boolean', default: false, description: 'enable to use inside padding' },
{ id: 'PADDING_TOP', type: 'number', default: 100, description: 'px' },
{ id: 'PADDING_BOTTOM', type: 'number', default: 400, description: 'px' },
{ id: 'PADDING_LEFT', type: 'number', default: 100, description: 'px' },
{ id: 'PADDING_RIGHT', type: 'number', default: 100, description: 'px' },
];

const SIMPLE_FUNC: ThemeFunc = (photo: Photo, input: ThemeOptionInput, store: Store) => {
const LABEL = (input.get('LABEL') as string).trim();
const FONT_FAMILY = (input.get('FONT_FAMILY') as string).trim();
const PADDING_INSIDE = input.get('PADDING_INSIDE') as boolean;
const PADDING_TOP = input.get('PADDING_TOP') as number;
const PADDING_BOTTOM = input.get('PADDING_BOTTOM') as number;
const PADDING_LEFT = input.get('PADDING_LEFT') as number;
const PADDING_RIGHT = input.get('PADDING_RIGHT') as number;

const canvas = sandbox(photo, {
targetRatio: store.ratio,
notCroppedMode: store.notCroppedMode,
backgroundColor: '#ffffff',
padding: PADDING_INSIDE ? { top: 0, right: 0, bottom: 0, left: 0 } : { top: PADDING_TOP, right: PADDING_RIGHT, bottom: PADDING_BOTTOM, left: PADDING_LEFT },
});

const context = canvas.getContext('2d')!;
context.fillStyle = '#a0a0a0';
context.textAlign = 'center';
context.textBaseline = 'middle';

context.font = `300 ${40}px ${FONT_FAMILY}`;
context.fillText(LABEL, canvas.width / 2, canvas.height - 60);

context.textAlign = 'left';
context.fillStyle = '#000000';
context.font = `700 ${100}px ${FONT_FAMILY}`;
const makerWidth = context.measureText(photo.make + ' ').width;
context.font = `300 ${100}px ${FONT_FAMILY}`;
const modelWidth = context.measureText(photo.model).width;
context.font = `700 ${100}px ${FONT_FAMILY}`;
context.fillText(photo.make, canvas.width / 2 - (makerWidth + modelWidth) / 2, canvas.height - PADDING_BOTTOM / 2 - 100);
context.font = `300 ${100}px ${FONT_FAMILY}`;
context.fillText(photo.model, canvas.width / 2 - (makerWidth + modelWidth) / 2 + makerWidth, canvas.height - PADDING_BOTTOM / 2 - 100);

context.textAlign = 'center';
context.fillStyle = '#a0a0a0';

if (photo.takenAt) {
context.font = `300 ${30}px ${FONT_FAMILY}`;
const takenAt = new Date(photo.takenAt);
context.fillText(
`${takenAt.getFullYear()}${(takenAt.getMonth() + 1).toString().padStart(2, '0')}${takenAt.getDate().toString().padStart(2, '0')}${takenAt.getHours().toString().padStart(2, '0')}:${takenAt
.getMinutes()
.toString()
.padStart(2, '0')}:${takenAt.getSeconds().toString().padStart(2, '0')}`,
canvas.width / 2,
canvas.height - PADDING_BOTTOM / 2 + 80
);
}

if (!store.disableExposureMeter) {
context.font = `300 ${50}px ${FONT_FAMILY}`;
context.fillText([`${photo.iso}`, `${photo.focalLength}`, `${photo.fNumber}`, `${photo.exposureTime}`].filter(Boolean).join(' ∙ '), canvas.width / 2, canvas.height - PADDING_BOTTOM / 2);
}

return canvas;
};

export { SIMPLE_FUNC, SIMPLE_OPTIONS };
2 changes: 2 additions & 0 deletions web/src/themes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { CUSTOM_TWO_LINE_FUNC, CUSTOM_TWO_LINE_OPTIONS } from './12_CUSTOM_TWO_L
import { TIP_FUNC, TIP_OPTIONS } from './13_TIP';
import { POSTER_FUNC, POSTER_OPTIONS } from './14_POSTER';
import { CINEMASCOPE_FUNC, CINEMASCOPE_OPTIONS } from './15_CINEMASCOPE';
import { SIMPLE_FUNC, SIMPLE_OPTIONS } from './16_SIMPLE';

type AcceptInputType = string | number | boolean;

Expand Down Expand Up @@ -44,6 +45,7 @@ const useThemeStore = create<ThemeStore>((set) => ({
const themes = [
{ name: 'No frame', func: NO_FRAME_THEME_FUNC, options: NO_FRAME_OPTIONS },
{ name: 'Just frame', func: JUST_FRAME_FUNC, options: JUST_FRAME_OPTIONS },
{ name: 'Simple', func: SIMPLE_FUNC, options: SIMPLE_OPTIONS },
{ name: 'Strap', func: STRAP_FUNC, options: STRAP_OPTIONS },
{ name: 'One line', func: ONE_LINE_FUNC, options: ONE_LINE_OPTIONS },
{ name: 'Two line', func: TWO_LINE_FUNC, options: TWO_LINE_OPTIONS },
Expand Down

0 comments on commit a332c99

Please sign in to comment.