Last active
January 11, 2023 09:29
-
-
Save shameen/18fdc4246f11df09e6039737f275fcf0 to your computer and use it in GitHub Desktop.
png: add iTXt chunk metadata (javascript / node.js / typescript)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import fs from "node:fs"; | |
import extract from "png-chunks-extract"; | |
import encode from "png-chunks-encode"; | |
/** Using png-chunks-extract + png-chunks-encode */ | |
function add_itxt_pngchunk( | |
fileBytes: Uint8Array | Buffer, | |
value: string, | |
keyword: string | |
) { | |
const chunks = extract(fileBytes); | |
const nul = String.fromCharCode(0x00); | |
/* iTXt chunk as defined by PNG (http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.iTXt) | |
Keyword: 1-79 bytes (character string) | |
Null separator: 1 byte (0x00) | |
Compression flag: 1 byte (0x00 off, 0x01 on) | |
Compression method: 1 byte (0x00 = deflate) | |
Language tag: 0 or more bytes (character string) | |
Null separator: 1 byte (0x00) | |
Translated keyword: 0 or more bytes | |
Null separator: 1 byte (0x00) | |
Text: 0 or more bytes | |
*/ | |
const iTXtValue = `${keyword}${nul}${nul}${nul}${nul}${nul}${value}`; | |
const chunkNew = { | |
name: "iTXt", | |
data: new TextEncoder().encode(iTXtValue), | |
}; | |
//add chunk as the second-last item (before the "IEND") | |
chunks.splice(-1, 0, chunkNew); | |
// join | |
const pngNew = Buffer.from(encode(chunks)); | |
return pngNew; | |
} | |
const simplePngBytes = Buffer.from([ | |
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, | |
0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, | |
0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xde, 0x00, 0x00, 0x00, 0x10, 0x49, 0x44, | |
0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xcf, 0xc0, 0x00, 0x00, 0x03, 0x01, 0x01, | |
0x00, 0x18, 0xdd, 0x8d, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, | |
0xae, 0x42, 0x60, 0x82, | |
]); | |
const pngNew = add_itxt_pngchunk( | |
simplePngBytes, | |
JSON.stringify({ score: 100 }), | |
"myKeyword" | |
); | |
// save to file | |
fs.writeFileSync("output3.png", pngNew, "binary"); | |
//The resulting PNG file can be verified using https://www.dcode.fr/png-chunks |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Stream, Readable } from 'node:stream'; | |
import fs from 'node:fs'; | |
import pngitxt from "png-itxt"; | |
/** Using png-itxt */ | |
async function add_itxt_pngitxt( | |
inputStream: Stream, | |
value: string, | |
keyword: string | |
): Promise<Stream> { | |
return inputStream.pipe( | |
pngitxt.set({ | |
keyword, | |
compressed: false, | |
//language: '', | |
//translated: '', | |
value, | |
}) | |
); | |
} | |
const simplePngBytes = Buffer.from([ | |
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, | |
0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, | |
0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xde, 0x00, 0x00, 0x00, 0x10, 0x49, 0x44, | |
0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xcf, 0xc0, 0x00, 0x00, 0x03, 0x01, 0x01, | |
0x00, 0x18, 0xdd, 0x8d, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, | |
0xae, 0x42, 0x60, 0x82, | |
]); | |
//Convert Buffer to Stream | |
const inputStream = new Readable({ | |
read() { | |
this.push(simplePngBytes); | |
this.push(null); | |
}, | |
}); | |
const pngNewStream = await add_itxt_pngitxt( | |
inputStream, | |
JSON.stringify({ score: 100 }), | |
"myKeyword" | |
); | |
// save to file | |
pngNewStream.pipe(fs.createWriteStream("output.png")); | |
//The resulting PNG file can be verified using https://www.dcode.fr/png-chunks |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pngMetadata from "png-metadata"; | |
import fs from "node:fs"; | |
/** Using png-metadata */ | |
function add_itxt_pngmetadata( | |
fileBytes: Uint8Array | Buffer, | |
value: string, | |
keyword: string | |
): string { | |
// split | |
const list = pngMetadata.splitChunk( | |
Buffer.from(fileBytes).toString("binary") | |
); | |
console.debug("metadata list: ", list); | |
// append metadata | |
const nul = String.fromCharCode(0x00); | |
/* iTXt chunk as defined by PNG (http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.iTXt) | |
Keyword: 1-79 bytes (character string) | |
Null separator: 1 byte (0x00) | |
Compression flag: 1 byte (0x00 off, 0x01 on) | |
Compression method: 1 byte (0x00 = deflate) | |
Language tag: 0 or more bytes (character string) | |
Null separator: 1 byte (0x00) | |
Translated keyword: 0 or more bytes | |
Null separator: 1 byte (0x00) | |
Text: 0 or more bytes | |
*/ | |
const chunkNew = pngMetadata.createChunk( | |
"iTXt", | |
`${keyword}${nul}${nul}${nul}${nul}${nul}${value}` | |
); | |
list.splice(-1, 0, chunkNew); | |
// join | |
return pngMetadata.joinChunk(list); | |
} | |
const simplePngBytes = Buffer.from([ | |
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, | |
0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, | |
0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xde, 0x00, 0x00, 0x00, 0x10, 0x49, 0x44, | |
0x41, 0x54, 0x08, 0xd7, 0x63, 0xf8, 0xcf, 0xc0, 0x00, 0x00, 0x03, 0x01, 0x01, | |
0x00, 0x18, 0xdd, 0x8d, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, | |
0xae, 0x42, 0x60, 0x82, | |
]); | |
const pngNew = add_itxt_pngmetadata( | |
simplePngBytes, | |
JSON.stringify({ score: 100 }), | |
"myKeyword" | |
); | |
//save to file | |
const filePath = "output2.png"; | |
fs.writeFileSync(filePath, pngNew, "binary"); | |
//check (Can also be verified using https://www.dcode.fr/png-chunks ) | |
var s = pngMetadata.readFileSync(filePath); | |
const chunks = pngMetadata.splitChunk(s); | |
console.log("metadata chunks: ", chunks); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment