Last active
August 26, 2021 10:03
-
-
Save Himura2la/8431024224179325e7e28d52a4bc96e5 to your computer and use it in GitHub Desktop.
Embed text into a PNG file (by draw.io project)
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
// 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