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

PngParser: Add support for zTXt chunk

This commit is contained in:
Jeff Schiller 2024-01-17 01:54:28 -08:00
parent afb1a67f2d
commit ade8f1a9ab
2 changed files with 59 additions and 1 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 bKGD, eXIf, hIST, iTXt, pHYs, sPLT, tIME, zTXt. // TODO: Ancillary chunks bKGD, eXIf, hIST, iTXt, pHYs, sPLT, tIME.
// let DEBUG = true; // let DEBUG = true;
let DEBUG = false; let DEBUG = false;
@ -33,6 +33,7 @@ export const PngParseEventType = {
sBIT: 'significant_bits', sBIT: 'significant_bits',
tEXt: 'textual_data', tEXt: 'textual_data',
tRNS: 'transparency', tRNS: 'transparency',
zTXt: 'compressed_textual_data',
}; };
/** @enum {number} */ /** @enum {number} */
@ -186,6 +187,22 @@ export class PngTextualDataEvent extends Event {
} }
} }
/**
* @typedef PngCompressedTextualData
* @property {string} keyword
* @property {number} compressionMethod Only value supported is 0 for deflate compression.
* @property {Uint8Array=} compressedText
*/
export class PngCompressedTextualDataEvent extends Event {
/** @param {PngCompressedTextualData} compressedTextualData */
constructor(compressedTextualData) {
super(PngParseEventType.zTXt);
/** @type {PngCompressedTextualData} */
this.compressedTextualData = compressedTextualData;
}
}
/** /**
* @typedef PngChunk Internal use only. * @typedef PngChunk Internal use only.
* @property {number} length * @property {number} length
@ -231,6 +248,16 @@ export class PngParser extends EventTarget {
return this; return this;
} }
/**
* Type-safe way to bind a listener for a PngCompressedTextualDataEvent.
* @param {function(PngCompressedTextualDataEvent): void} listener
* @returns {PngParser} for chaining
*/
onCompressedTextualData(listener) {
super.addEventListener(PngParseEventType.zTXt, listener);
return this;
}
/** /**
* Type-safe way to bind a listener for a PngImageGammaEvent. * Type-safe way to bind a listener for a PngImageGammaEvent.
* @param {function(PngImageGammaEvent): void} listener * @param {function(PngImageGammaEvent): void} listener
@ -479,6 +506,20 @@ export class PngParser extends EventTarget {
this.dispatchEvent(new PngTransparencyEvent(transparency)); this.dispatchEvent(new PngTransparencyEvent(transparency));
break; break;
// https://www.w3.org/TR/png-3/#11zTXt
case 'zTXt':
const compressedByteArr = chStream.peekBytes(length);
const compressedNullIndex = compressedByteArr.indexOf(0);
/** @type {PngCompressedTextualData} */
const compressedTextualData = {
keyword: chStream.readString(compressedNullIndex),
compressionMethod: chStream.skip(1).readNumber(1),
compressedText: chStream.readBytes(length - compressedNullIndex - 2),
};
this.dispatchEvent(new PngCompressedTextualDataEvent(compressedTextualData));
break;
// https://www.w3.org/TR/png-3/#11IDAT // https://www.w3.org/TR/png-3/#11IDAT
case 'IDAT': case 'IDAT':
/** @type {PngImageData} */ /** @type {PngImageData} */
@ -551,6 +592,9 @@ async function main() {
parser.onTextualData(evt => { parser.onTextualData(evt => {
// console.dir(evt.textualData); // console.dir(evt.textualData);
}); });
parser.onCompressedTextualData(evt => {
// console.dir(evt.compressedTextualData);
});
try { try {
await parser.start(); await parser.start();

View file

@ -4,6 +4,7 @@ import { expect } from 'chai';
import { PngColorType, PngInterlaceMethod, PngParser } from '../image/parsers/png.js'; import { PngColorType, PngInterlaceMethod, PngParser } from '../image/parsers/png.js';
/** @typedef {import('../image/parsers/png.js').PngChromaticies} PngChromaticies */ /** @typedef {import('../image/parsers/png.js').PngChromaticies} PngChromaticies */
/** @typedef {import('../image/parsers/png.js').PngCompressedTextualData} PngCompressedTextualData */
/** @typedef {import('../image/parsers/png.js').PngImageData} PngImageData */ /** @typedef {import('../image/parsers/png.js').PngImageData} PngImageData */
/** @typedef {import('../image/parsers/png.js').PngImageGamma} PngImageGamma */ /** @typedef {import('../image/parsers/png.js').PngImageGamma} PngImageGamma */
/** @typedef {import('../image/parsers/png.js').PngImageHeader} PngImageHeader */ /** @typedef {import('../image/parsers/png.js').PngImageHeader} PngImageHeader */
@ -166,4 +167,17 @@ describe('bitjs.image.parsers.PngParser', () => {
expect(textualDataArr[1].keyword).equals('Author'); expect(textualDataArr[1].keyword).equals('Author');
expect(textualDataArr[1].textString).equals('Willem A.J. van Schaik\n(willem@schaik.com)'); expect(textualDataArr[1].textString).equals('Willem A.J. van Schaik\n(willem@schaik.com)');
}); });
it('extracts zTXt', async () => {
/** @type {PngCompressedTextualData} */
let data;
await getPngParser('tests/image-testfiles/ctzn0g04.png')
.onCompressedTextualData(evt => { data = evt.compressedTextualData })
.start();
expect(data.keyword).equals('Disclaimer');
expect(data.compressionMethod).equals(0);
expect(data.compressedText.byteLength).equals(17);
});
}); });