0

Right now I have a multi-image TIFF loading in Node.js using the Tiff.js library. The Tiff.js library allows me to open a multi-image Tiff and select an image by using the setDirectory command. Any ideas on how to save each of the images on the server?
My Code:

fs.readFile(path.resolve(__dirname, filename), function(err, data) {
    if (err) {
        throw err;
    }
    tiff = new Tiff({
        buffer: data
    });
    console.log('width:', tiff.width());
    console.log('height:', tiff.height());
    console.log('currentDirectory:', tiff.currentDirectory());
    console.log('countDirectory:', tiff.countDirectory());
    for (var i = 0, len = tiff.countDirectory(); i < len; ++i) {
        tiff.setDirectory(i);
    }
    tiff.close();
});
Daniel Kobe
  • 9,376
  • 15
  • 62
  • 109

1 Answers1

0

That's actually not trivial, as libtiff (on which tiff.js is based) doesn't support such an action directly.

I pieced together some code that writes out each directory as a PNG file (using node-png):

var path = require('path');
var fs   = require('fs');
var Tiff = require('tiff');
var PNG  = require('node-png').PNG;

fs.readFile(path.resolve(__dirname, filename), function(err, data) {
  if (err) {
      throw err;
  }
  var tiff = new Tiff({ buffer: data });
  console.log('width:', tiff.width());
  console.log('height:', tiff.height());
  console.log('currentDirectory:', tiff.currentDirectory());
  console.log('countDirectory:', tiff.countDirectory());
  for (var i = 0, len = tiff.countDirectory(); i < len; ++i) {
    writeLayer(tiff, i);
  }
  tiff.close();
});

function writeLayer(tiff, layer) {
  tiff.setDirectory(layer);
  var width  = tiff.width();
  var height = tiff.height();
  var bufsiz = width * height * 4;
  var raster = Tiff.Module.ccall('_TIFFmalloc', 'number', ['number'], [ bufsiz ]);
  var result = Tiff.Module.ccall('TIFFReadRGBAImageOriented', 'number', [
      'number',
      'number',
      'number',
      'number',
      'number',
      'number'
  ], [
      tiff._tiffPtr,
      width,
      height,
      raster,
      1,
      0
  ]);

  if (result === 0) {
    throw new Tiff.Exception('The function TIFFReadRGBAImageOriented returns NULL');
  }
  var image = Tiff.Module.HEAPU8.subarray(raster, raster + bufsiz);
  var png   = new PNG({
    filterType : -1,
    width      : width,
    height     : height
  });
  for (var y = 0; y < height; y++) {
    for (var x = 0; x < width; x++) {
      var idx = (y * width + x) * 4;
      png.data[idx]     = image[idx];
      png.data[idx + 1] = image[idx + 1];
      png.data[idx + 2] = image[idx + 2];
      png.data[idx + 3] = image[idx + 3];
    }
  }
  png.pack().pipe(fs.createWriteStream('layer-' + layer + '.png'));
  Tiff.Module.ccall('free', 'number', ['number'], [raster]);
}

It's not optimized, nor well tested, but a 2-layer TIFF that I threw at it got split into two separate files.

I wonder if it's not less trouble to just use child_process.exec() for calling tiffsplit (which comes with libtiff).

robertklep
  • 198,204
  • 35
  • 394
  • 381