5

I need to change the resolution/density in JPG/PNG type of images in javascript. The reason I need to do this is so that I can send the image to a third party API which will then know how many pixels per inch (DPI/PPI) to print based on the resolution/density metadata.

Are there any such solution in javascript?

andre
  • 1,660
  • 3
  • 19
  • 31
  • No. This is done via your graphics editor to the image itself. It is not possible to alter the image this way via programming. – Scott Marcus Oct 01 '17 at 00:44
  • A solution is available on this page. - https://stackoverflow.com/questions/20379027/javascript-reduce-the-size-and-quality-of-image-with-based64-encoded-code – Damien Oct 01 '17 at 00:44
  • 1
    @ScottMarcus why is not possible? I see see examples of where software adds resolution/density metadata to image conversions from specific files. One example is PDF to JPEG in this link: https://www.filestack.com/docs/document-transformations ... Another example is setting density in an SVG image which can then be converted to JPEG: http://sharp.dimens.io/en/stable/api-constructor/ ... In my case I already have an uploaded JPEG image in which I need to change density. I cannot convert it to SVG or PDF and then back to JPG. – andre Oct 01 '17 at 00:51
  • 1
    Isn't it simply metadata? – andre Oct 01 '17 at 00:54
  • See [canvas.toDataURL() does not alter image quality. How comes?](https://stackoverflow.com/questions/46436237/canvas-todataurl-does-not-alter-image-quality-how-comes/) – guest271314 Oct 01 '17 at 01:22
  • In the case of JPEG, you'd need to specify a file format. JPEG itself has no concept of density. – user3344003 Oct 01 '17 at 02:52
  • @user3344003 isn't JPEG a file format? – andre Oct 01 '17 at 04:06
  • In ImageMagick, it is possible to add density to the image per this link: https://superuser.com/questions/479197/i-want-to-change-dpi-with-imagemagick-without-changing-the-actual-byte-size-of-t ... so it is achievable, but I prefer to stick to a simpler solution. I don't want to run a whole image magic instance to add one simple piece of metadata. – andre Oct 01 '17 at 04:08
  • JPEG is not a file format. – user3344003 Oct 01 '17 at 04:15
  • 1
    Adding the DPI as metadata does nothing to actually modify the image. Saying that your image has 128 DPI does not make it so, the image must actually be that dense. Only graphics software can modify the actual image density. As mentioned in the link that @guest271314 posted: *" DPI is purely arbitrarily when saved as meta in/with images and function as a hint when transferred to physical medium such as a screen or to paper (and the entire pipe-line displaying the image considers its DPI)."* – Scott Marcus Oct 01 '17 at 14:02
  • Of course it can be "changed programmatically". Otherwise an image editor program wouldn't be able to do it. Also the density is metadata. There is no such things as an "actual density" other than this metadata value. I think you are confusing the density/resolution (how many pixels there are in certain physical unit in print) with the width and height in pixels. – Eneroth3 Dec 07 '19 at 20:56

4 Answers4

5

For anyone curious on a solution, I ended up using graphicMagic (node's version of Image Magick). Because I am using AWS Lambda (whose instances comes preinstalled with ImageMagic), it made it easier, I just had to install 'gm' npm package.

It isn't the most performant solution because I have to resize after resample, but it works!

const gm = require('gm').subClass({imageMagick: true});

function addResolution(inputBuffer, resizeWidth, resizeHeight) {
  return new Promise((resolve, reject) =>{

    gm(inputBuffer)
    .resample(150, 150) // resampled to 150 resolution
    // you have to set the width and height again because resample rearranges those params
    .resize(resizeWidth, resizeHeight, '!')
    .toBuffer('JPEG',function (err, buffer) {
      if (err) reject(err)
      resolve(buffer)
    })
  })
}
andre
  • 1,660
  • 3
  • 19
  • 31
  • Why not just resize and then set the desired density? – fmw42 Oct 01 '17 at 18:45
  • @fmw42 'resample' changes the height and width of an image in addition to changing the resolution/density so a resize is necessary afterwards. ImageMagic's 'density' method only works when converting PDF/SVG/AI formats to jpeg. Again it's not ideal solution, but it gets the job done for my use case. – andre Oct 02 '17 at 14:25
  • `@AndriyKulak` If you have to resize after a resample, then you might as well just resize. You can always set the density and units (for printing) of any raster format, not just vector formats with -density -units (command line). So the resample is not necessary. There should be an equivalent command to -density in your variant of Imagemagick – fmw42 Oct 02 '17 at 16:41
  • Huh @fmw42 the density(150, 150) works now oddly enough. I must have done something wrong before. It must have been converting PNG format into JPEG. i now convert all files to jpegs before I do the transformation above and don't seem to get any errors. Thanks! – andre Oct 05 '17 at 05:48
5

You can also use this recently published library, that does exactly just dpi manipulation for JPEG ( JFIF ) and PNGs

https://github.com/shutterstock/changeDPI

AndreaBogazzi
  • 14,323
  • 3
  • 38
  • 63
  • I'd like to second this however that library uses FileReader which seems like a browser only type of implementation. I've been banging my head on the wall trying to convert it to read simple Buffers (and having this library mess with the bytes of that buffer). If anyone has figured out how to send it a buffer let me know. – Chris Jan 13 '19 at 19:59
  • There is an open pr for that. The library works under node anyway, since tests are written for node, i can understand buffers are better. The library is born for the browser since in node the solution was already available with other products. Please open an issue on the github repo, we will sort it out. – AndreaBogazzi Jan 14 '19 at 07:47
  • 2
    After attempting to convert changeDPI and others to work in node (not just the browser) I found that https://www.npmjs.com/package/png-dpi-reader-writer worked. – Chris Jan 21 '19 at 17:47
  • 1
    Incredible, I have been looking for something like this for years. Look how complex it really is to do such a simple thing to meta data! Thanks! – OG Sean Oct 23 '19 at 19:50
  • This tool does exactly what I needed it to, and without needing to hit a backend. Thanks a lot!! – Chris Sandvik Nov 23 '20 at 22:09
0

You can do this by adding/changing the pHYs chunk of a PNG file:

You can check my code at https://github.com/murkle/rewrite-png-pHYs-chunk to understand more

Petter Friberg
  • 21,252
  • 9
  • 60
  • 109
murkle
  • 477
  • 4
  • 8
0

The resolution/density can be set easily using JavasCript standard image processing liberary sharp by withMetadata function.

Simple example:

// Set output metadata to 96 DPI
const data = await sharp(input)
  .withMetadata({ density: 96 })
  .toBuffer();

Npm module: sharp

Gagan
  • 1,267
  • 3
  • 18
  • 28