Skip to content

Instantly share code, notes, and snippets.

@Himura2la
Last active August 26, 2021 10:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Himura2la/8431024224179325e7e28d52a4bc96e5 to your computer and use it in GitHub Desktop.
Save Himura2la/8431024224179325e7e28d52a4bc96e5 to your computer and use it in GitHub Desktop.
Embed text into a PNG file (by draw.io project)
// Source: https://github.com/jgraph/drawio/blob/v14.9.9/src/main/webapp/electron.js#L970
//NOTE: Key length must not be longer than 79 bytes (not checked)
function writePngWithText(origBuff, key, text, compressed, base64encoded)
{
var isDpi = key == 'dpi';
var inOffset = 0;
var outOffset = 0;
var data = text;
var dataLen = isDpi? 9 : key.length + data.length + 1; //we add 1 zeros with non-compressed data, for pHYs it's 2 of 4-byte-int + 1 byte
//prepare compressed data to get its size
if (compressed)
{
data = zlib.deflateRawSync(encodeURIComponent(text));
dataLen = key.length + data.length + 2; //we add 2 zeros with compressed data
}
var outBuff = Buffer.allocUnsafe(origBuff.length + dataLen + 4); //4 is the header size "zTXt", "tEXt" or "pHYs"
try
{
var magic1 = origBuff.readUInt32BE(inOffset);
inOffset += 4;
var magic2 = origBuff.readUInt32BE(inOffset);
inOffset += 4;
if (magic1 != 0x89504e47 && magic2 != 0x0d0a1a0a)
{
throw new Error("PNGImageDecoder0");
}
outBuff.writeUInt32BE(magic1, outOffset);
outOffset += 4;
outBuff.writeUInt32BE(magic2, outOffset);
outOffset += 4;
}
catch (e)
{
log.error(e.message, {stack: e.stack});
throw new Error("PNGImageDecoder1");
}
try
{
while (inOffset < origBuff.length)
{
var length = origBuff.readInt32BE(inOffset);
inOffset += 4;
var type = origBuff.readInt32BE(inOffset)
inOffset += 4;
if (type == PNG_CHUNK_IDAT)
{
// Insert zTXt chunk before IDAT chunk
outBuff.writeInt32BE(dataLen, outOffset);
outOffset += 4;
var typeSignature = isDpi? 'pHYs' : (compressed ? "zTXt" : "tEXt");
outBuff.write(typeSignature, outOffset);
outOffset += 4;
if (isDpi)
{
var dpm = Math.round(parseInt(text) / 0.0254) || 3937; //One inch is equal to exactly 0.0254 meters. 3937 is 100dpi
outBuff.writeInt32BE(dpm, outOffset);
outBuff.writeInt32BE(dpm, outOffset + 4);
outBuff.writeInt8(1, outOffset + 8);
outOffset += 9;
data = Buffer.allocUnsafe(9);
data.writeInt32BE(dpm, 0);
data.writeInt32BE(dpm, 4);
data.writeInt8(1, 8);
}
else
{
outBuff.write(key, outOffset);
outOffset += key.length;
outBuff.writeInt8(0, outOffset);
outOffset ++;
if (compressed)
{
outBuff.writeInt8(0, outOffset);
outOffset ++;
data.copy(outBuff, outOffset);
}
else
{
outBuff.write(data, outOffset);
}
outOffset += data.length;
}
var crcVal = 0xffffffff;
crcVal = crc.crcjam(typeSignature, crcVal);
crcVal = crc.crcjam(data, crcVal);
// CRC
outBuff.writeInt32BE(crcVal ^ 0xffffffff, outOffset);
outOffset += 4;
// Writes the IDAT chunk after the zTXt
outBuff.writeInt32BE(length, outOffset);
outOffset += 4;
outBuff.writeInt32BE(type, outOffset);
outOffset += 4;
origBuff.copy(outBuff, outOffset, inOffset);
// Encodes the buffer using base64 if requested
return base64encoded? outBuff.toString('base64') : outBuff;
}
outBuff.writeInt32BE(length, outOffset);
outOffset += 4;
outBuff.writeInt32BE(type, outOffset);
outOffset += 4;
origBuff.copy(outBuff, outOffset, inOffset, inOffset + length + 4);// +4 to move past the crc
inOffset += length + 4;
outOffset += length + 4;
}
}
catch (e)
{
log.error(e.message, {stack: e.stack});
throw e;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment