npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@janhapke/exiv2

v0.2808.5

Published

A native c++ extension for node.js that provides support for reading & writing image metadata via Exiv2. Fork of @11ways/exiv2 with a fix for interpreted (rather than raw) tag values.

Readme

Why this fork exists

@janhapke/exiv2 is a fork of @11ways/exiv2, maintained by Jan Hapke. It exists to:

  • fix getImageTags() returning Exiv2's raw tag values instead of the interpreted ones — see CHANGELOG.md for the underlying bug and fix (e.g. a Nikon lens ID like "154" now correctly resolves to "Nikon AF-S DX VR Zoom-Nikkor 18-55mm f/3.5-5.6G")
  • ship hand-written TypeScript declarations (exiv2.d.ts), which upstream has never had

Versioning scheme

This fork tracks the Exiv2 C++ library version it's built and tested against, since correctness here depends directly on which native Exiv2 release resolves tag interpretation. Versions are 0.XXYY.Z:

  • XXYY — the tracked Exiv2 version's minor number followed by its patch number, zero-padded to 2 digits, concatenated (Exiv2 0.28.8 → minor 28 + patch 08 → 2808).
  • Z — this fork's own release counter for that Exiv2 version, starting at 0 and bumped for every fork-only change (a fix, a feature like type declarations, a metadata correction) that doesn't require bumping the tracked Exiv2 version.

For example, 0.2808.2 is this fork's 3rd release (Z=2) built against Exiv2 0.28.8.

