Skip to content

Commit ade8f1a

Browse files
committed
PngParser: Add support for zTXt chunk
1 parent afb1a67 commit ade8f1a

File tree

2 files changed

+59
-1
lines changed

2 files changed

+59
-1
lines changed

image/parsers/png.js

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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, zTXt.
17+
// TODO: Ancillary chunks bKGD, eXIf, hIST, iTXt, pHYs, sPLT, tIME.
1818

1919
// let DEBUG = true;
2020
let DEBUG = false;
@@ -33,6 +33,7 @@ export const PngParseEventType = {
3333
sBIT: 'significant_bits',
3434
tEXt: 'textual_data',
3535
tRNS: 'transparency',
36+
zTXt: 'compressed_textual_data',
3637
};
3738

3839
/** @enum {number} */
@@ -186,6 +187,22 @@ export class PngTextualDataEvent extends Event {
186187
}
187188
}
188189

190+
/**
191+
* @typedef PngCompressedTextualData
192+
* @property {string} keyword
193+
* @property {number} compressionMethod Only value supported is 0 for deflate compression.
194+
* @property {Uint8Array=} compressedText
195+
*/
196+
197+
export class PngCompressedTextualDataEvent extends Event {
198+
/** @param {PngCompressedTextualData} compressedTextualData */
199+
constructor(compressedTextualData) {
200+
super(PngParseEventType.zTXt);
201+
/** @type {PngCompressedTextualData} */
202+
this.compressedTextualData = compressedTextualData;
203+
}
204+
}
205+
189206
/**
190207
* @typedef PngChunk Internal use only.
191208
* @property {number} length
@@ -231,6 +248,16 @@ export class PngParser extends EventTarget {
231248
return this;
232249
}
233250

251+
/**
252+
* Type-safe way to bind a listener for a PngCompressedTextualDataEvent.
253+
* @param {function(PngCompressedTextualDataEvent): void} listener
254+
* @returns {PngParser} for chaining
255+
*/
256+
onCompressedTextualData(listener) {
257+
super.addEventListener(PngParseEventType.zTXt, listener);
258+
return this;
259+
}
260+
234261
/**
235262
* Type-safe way to bind a listener for a PngImageGammaEvent.
236263
* @param {function(PngImageGammaEvent): void} listener
@@ -479,6 +506,20 @@ export class PngParser extends EventTarget {
479506
this.dispatchEvent(new PngTransparencyEvent(transparency));
480507
break;
481508

509+
// https://www.w3.org/TR/png-3/#11zTXt
510+
case 'zTXt':
511+
const compressedByteArr = chStream.peekBytes(length);
512+
const compressedNullIndex = compressedByteArr.indexOf(0);
513+
514+
/** @type {PngCompressedTextualData} */
515+
const compressedTextualData = {
516+
keyword: chStream.readString(compressedNullIndex),
517+
compressionMethod: chStream.skip(1).readNumber(1),
518+
compressedText: chStream.readBytes(length - compressedNullIndex - 2),
519+
};
520+
this.dispatchEvent(new PngCompressedTextualDataEvent(compressedTextualData));
521+
break;
522+
482523
// https://www.w3.org/TR/png-3/#11IDAT
483524
case 'IDAT':
484525
/** @type {PngImageData} */
@@ -551,6 +592,9 @@ async function main() {
551592
parser.onTextualData(evt => {
552593
// console.dir(evt.textualData);
553594
});
595+
parser.onCompressedTextualData(evt => {
596+
// console.dir(evt.compressedTextualData);
597+
});
554598

555599
try {
556600
await parser.start();

tests/image-parsers-png.spec.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { expect } from 'chai';
44
import { PngColorType, PngInterlaceMethod, PngParser } from '../image/parsers/png.js';
55

66
/** @typedef {import('../image/parsers/png.js').PngChromaticies} PngChromaticies */
7+
/** @typedef {import('../image/parsers/png.js').PngCompressedTextualData} PngCompressedTextualData */
78
/** @typedef {import('../image/parsers/png.js').PngImageData} PngImageData */
89
/** @typedef {import('../image/parsers/png.js').PngImageGamma} PngImageGamma */
910
/** @typedef {import('../image/parsers/png.js').PngImageHeader} PngImageHeader */
@@ -166,4 +167,17 @@ describe('bitjs.image.parsers.PngParser', () => {
166167
expect(textualDataArr[1].keyword).equals('Author');
167168
expect(textualDataArr[1].textString).equals('Willem A.J. van Schaik\n([email protected])');
168169
});
170+
171+
it('extracts zTXt', async () => {
172+
/** @type {PngCompressedTextualData} */
173+
let data;
174+
175+
await getPngParser('tests/image-testfiles/ctzn0g04.png')
176+
.onCompressedTextualData(evt => { data = evt.compressedTextualData })
177+
.start();
178+
179+
expect(data.keyword).equals('Disclaimer');
180+
expect(data.compressionMethod).equals(0);
181+
expect(data.compressedText.byteLength).equals(17);
182+
});
169183
});

0 commit comments

Comments
 (0)