@@ -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 bKGD, eXIf, hIST, iTXt, pHYs, sPLT, tIME.
17+ // TODO: Ancillary chunks bKGD, eXIf, hIST, pHYs, sPLT, tIME.
1818
1919// let DEBUG = true;
2020let DEBUG = false ;
@@ -30,6 +30,7 @@ export const PngParseEventType = {
3030 // Ancillary chunks.
3131 cHRM : 'chromaticities_white_point' ,
3232 gAMA : 'image_gamma' ,
33+ iTXt : 'intl_text_data' ,
3334 sBIT : 'significant_bits' ,
3435 tEXt : 'textual_data' ,
3536 tRNS : 'transparency' ,
@@ -203,6 +204,25 @@ export class PngCompressedTextualDataEvent extends Event {
203204 }
204205}
205206
207+ /**
208+ * @typedef PngIntlTextualData
209+ * @property {string } keyword
210+ * @property {number } compressionFlag 0 for uncompressed, 1 for compressed.
211+ * @property {number } compressionMethod 0 means zlib defalt when compressionFlag is 1.
212+ * @property {string= } languageTag
213+ * @property {string= } translatedKeyword
214+ * @property {Uint8Array } text The raw UTF-8 text (may be compressed).
215+ */
216+
217+ export class PngIntlTextualDataEvent extends Event {
218+ /** @param {PngIntlTextualData } intlTextualdata */
219+ constructor ( intlTextualdata ) {
220+ super ( PngParseEventType . iTXt ) ;
221+ /** @type {PngIntlTextualData } */
222+ this . intlTextualdata = intlTextualdata ;
223+ }
224+ }
225+
206226/**
207227 * @typedef PngChunk Internal use only.
208228 * @property {number } length
@@ -288,6 +308,16 @@ export class PngParser extends EventTarget {
288308 return this ;
289309 }
290310
311+ /**
312+ * Type-safe way to bind a listener for a PngIntlTextualDataEvent.
313+ * @param {function(PngIntlTextualDataEvent): void } listener
314+ * @returns {PngParser } for chaining
315+ */
316+ onIntlTextualData ( listener ) {
317+ super . addEventListener ( PngParseEventType . iTXt , listener ) ;
318+ return this ;
319+ }
320+
291321 /**
292322 * Type-safe way to bind a listener for a PngPaletteEvent.
293323 * @param {function(PngPaletteEvent): void } listener
@@ -520,6 +550,29 @@ export class PngParser extends EventTarget {
520550 this . dispatchEvent ( new PngCompressedTextualDataEvent ( compressedTextualData ) ) ;
521551 break ;
522552
553+ // https://www.w3.org/TR/png-3/#11iTXt
554+ case 'iTXt' :
555+ const intlByteArr = chStream . peekBytes ( length ) ;
556+ const intlNull0 = intlByteArr . indexOf ( 0 ) ;
557+ const intlNull1 = intlByteArr . indexOf ( 0 , intlNull0 + 1 ) ;
558+ const intlNull2 = intlByteArr . indexOf ( 0 , intlNull1 + 1 ) ;
559+ if ( intlNull0 === - 1 ) throw `iTXt: Did not have one null` ;
560+ if ( intlNull1 === - 1 ) throw `iTXt: Did not have two nulls` ;
561+ if ( intlNull2 === - 1 ) throw `iTXt: Did not have three nulls` ;
562+
563+ /** @type {PngIntlTextualData } */
564+ const intlTextData = {
565+ keyword : chStream . readString ( intlNull0 ) ,
566+ compressionFlag : chStream . skip ( 1 ) . readNumber ( 1 ) ,
567+ compressionMethod : chStream . readNumber ( 1 ) ,
568+ languageTag : ( intlNull1 - intlNull0 > 1 ) ? chStream . readString ( intlNull1 - intlNull0 - 1 ) : undefined ,
569+ translatedKeyword : ( intlNull2 - intlNull1 > 1 ) ? chStream . skip ( 1 ) . readString ( intlNull2 - intlNull1 - 1 ) : undefined ,
570+ text : chStream . skip ( 1 ) . readBytes ( length - intlNull2 - 1 ) ,
571+ } ;
572+
573+ this . dispatchEvent ( new PngIntlTextualDataEvent ( intlTextData ) ) ;
574+ break ;
575+
523576 // https://www.w3.org/TR/png-3/#11IDAT
524577 case 'IDAT' :
525578 /** @type {PngImageData } */
@@ -595,6 +648,9 @@ async function main() {
595648 parser . onCompressedTextualData ( evt => {
596649 // console.dir(evt.compressedTextualData);
597650 } ) ;
651+ parser . onIntlTextualData ( evt => {
652+ // console.dir(evt.intlTextualdata);
653+ } ) ;
598654
599655 try {
600656 await parser . start ( ) ;
0 commit comments