1
0
Fork 0
mirror of https://github.com/codedread/bitjs synced 2025-10-03 17:49:16 +02:00

PngParser: Add support for pHYs chunk

This commit is contained in:
Jeff Schiller 2024-01-18 20:10:29 -08:00
parent 1869cd7bfa
commit 17b3850745
3 changed files with 67 additions and 5 deletions

View file

@ -14,7 +14,7 @@ import { ByteStream } from '../../io/bytestream.js';
// https://www.w3.org/TR/png-3/ // https://www.w3.org/TR/png-3/
// https://en.wikipedia.org/wiki/PNG#File_format // https://en.wikipedia.org/wiki/PNG#File_format
// TODO: Ancillary chunks eXIf, hIST, pHYs, sPLT. // TODO: Ancillary chunks eXIf, hIST, sPLT.
// let DEBUG = true; // let DEBUG = true;
let DEBUG = false; let DEBUG = false;
@ -32,6 +32,7 @@ export const PngParseEventType = {
cHRM: 'chromaticities_white_point', cHRM: 'chromaticities_white_point',
gAMA: 'image_gamma', gAMA: 'image_gamma',
iTXt: 'intl_text_data', iTXt: 'intl_text_data',
pHYs: 'physical_pixel_dims',
sBIT: 'significant_bits', sBIT: 'significant_bits',
tEXt: 'textual_data', tEXt: 'textual_data',
tIME: 'last_mod_time', tIME: 'last_mod_time',
@ -262,6 +263,27 @@ export class PngLastModTimeEvent extends Event {
} }
} }
export const PngUnitSpecifier = {
UNKNOWN: 0,
METRE: 1,
};
/**
* @typedef PngPhysicalPixelDimensions
* @property {number} pixelPerUnitX
* @property {number} pixelPerUnitY
* @property {PngUnitSpecifier} unitSpecifier
*/
export class PngPhysicalPixelDimensionsEvent extends Event {
/** @param {PngPhysicalPixelDimensions} physicalPixelDimensions */
constructor(physicalPixelDimensions) {
super(PngParseEventType.pHYs);
/** @type {PngPhysicalPixelDimensions} */
this.physicalPixelDimensions = physicalPixelDimensions;
}
}
/** /**
* @typedef PngChunk Internal use only. * @typedef PngChunk Internal use only.
* @property {number} length * @property {number} length
@ -368,8 +390,8 @@ export class PngParser extends EventTarget {
} }
/** /**
* Type-safe way to bind a listener for a PngLastModTime. * Type-safe way to bind a listener for a PngLastModTimeEvent.
* @param {function(PngLastModTime): void} listener * @param {function(PngLastModTimeEvent): void} listener
* @returns {PngParser} for chaining * @returns {PngParser} for chaining
*/ */
onLastModTime(listener) { onLastModTime(listener) {
@ -387,6 +409,16 @@ export class PngParser extends EventTarget {
return this; return this;
} }
/**
* Type-safe way to bind a listener for a PngPhysicalPixelDimensionsEvent.
* @param {function(PngPhysicalPixelDimensionsEvent): void} listener
* @returns {PngParser} for chaining
*/
onPhysicalPixelDimensions(listener) {
super.addEventListener(PngParseEventType.pHYs, listener);
return this;
}
/** /**
* Type-safe way to bind a listener for a PngSignificantBitsEvent. * Type-safe way to bind a listener for a PngSignificantBitsEvent.
* @param {function(PngSignificantBitsEvent): void} listener * @param {function(PngSignificantBitsEvent): void} listener
@ -571,6 +603,21 @@ export class PngParser extends EventTarget {
this.dispatchEvent(new PngPaletteEvent(this.palette)); this.dispatchEvent(new PngPaletteEvent(this.palette));
break; break;
// https://www.w3.org/TR/png-3/#11pHYs
case 'pHYs':
/** @type {physicalPixelDimensions} */
const pixelDims = {
pixelPerUnitX: chStream.readNumber(4),
pixelPerUnitY: chStream.readNumber(4),
unitSpecifier: chStream.readNumber(1),
};
if (!Object.values(PngUnitSpecifier).includes(pixelDims.unitSpecifier)) {
throw `Bad pHYs unit specifier: ${pixelDims.unitSpecifier}`;
}
this.dispatchEvent(new PngPhysicalPixelDimensionsEvent(pixelDims));
break;
// https://www.w3.org/TR/png-3/#11tEXt // https://www.w3.org/TR/png-3/#11tEXt
case 'tEXt': case 'tEXt':
const byteArr = chStream.peekBytes(length); const byteArr = chStream.peekBytes(length);
@ -750,7 +797,10 @@ async function main() {
// console.dir(evt.backgroundColor); // console.dir(evt.backgroundColor);
}); });
parser.onLastModTime(evt => { parser.onLastModTime(evt => {
console.dir(evt.lastModTime); // console.dir(evt.lastModTime);
});
parser.onPhysicalPixelDimensions(evt => {
// console.dir(evt.physicalPixelDimensions);
}); });
try { try {

View file

@ -1,7 +1,7 @@
import * as fs from 'node:fs'; import * as fs from 'node:fs';
import 'mocha'; import 'mocha';
import { expect } from 'chai'; import { expect } from 'chai';
import { PngColorType, PngInterlaceMethod, PngParser } from '../image/parsers/png.js'; import { PngColorType, PngInterlaceMethod, PngUnitSpecifier, PngParser } from '../image/parsers/png.js';
/** @typedef {import('../image/parsers/png.js').PngBackgroundColor} PngBackgroundColor */ /** @typedef {import('../image/parsers/png.js').PngBackgroundColor} PngBackgroundColor */
/** @typedef {import('../image/parsers/png.js').PngChromaticies} PngChromaticies */ /** @typedef {import('../image/parsers/png.js').PngChromaticies} PngChromaticies */
@ -12,6 +12,7 @@ import { PngColorType, PngInterlaceMethod, PngParser } from '../image/parsers/pn
/** @typedef {import('../image/parsers/png.js').PngIntlTextualData} PngIntlTextualData */ /** @typedef {import('../image/parsers/png.js').PngIntlTextualData} PngIntlTextualData */
/** @typedef {import('../image/parsers/png.js').PngLastModTime} PngLastModTime */ /** @typedef {import('../image/parsers/png.js').PngLastModTime} PngLastModTime */
/** @typedef {import('../image/parsers/png.js').PngPalette} PngPalette */ /** @typedef {import('../image/parsers/png.js').PngPalette} PngPalette */
/** @typedef {import('../image/parsers/png.js').PngPhysicalPixelDimensions} PngPhysicalPixelDimensions */
/** @typedef {import('../image/parsers/png.js').PngSignificantBits} PngSignificantBits */ /** @typedef {import('../image/parsers/png.js').PngSignificantBits} PngSignificantBits */
/** @typedef {import('../image/parsers/png.js').PngTextualData} PngTextualData */ /** @typedef {import('../image/parsers/png.js').PngTextualData} PngTextualData */
/** @typedef {import('../image/parsers/png.js').PngTransparency} PngTransparency */ /** @typedef {import('../image/parsers/png.js').PngTransparency} PngTransparency */
@ -248,4 +249,15 @@ describe('bitjs.image.parsers.PngParser', () => {
expect(lastModTime.minute).equals(59); expect(lastModTime.minute).equals(59);
expect(lastModTime.second).equals(59); expect(lastModTime.second).equals(59);
}); });
it('extracts pHYs', async () => {
/** @type {PngPhysicalPixelDimensions} */
let pixelDims;
await getPngParser('tests/image-testfiles/cdun2c08.png')
.onPhysicalPixelDimensions(evt => { pixelDims = evt.physicalPixelDimensions })
.start();
expect(pixelDims.pixelPerUnitX).equals(1000);
expect(pixelDims.pixelPerUnitY).equals(1000);
expect(pixelDims.unitSpecifier).equals(PngUnitSpecifier.METRE);
});
}); });

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 B