Dpi van png in javascript
Wat is dpi
De grote van een digitale afbeelding verschilt van een geprinte afbeelding. Dit wordt aangegeven met de dpi van de afbeelding. Dpi staat voor dots per inch, ook wel uitgedrukt in ppi (pixels per inch) of ppc (pixels per centimeter). Met behulp van deze verhouding kan uitgerekend worden wat de geprinte grote van een afbeelding zal worden.
Bij het vertonen van afbeeldingen op een website is de dpi normaal gesproken niet belangrijk. Echter wanneer de afbeelding vervolgens uitgeprint wordt is deze waarde ineens wel van belang. Ook bij het printvoorbeeld zal hier rekening mee moeten worden gehouden.
De dpi staat in het afbeelding bestand beschreven en heeft anders een standaard waarde van 72. Meestal wordt deze waarde opgehaald met behulp van een code bibliotheken. Deze was echter nog niet aanwezig voor javascript waardoor de waarde er handmatig uitgehaald moest worden.
Een andere kijk op een afbeelding
In een afbeelding bestand zoals png staat vaak meer informatie dan alleen de afbeelding zelf. Zo is hierin het bestandstype, de grote, de kleuren waardes, de transparentie en nog vele andere gegevens terug te vinden. De details hiervan zijn terug te vinden op de Libpng specificaties website
De gegevens die hier echter belangrijk zijn zijn de pHYs wat komt van Physical pixel dimensions. De 8 bytes die hierna komen beschrijven de afmetingen van de afbeelding voor de horizontale en verticale as. Hierna komt nog een byte die de eenheid van de opgegeven afmetingen aangeeft. Over het algemeen komen hier maar twee verschillende waardes voor: 0 (eenheid onbekend) en 1 (afmeting in meters). Let op, omdat de afmeting in meters is, is dit dus niet de dpi maar de ppm van de afbeelding. Deze moet eerst nog omgerekend worden.
Figuur 1 - Voorbeeld png afbeelding | |
Figuur 2 - Een klein deel van figuur 1 als hexidecimals in hexed. (let ook op de pHYs waarde in de rechter kolom) |
Code
Om deze gegevens in javascript uit te lezen wordt de afbeelding eerst omgezet naar een 8 bit array. Vervolgens wordt door alle waarden geloopt opzoek naar de achtereenvolgende waarden p H Y s. Als deze gevonden zijn worden de volgende 8 waarden uitgelezen en omgezet naar 2 decimale waarden. Hierna wordt gecontroleerd of de waarden in meters zijn opgegeven, en worden deze vervolgens omgezet naar inches om zo de dpi te verkrijgen.
Verder wordt gezocht naar de waarde IDAT wat het begin van de daadwerkelijke afbeelding aangeeft. Deze waarde is altijd aanwezig in een png en komt altijd na de pHYs als deze aanwezig is. Op deze manier hoeft niet de complete afbeelding doorzocht te worden, mochten de fysieke afmetingen niet gespecificeerd zijn.
// get dpi from a png file (using a html5 file althought any type of png convertable to a bit array will work)
var reader = new FileReader();
reader.onload = function() {
// retrieve and convert the image to a bit array
var arrayBuffer = this.result;
var array = new Uint8Array(arrayBuffer);
// loop through all bits
for (bit = 0; bit < array.length - 1; bit++) {
// check for pHYs
if (testForPHYs(array, bit)) {
// get dpi
var dpi = readDpi(array, index + 4);
// use / apply the found dpi
setDpi(dpi);
// break if found
return;
// check for IDAT
} else if (testForIDAT(array, bit)) {
// break if found
return;
}
}
}
// start the filereader once its loaded
reader.readAsArrayBuffer(file);
// test the bit at index + the 3 next bits
function testForPHYs(array, index) {
// test for pHYs in order
if (array[index] == 112 && // p
array[index + 1] == 72 && // H
array[index + 2] == 89 && // Y
array[index + 3] == 115) { // s
// found
return true;
}
// incorrect sequence, not found
return false;
}
// test the bit at index + the 3 next bits
function testForIDAT(array, index) {
// test for IDAT in order
if (array[index] == 73 && // I
array[index + 1] == 68 && // D
array[index + 2] == 65 && // A
array[index + 3] == 84) { // T
// found
return true;
}
// incorrect sequence, not found
return false;
}
// read the dpi (starting at the first bit after pHYs)
function readDpi(array, index) {
// read the next 4 bits and merge them using bitshifts
var ppm = (array[index] << 24) +
(array[index + 1] << 16) +
(array[index + 2] << 8) +
(array[index + 3]);
// convert pixels per meter to dpi
var dpi = ppm / 100 / 0.393700787;
// return the dpi
return dpi;
}