@@ -14,7 +14,7 @@ import { ByteStream } from '../../io/bytestream.js';
1414// https://www.w3.org/TR/png-3/
1515// https://en.wikipedia.org/wiki/PNG#File_format
1616
17- // TODO: Ancillary chunks eXIf, hIST, pHYs, sPLT.
17+ // TODO: Ancillary chunks eXIf, hIST, sPLT.
1818
1919// let DEBUG = true;
2020let DEBUG = false ;
@@ -32,6 +32,7 @@ export const PngParseEventType = {
3232 cHRM : 'chromaticities_white_point' ,
3333 gAMA : 'image_gamma' ,
3434 iTXt : 'intl_text_data' ,
35+ pHYs : 'physical_pixel_dims' ,
3536 sBIT : 'significant_bits' ,
3637 tEXt : 'textual_data' ,
3738 tIME : 'last_mod_time' ,
@@ -262,6 +263,27 @@ export class PngLastModTimeEvent extends Event {
262263 }
263264}
264265
266+ export const PngUnitSpecifier = {
267+ UNKNOWN : 0 ,
268+ METRE : 1 ,
269+ } ;
270+
271+ /**
272+ * @typedef PngPhysicalPixelDimensions
273+ * @property {number } pixelPerUnitX
274+ * @property {number } pixelPerUnitY
275+ * @property {PngUnitSpecifier } unitSpecifier
276+ */
277+
278+ export class PngPhysicalPixelDimensionsEvent extends Event {
279+ /** @param {PngPhysicalPixelDimensions } physicalPixelDimensions */
280+ constructor ( physicalPixelDimensions ) {
281+ super ( PngParseEventType . pHYs ) ;
282+ /** @type {PngPhysicalPixelDimensions } */
283+ this . physicalPixelDimensions = physicalPixelDimensions ;
284+ }
285+ }
286+
265287/**
266288 * @typedef PngChunk Internal use only.
267289 * @property {number } length
@@ -368,8 +390,8 @@ export class PngParser extends EventTarget {
368390 }
369391
370392 /**
371- * Type-safe way to bind a listener for a PngLastModTime .
372- * @param {function(PngLastModTime ): void } listener
393+ * Type-safe way to bind a listener for a PngLastModTimeEvent .
394+ * @param {function(PngLastModTimeEvent ): void } listener
373395 * @returns {PngParser } for chaining
374396 */
375397 onLastModTime ( listener ) {
@@ -387,6 +409,16 @@ export class PngParser extends EventTarget {
387409 return this ;
388410 }
389411
412+ /**
413+ * Type-safe way to bind a listener for a PngPhysicalPixelDimensionsEvent.
414+ * @param {function(PngPhysicalPixelDimensionsEvent): void } listener
415+ * @returns {PngParser } for chaining
416+ */
417+ onPhysicalPixelDimensions ( listener ) {
418+ super . addEventListener ( PngParseEventType . pHYs , listener ) ;
419+ return this ;
420+ }
421+
390422 /**
391423 * Type-safe way to bind a listener for a PngSignificantBitsEvent.
392424 * @param {function(PngSignificantBitsEvent): void } listener
@@ -571,6 +603,21 @@ export class PngParser extends EventTarget {
571603 this . dispatchEvent ( new PngPaletteEvent ( this . palette ) ) ;
572604 break ;
573605
606+ // https://www.w3.org/TR/png-3/#11pHYs
607+ case 'pHYs' :
608+ /** @type {physicalPixelDimensions } */
609+ const pixelDims = {
610+ pixelPerUnitX : chStream . readNumber ( 4 ) ,
611+ pixelPerUnitY : chStream . readNumber ( 4 ) ,
612+ unitSpecifier : chStream . readNumber ( 1 ) ,
613+ } ;
614+ if ( ! Object . values ( PngUnitSpecifier ) . includes ( pixelDims . unitSpecifier ) ) {
615+ throw `Bad pHYs unit specifier: ${ pixelDims . unitSpecifier } ` ;
616+ }
617+
618+ this . dispatchEvent ( new PngPhysicalPixelDimensionsEvent ( pixelDims ) ) ;
619+ break ;
620+
574621 // https://www.w3.org/TR/png-3/#11tEXt
575622 case 'tEXt' :
576623 const byteArr = chStream . peekBytes ( length ) ;
@@ -750,7 +797,10 @@ async function main() {
750797 // console.dir(evt.backgroundColor);
751798 } ) ;
752799 parser . onLastModTime ( evt => {
753- console . dir ( evt . lastModTime ) ;
800+ // console.dir(evt.lastModTime);
801+ } ) ;
802+ parser . onPhysicalPixelDimensions ( evt => {
803+ // console.dir(evt.physicalPixelDimensions);
754804 } ) ;
755805
756806 try {
0 commit comments