Sharp: metadata.density undefined on an image with JFIF/EXIF

Created on 20 Jun 2019  路  8Comments  路  Source: lovell/sharp

What is the output of running npx envinfo --binaries --languages --system --utilities?

  System:
    OS: Linux 4.4 Ubuntu 18.04.2 LTS (Bionic Beaver)
    CPU: (6) x64 Intel(R) Core(TM) i5-8400 CPU @ 2.80GHz
    Memory: 6.97 GB / 15.95 GB
    Shell: 4.4.19 - /bin/bash
  Binaries:
    Node: 11.8.0 - ~/.nvm/versions/node/v11.8.0/bin/node
    npm: 6.9.0 - /private-route to binaries (correctly set)
  Utilities:
    Make: 4.1 - /usr/bin/make
    GCC: 7.4.0 - /usr/bin/gcc
    Git: 2.17.1 - /usr/bin/git
  Languages:
    Bash: 4.4.19 - /bin/bash
    Perl: 5.26.1 - /usr/bin/perl
    PHP: 7.2.19 - /usr/bin/php
    Python: 2.7.15+ - /usr/bin/python
    Ruby: 2.5.3 - /usr/share/rvm/rubies/ruby-2.5.3/bin/ruby

What are the steps to reproduce?
try to read the meta data of the following image:
file 1 with spaces 50dpi
or this image:
file 1 with spaces

What is the expected behaviour?

receiving the density parameter with:

function async logDensity(filePath){
    const image = sharp(filePath);
    const metadata = await image.metadata();
   console.log(metadata.density);
}

Are you able to provide a standalone code sample, without other dependencies, that demonstrates this problem?

no

Are you able to provide a sample image that helps explain the problem?

no

question

Most helpful comment

after further investigation I found out what my problem was:

I was rotating the images before validating them invoking .rotate().toFile() but this caused a loss of the metadatas, to preserve the metadata after rotating I had to switch to .rotate().withMetadata().toFile() and it's now working as intended...

Thanks a lot for your help investigating the issue and helping me solving it!

All 8 comments

Hello, density is only relevant for some image formats.

density: Number of pixels per inch (DPI), if present
https://sharp.pixelplumbing.com/en/stable/api-input/#metadata

JPEG doesn't contain this. What value did you expect?

What value did you expect

JFIF/EXIF x resolution/y resolution ?

or when the image is a jpeg am i supposed to manually extract the JFIF/EXIF and manually check those proprieties?

By my understanding x resolution and y resolution (when having the same value between the two) is pretty much the image density?

( link with jfif/exif of the given image) https://exifinfo.org/detail/TTKBvv-m4QGjTwuRKZdoKw

density is only relevant for some image formats.

Would be great if you could add that to the documentation for some extra clarification

The EXIF metadata is made available in the metadata.exif property, which can be parsed using something like the exif-reader module. There's an example of this in the unit tests.

https://github.com/lovell/sharp/blob/9dd6510de6340c15e9bcd18a69af5a4115baffb1/test/unit/metadata.js#L53-L56

Okay so the expected way to deal with image density when the extension is .jpeg is to read the exif data and get the x/ resolution from there?

Just to be extra sure, when would be a good time to use the metadata.density ?

Am i missing something in understanding the documentation or this is a little obscure/undocumented? (not the part about the metadata.exif, that is indeed well documented, but the part about metadata.density to me feel somewhat missing? maybe it would be a good idea to document the use case when the density isn't undefined?)

Apologies, because these JPEG images do contain EXIF metadata for "X Resolution" etc. they will also return a density (this is logic within libvips).

I see metadata.density of 50 for the first image and 72 for the second.

Could you have a custom, global installation of libvips that has been compiled without the libexif dependency?

Could you have a custom, global installation of libvips that has been compiled without the libexif dependency?

Global surely not, the same code is currently working in 3 different environments (one of which is an amazon elastic beanstalk) and in none of those libvips has been globally installed...

this is the package.json:

    "ascii-table": "0.0.9",
    "aws-sdk": "^2.414.0",
    "babel-cli": "^6.26.0",
    "babel-core": "^6.26.3",
    "babel-preset-env": "^1.7.0",
    "bcryptjs": "^2.4.3",
    "cors": "^2.8.5",
    "dotenv": "^6.2.0",
    "exif-reader": "^1.0.2",    <-- new entry after your answears about how to handle exif metadata
    "express": "^4.16.4",
    "express-bearer-token": "^2.2.0",
    "fs-extra": "^8.0.1",
    "jsonapi-serializer": "^3.6.4",
    "jsonwebtoken": "^8.4.0",
    "mkdirp": "^0.5.1",
    "morgan": "^1.9.1",
    "multer": "^1.4.1",
    "multer-s3": "^2.9.0",
    "multer-sharp-s3": "^0.1.0",
    "node-env-file": "^0.1.8",
    "node-geocoder": "^3.22.0",
    "nodemailer": "^5.1.1",
    "nodemon": "^1.18.9",
    "passport": "^0.4.0",
    "passport-facebook": "^3.0.0",
    "passport-local": "^1.0.0",
    "pg": "^7.8.0",
    "pg-hstore": "^2.3.2",
    "request": "^2.88.0",
    "request-promise": "^4.2.4",
    "sequelize": "^4.43.0",
    "sequelize-cli": "^5.4.0",
    "sharp": "^0.20.8"

sharp actually gets to read the file after multer saves the image in a temp folder, what may be happening is that multer cuts-off the exif metadata before sharp gets a chance to read it...

I'm using sharp as a two-step validator after multer validate the data it can validate (file size, mime type), I would like sharp to limit the "accepted" image to a minimum width of 1920px and a density of 72 dpi...

Any reccomanded way to deal with user file image upload preserving the EXIF?

If you're relying on the libvips that ships with sharp, please can you upgrade to the latest version of sharp, currently v0.22.1, to pick up recent improvements to EXIF handling in libvips.

As you suggest, if there's a step in your logic that could be removing EXIF metadata then that's likely to be the cause.

after further investigation I found out what my problem was:

I was rotating the images before validating them invoking .rotate().toFile() but this caused a loss of the metadatas, to preserve the metadata after rotating I had to switch to .rotate().withMetadata().toFile() and it's now working as intended...

Thanks a lot for your help investigating the issue and helping me solving it!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jaekunchoi picture jaekunchoi  路  3Comments

terbooter picture terbooter  路  3Comments

knoxcard picture knoxcard  路  3Comments

Andresmag picture Andresmag  路  3Comments

kachurovskiy picture kachurovskiy  路  3Comments