UNPKG

changedpi

Version:

Quickly change the dpi for an image without re enconding

193 lines (175 loc) 6.39 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.changeDpiBlob = changeDpiBlob; exports.changeDpiDataUrl = changeDpiDataUrl; function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } function createPngDataTable() { /* Table of CRCs of all 8-bit messages. */ var crcTable = new Int32Array(256); for (var n = 0; n < 256; n++) { var c = n; for (var k = 0; k < 8; k++) { c = c & 1 ? 0xedb88320 ^ c >>> 1 : c >>> 1; } crcTable[n] = c; } return crcTable; } function calcCrc(buf) { var c = -1; if (!pngDataTable) pngDataTable = createPngDataTable(); for (var n = 0; n < buf.length; n++) { c = pngDataTable[(c ^ buf[n]) & 0xFF] ^ c >>> 8; } return c ^ -1; } var pngDataTable = void 0; var PNG = 'image/png'; var JPEG = 'image/jpeg'; // those are 3 possible signature of the physBlock in base64. // the pHYs signature block is preceed by the 4 bytes of lenght. The length of // the block is always 9 bytes. So a phys block has always this signature: // 0 0 0 9 p H Y s. // However the data64 encoding aligns we will always find one of those 3 strings. // this allow us to find this particular occurence of the pHYs block without // converting from b64 back to string var b64PhysSignature1 = 'AAlwSFlz'; var b64PhysSignature2 = 'AAAJcEhZ'; var b64PhysSignature3 = 'AAAACXBI'; var _P = 'p'.charCodeAt(0); var _H = 'H'.charCodeAt(0); var _Y = 'Y'.charCodeAt(0); var _S = 's'.charCodeAt(0); function changeDpiBlob(blob, dpi) { // 33 bytes are ok for pngs and jpegs // to contain the information. var headerChunk = blob.slice(0, 33); return new Promise(function (resolve, reject) { var fileReader = new FileReader(); fileReader.onload = function () { var dataArray = new Uint8Array(fileReader.result); var tail = blob.slice(33); var changedArray = changeDpiOnArray(dataArray, dpi, blob.type); resolve(new Blob([changedArray, tail], { type: blob.type })); }; fileReader.readAsArrayBuffer(headerChunk); }); } function changeDpiDataUrl(base64Image, dpi) { var dataSplitted = base64Image.split(','); var format = dataSplitted[0]; var body = dataSplitted[1]; var type = void 0; var headerLength = void 0; var overwritepHYs = false; if (format.indexOf(PNG) !== -1) { type = PNG; var b64Index = detectPhysChunkFromDataUrl(body); // 28 bytes in dataUrl are 21bytes, length of phys chunk with everything inside. if (b64Index >= 0) { headerLength = Math.ceil((b64Index + 28) / 3) * 4; overwritepHYs = true; } else { headerLength = 33 / 3 * 4; } } if (format.indexOf(JPEG) !== -1) { type = JPEG; headerLength = 18 / 3 * 4; } // 33 bytes are ok for pngs and jpegs // to contain the information. var stringHeader = body.substring(0, headerLength); var restOfData = body.substring(headerLength); var headerBytes = atob(stringHeader); var dataArray = new Uint8Array(headerBytes.length); for (var i = 0; i < dataArray.length; i++) { dataArray[i] = headerBytes.charCodeAt(i); } var finalArray = changeDpiOnArray(dataArray, dpi, type, overwritepHYs); var base64Header = btoa(String.fromCharCode.apply(String, _toConsumableArray(finalArray))); return [format, ',', base64Header, restOfData].join(''); } function detectPhysChunkFromDataUrl(data) { var b64index = data.indexOf(b64PhysSignature1); if (b64index === -1) { b64index = data.indexOf(b64PhysSignature2); } if (b64index === -1) { b64index = data.indexOf(b64PhysSignature3); } // if b64index === -1 chunk is not found return b64index; } function searchStartOfPhys(data) { var length = data.length - 1; // we check from the end since we cut the string in proximity of the header // the header is within 21 bytes from the end. for (var i = length; i >= 4; i--) { if (data[i - 4] === 9 && data[i - 3] === _P && data[i - 2] === _H && data[i - 1] === _Y && data[i] === _S) { return i - 3; } } } function changeDpiOnArray(dataArray, dpi, format, overwritepHYs) { if (format === JPEG) { dataArray[13] = 1; // 1 pixel per inch or 2 pixel per cm dataArray[14] = dpi >> 8; // dpiX high byte dataArray[15] = dpi & 0xff; // dpiX low byte dataArray[16] = dpi >> 8; // dpiY high byte dataArray[17] = dpi & 0xff; // dpiY low byte return dataArray; } if (format === PNG) { var physChunk = new Uint8Array(13); // chunk header pHYs // 9 bytes of data // 4 bytes of crc // this multiplication is because the standard is dpi per meter. dpi *= 39.3701; physChunk[0] = _P; physChunk[1] = _H; physChunk[2] = _Y; physChunk[3] = _S; physChunk[4] = dpi >>> 24; // dpiX highest byte physChunk[5] = dpi >>> 16; // dpiX veryhigh byte physChunk[6] = dpi >>> 8; // dpiX high byte physChunk[7] = dpi & 0xff; // dpiX low byte physChunk[8] = physChunk[4]; // dpiY highest byte physChunk[9] = physChunk[5]; // dpiY veryhigh byte physChunk[10] = physChunk[6]; // dpiY high byte physChunk[11] = physChunk[7]; // dpiY low byte physChunk[12] = 1; // dot per meter.... var crc = calcCrc(physChunk); var crcChunk = new Uint8Array(4); crcChunk[0] = crc >>> 24; crcChunk[1] = crc >>> 16; crcChunk[2] = crc >>> 8; crcChunk[3] = crc & 0xff; if (overwritepHYs) { var startingIndex = searchStartOfPhys(dataArray); dataArray.set(physChunk, startingIndex); dataArray.set(crcChunk, startingIndex + 13); return dataArray; } else { // i need to give back an array of data that is divisible by 3 so that // dataurl encoding gives me integers, for luck this chunk is 17 + 4 = 21 // if it was we could add a text chunk contaning some info, untill desired // length is met. // chunk structur 4 bytes for length is 9 var chunkLength = new Uint8Array(4); chunkLength[0] = 0; chunkLength[1] = 0; chunkLength[2] = 0; chunkLength[3] = 9; var finalHeader = new Uint8Array(54); finalHeader.set(dataArray, 0); finalHeader.set(chunkLength, 33); finalHeader.set(physChunk, 37); finalHeader.set(crcChunk, 50); return finalHeader; } } }