Skip to content

Commit aeb5bb2

Browse files
committed
PngParser: Add support for eXIf chunk
1 parent 29ca69e commit aeb5bb2

File tree

3 files changed

+50
-1
lines changed

3 files changed

+50
-1
lines changed

image/parsers/png.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@
1010

1111
import * as fs from 'node:fs'; // TODO: Remove.
1212
import { ByteStream } from '../../io/bytestream.js';
13+
import { getExifProfile } from './exif.js';
14+
15+
/** @typedef {import('./exif.js').ExifValue} ExifValue */
1316

1417
// https://www.w3.org/TR/png-3/
1518
// https://en.wikipedia.org/wiki/PNG#File_format
1619

17-
// TODO: Ancillary chunks eXIf, hIST, sPLT.
20+
// TODO: Ancillary chunks: hIST, sPLT.
1821

1922
// let DEBUG = true;
2023
let DEBUG = false;
@@ -30,6 +33,7 @@ export const PngParseEventType = {
3033
// Ancillary chunks.
3134
bKGD: 'background_color',
3235
cHRM: 'chromaticities_white_point',
36+
eXIf: 'exif_profile',
3337
gAMA: 'image_gamma',
3438
iTXt: 'intl_text_data',
3539
pHYs: 'physical_pixel_dims',
@@ -284,6 +288,17 @@ export class PngPhysicalPixelDimensionsEvent extends Event {
284288
}
285289
}
286290

291+
/** @typedef {Map<number, ExifValue>} PngExifProfile */
292+
293+
export class PngExifProfileEvent extends Event {
294+
/** @param {PngExifProfile} exifProfile */
295+
constructor(exifProfile) {
296+
super(PngParseEventType.eXIf);
297+
/** @type {PngExifProfile} */
298+
this.exifProfile = exifProfile;
299+
}
300+
}
301+
287302
/**
288303
* @typedef PngChunk Internal use only.
289304
* @property {number} length
@@ -349,6 +364,16 @@ export class PngParser extends EventTarget {
349364
return this;
350365
}
351366

367+
/**
368+
* Type-safe way to bind a listener for a PngExifProfileEvent.
369+
* @param {function(PngExifProfileEvent): void} listener
370+
* @returns {PngParser} for chaining
371+
*/
372+
onExifProfile(listener) {
373+
super.addEventListener(PngParseEventType.eXIf, listener);
374+
return this;
375+
}
376+
352377
/**
353378
* Type-safe way to bind a listener for a PngImageGammaEvent.
354379
* @param {function(PngImageGammaEvent): void} listener
@@ -715,6 +740,12 @@ export class PngParser extends EventTarget {
715740
this.dispatchEvent(new PngIntlTextualDataEvent(intlTextData));
716741
break;
717742

743+
// https://www.w3.org/TR/png-3/#eXIf
744+
case 'eXIf':
745+
const exifValueMap = getExifProfile(chStream);
746+
this.dispatchEvent(new PngExifProfileEvent(exifValueMap));
747+
break;
748+
718749
// https://www.w3.org/TR/png-3/#11IDAT
719750
case 'IDAT':
720751
/** @type {PngImageData} */
@@ -802,6 +833,9 @@ async function main() {
802833
parser.onPhysicalPixelDimensions(evt => {
803834
// console.dir(evt.physicalPixelDimensions);
804835
});
836+
parser.onExifProfile(evt => {
837+
// console.dir(evt.exifProfile);
838+
});
805839

806840
try {
807841
await parser.start();

tests/image-parsers-png.spec.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import * as fs from 'node:fs';
22
import 'mocha';
33
import { expect } from 'chai';
44
import { PngColorType, PngInterlaceMethod, PngUnitSpecifier, PngParser } from '../image/parsers/png.js';
5+
import { ExifDataFormat, ExifTagNumber } from '../image/parsers/exif.js';
6+
7+
/** @typedef {import('../image/parsers/exif.js').ExifValue} ExifValue */
58

69
/** @typedef {import('../image/parsers/png.js').PngBackgroundColor} PngBackgroundColor */
710
/** @typedef {import('../image/parsers/png.js').PngChromaticies} PngChromaticies */
@@ -260,4 +263,16 @@ describe('bitjs.image.parsers.PngParser', () => {
260263
expect(pixelDims.pixelPerUnitY).equals(1000);
261264
expect(pixelDims.unitSpecifier).equals(PngUnitSpecifier.METRE);
262265
});
266+
267+
it('extracts eXIf', async () => {
268+
/** @type {PngPhysicalPixelDimensions} */
269+
let exif;
270+
await getPngParser('tests/image-testfiles/exif2c08.png')
271+
.onExifProfile(evt => { exif = evt.exifProfile })
272+
.start();
273+
274+
const descVal = exif.get(ExifTagNumber.COPYRIGHT);
275+
expect(descVal.dataFormat).equals(ExifDataFormat.ASCII_STRING);
276+
expect(descVal.stringValue).equals('2017 Willem van Schaik');
277+
});
263278
});

tests/image-testfiles/exif2c08.png

1.75 KB
Loading

0 commit comments

Comments
 (0)