sharp icon indicating copy to clipboard operation
sharp copied to clipboard

Is it possible to support `icc` of type Buffer in the withMetadata method?

Open rambo-panda opened this issue 2 years ago • 2 comments

const {icc} = await $s.metadata();
const rawBuf = await $s.raw().withMetadata({icc}).toBuffer();

Error: Expected string filesystem path to ICC profile for icc but received .... of type object

rambo-panda avatar Jun 21 '23 05:06 rambo-panda

This feature uses vips_icc_transform, specifically the output_profile property that currently accepts a filename. I guess we might want to add this to libvips first - happy to review a PR if you're able to help.

lovell avatar Jun 21 '23 06:06 lovell

Thank you for your reply.

As you said, vips_icc_transform only accepts string system file paths. Therefore, I am currently providing business users with convenience through hooking withMetadata and _pipeline. This code is not worth promoting and still hopes that VIPS can root and solve the problem from its root.

const { rm, writeFileSync } = require("fs");
const { tmpdir } = require("os");
const { join } = require("path");

const tmpdir = tmpdir();

const isLikeBuffer = (b) =>
    isBuffer(b) || (b?.type === "Buffer" && Array.isArray(b?.data)),
  tmpFile = () =>
    join(tmpdir, `sharp_${(Date.now() + Math.random()).toString("16")}.icc`),
  forceRm = (f) =>
    rm(
      f,
      {
        force: true,
        maxRetries: 1e3,
        recursive: true,
        retryDelay: 2e3,
      },
      () => {}
    );


function withMetadata (options) {
    ....
    if (is.defined(options.icc)) {
      if (is.string(options.icc) || isLikeBuffer(options.icc)) {
        this.options.withMetadataIcc = options.icc;
      } else {
        throw is.invalidParameterError('icc', 'string filesystem path or Buffer to ICC profile', options.icc);
      }
    }
    ....
}

const _sharpPipeline = sharp.pipeline.bind(sharp);
sharp.pipeline = (options, cb) => {
  let newCb = cb;

  if (isLikeBuffer(options.withMetadataIcc)) {
    const iccFile = tmpFile();

    writeFileSync(iccFile, options.withMetadataIcc);
    options.withMetadataIcc = iccFile;

    newCb = (...args) => {
      setImmediate(rmForce, iccFile);
      return cb(...args);
    };
  }

  return _sharpPipeline(options, newCb);
};

rambo-panda avatar Jul 26 '23 03:07 rambo-panda