The patch number is zero-padded (not left as a bare concatenation) because Exiv2's minor version has already grown from 1 digit to 2 (0.9 → 0.10, and never dropped back), and its patch number has already reached 8 in the current 0.28.x line — one release away from testing a double-digit patch. An unpadded XXY scheme breaks the moment a minor bump happens while the old minor's patch was already double digits: e.g. Exiv2 0.28.10 would concatenate to 2810, but 0.29.0 would concatenate to 290 — and since semver compares these as plain numbers, 2810 > 290 would make the fork version for the older 0.28.10 sort as newer than the fork version for 0.29.0. Zero-padding the patch to a fixed 2-digit width keeps every comparison monotonic no matter how the digit counts change (assuming Exiv2's patch number stays under 100, comfortably true for the foreseeable future).

Note: this fork's very first release (the interpreted-tags fix) was published as plain 0.28.8, before this scheme was adopted. Every release from 0.2808.2 onward follows the scheme above.

Exiv2

Exiv2 is a native C++ extension for node.js that provides support for reading and writing image metadata via the Exiv2 library.

It was created by Damian Beresford

Dependencies

To build this addon you'll need the Exiv2 library and headers so if you're using a package manager you might need to install an additional "-dev" packages.

Debian / Ubuntu

apt-get install pkg-config exiv2 libexiv2-dev

macOS

You'll also need to install pkg-config to help locate the library and headers.

MacPorts:

port install pkgconfig exiv2

Homebrew:

brew install pkg-config exiv2

FreeBSD

pkg install pkgconf exiv2

Arch Linux

pacman -S exiv2 pkgconf

Windows

Install pkg-config using Chocolatey:

choco install pkgconfiglite

Download latest msvc64 exiv2 build from the Exiv2 download page and extract to a folder of your choice.

Add a system variable named PKG_CONFIG_PATH and set it's value to EXIV2ROOTDIR\lib\pkgconfig replacing EXIV2ROOTDIR with the path where you extracted exiv2 from the step before (e.g. D:\src\exiv2msvs\lib\pkgconfig).

You'll also need windows-build-tools to compile this package.

For Electron apps, you'll want to copy exiv2.dll to the root directory of your Electron Windows build. You can automated this using the extraFiles option.

Other systems

See the Exiv2 download page for more information.

Requirements

  • Node.js 18 or later
  • Exiv2 library and development headers (see Dependencies above)
  • pkg-config

Installation Instructions

Once the dependencies are in place, you can build and install the module using npm:

npm install @janhapke/exiv2

You can verify that everything is installed and operating correctly by running the tests:

npm test

Interpreted vs. raw tag values

Unlike the upstream @11ways/exiv2 package, getImageTags() here returns interpreted tag values (via Exiv2's Metadatum::print()) instead of raw ones (via Value::toString()). This matters for tags whose stored value needs manufacturer-specific decoding to be meaningful, e.g.:

// Exif.NikonLd2.LensIDNumber
// before: "154"
// after:  "Nikon AF-S DX VR Zoom-Nikkor 18-55mm f/3.5-5.6G"

See CHANGELOG.md for details.

Diagnostics / logging

Exiv2's internal logger (Exiv2::LogMsg) writes its own Warning:/Error: diagnostics straight to stderr whenever it hits malformed image structure (e.g. a corrupted or truncated file), independently of whether the call you made succeeds. For example, getImageTags() can resolve normally with no tags and no error while Exiv2 has already printed something like:

Error: Directory Image with 572 entries considered invalid; not read.

Two functions let you control this:

var ex = require('@janhapke/exiv2');

// Suppress Exiv2's diagnostics entirely.
ex.muteLog();
// ...equivalent to:
ex.setLogLevel('mute');

// Or route them into your own code instead of stderr.
ex.setLogHandler(function(event) {
  console.log(event.level, event.message);
});

// Restore the default stderr output.
ex.setLogHandler(null);

setLogLevel() accepts 'debug', 'info', 'warn', 'error', or 'mute' (Exiv2's default level is 'warn'); only messages at or above that severity reach the handler.

Caveat: setLogLevel()/muteLog() control Exiv2's own process-global log level, not anything scoped to a single call — they affect every getImageTags()/setImageTags()/deleteImageTags()/getImagePreviews() call in the process, including ones made from other Node.js environments (see "Concurrency and worker_threads" below). setLogHandler(), by contrast, is scoped to the Node.js environment (main thread, or a single worker_thread) that calls it — a handler installed on the main thread never receives events from calls made in a worker thread, and vice versa. Within one environment, a handler receives log events from whichever call on that environment happens to be running at the time; if multiple calls from the same environment are in flight concurrently, there is no way to attribute a given message back to a specific one.

Concurrency

All four calls run off the main thread and are safe to call concurrently from JS. Internally:

  • getImageTags() and getImagePreviews() (read-only — readMetadata() only) run truly in parallel with each other, up to Node's worker pool size (UV_THREADPOOL_SIZE, 4 by default).
  • setImageTags() and deleteImageTags() (call writeMetadata()) are serialized — only one write, and no concurrent read, runs at a time.
  • setLogLevel()/muteLog()/setLogHandler() are also serialized against everything else, briefly, while they run.

This split follows Exiv2's own documented thread-safety model: Exif/IPTC parsing is reentrant, and the XMP toolkit's own encode()/decode() are documented thread-safe internally — provided XmpParser::initialize() has already run once, which this addon does automatically at load time, before any concurrent call is possible. Writes stay serialized because XmpParser::encode() (the path writeMetadata() takes whenever a file already carries XMP data) iterates an internal Exiv2 registry with no lock at all as of Exiv2 0.28.x — a real, upstream, not-yet-released-fixed race, not something this addon can safely work around from the outside.

Known residual risk on reads: two concurrent reads that register conflicting XMP namespace prefixes can still overwrite each other's entry in Exiv2's one shared internal registry. This is memory-safe (Exiv2 internally mutex-protects the write itself) but can produce a wrong XMP tag value, or occasionally a normal err on the affected call — not a crash. If your files don't carry unusual/custom XMP namespaces this is very unlikely to matter in practice.

worker_threads

This addon is a Node-API addon, which Node.js loads fresh into every environment that require()s it — the main thread, and independently, any worker_thread — all sharing the same underlying Exiv2 library and its process-wide state (XmpParser, the log level, etc.), but each with its own isolated setLogHandler() handler (see above). This means it's safe to require() this addon and call setLogHandler() from inside a worker_thread pool (e.g. to parallelize decoding across CPU cores) without one worker's handler ever being invoked across another worker's V8 isolate — a real crash (v8::HandleScope::CreateHandle() Cannot create a handle without a HandleScope) prior to this being fixed. getImageTags()/setImageTags()/deleteImageTags()/ getImagePreviews() calls from different worker_threads are subject to the same read/write locking described above, shared across all threads in the process (not per-worker) — so, for example, a write from one worker still blocks a concurrent read from another.

Sample Usage

Read tags:

var ex = require('@janhapke/exiv2');

ex.getImageTags('./photo.jpg', function(err, tags) {
  console.log("DateTime: " + tags["Exif.Image.DateTime"]);
  console.log("DateTimeOriginal: " + tags["Exif.Photo.DateTimeOriginal"]);
});

var fs = require('fs');

ex.getImageTags(fs.readFileSync('./photo.jpg'), function(err, tags) {
  console.log("DateTime: " + tags["Exif.Image.DateTime"]);
  console.log("DateTimeOriginal: " + tags["Exif.Photo.DateTimeOriginal"]);
});

Load preview images:

var ex = require('@janhapke/exiv2')
  , fs = require('fs');

ex.getImagePreviews('./photo.jpg', function(err, previews) {
  // Display information about the previews.
  console.log(previews);

  // Or you can save them--though you'll probably want to check the MIME
  // type before picking an extension.
  fs.writeFile('preview.jpg', previews[0].data);
});

Write tags:

var ex = require('@janhapke/exiv2')

var newTags = {
  "Exif.Photo.UserComment" : "Some Comment..",
  "Exif.Canon.OwnerName" : "My Camera"
};
ex.setImageTags('./photo.jpg', newTags, function(err){
  if (err) {
    console.error(err);
  } else {
    console.log("setImageTags complete..");
  }
});

Delete tags:

var ex = require('@janhapke/exiv2')

var tagsToDelete = ["Exif.Photo.UserComment", "Exif.Canon.OwnerName"];
ex.deleteImageTags('./photo.jpg', tagsToDelete, function(err){
  if (err) {
    console.error(err);
  } else {
    console.log("deleteImageTags complete..");
  }
});

Take a look at the examples/ and test/ directories for more.

Authors

See also the list of contributors who participated in the upstream project, and AUTHORS for this fork.

The original @11ways/exiv2node is developed at Eleven Ways, a team of IAAP Certified Accessibility Specialists.

License

This project is licensed under the MIT License - see the LICENSE file for details.