/* * convert.js * * can convert an image to a different image format. * can also modify dimensions of an image (width and height) * * It's a demonstration of imgITools interface which was introduced in gecko * 1.9. Therefore, that script won't work in gecko 1.8 * * Current version works as a xpcshell. If you need to include it in a chrome * application, it's quite easy, but a few steps are necessary: * - parsearguments is the function where xpcshell arguments are parsed. You * will need to rewrite it and feed it with your input data. In your * modified fonction, you should better not use resolveFileName. * - that script uses xpcshell specific function, such as quit and print. you * may need to reimplement them, or find a better way to stop process * flow, and to report message to user. * */ const Cc = Components.classes; const Ci = Components.interfaces; const OS = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS; const IMGTOOLS = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools); String.prototype.beginsWith = function (substr) { return (this.slice(0, substr.length) == substr); } function error(aMsg, aErrCode) { var errcode; if (aErrCode === undefined) { errcode = 1; } else { errcode = aErrCode; } print ("convert.js: " + aMsg); quit (errcode); } function warn(aMsg) { print ("convert.js: " + aMsg); } function usage() { print("convert.js [options] infile outfile"); print("Options"); print("\t-size width:height\twidth and height of output image"); } /* * aFileName: string * @returns: nsIFile; */ function createFile (aFileName) { var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); file.initWithPath (aFileName); return file; } /* * aFile: nsIFile * @returns: nsIBufferedInputStream * * XXX: The image decoders use ReadSegments, which isn't implemented on file input * streams. Therefore, we need a buffered stream */ function inputFileStream(aFile) { var stream = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(Ci.nsIFileInputStream); stream.init(aFile, 0x01, -1, null); var bstream = Cc["@mozilla.org/network/buffered-input-stream;1"].createInstance(Ci.nsIBufferedInputStream); bstream.init(stream, 1024); return bstream; } /* * aFile: nsIFile * @returns: nsIBufferedInputStream * * XXX: The image decoders use ReadSegments, which isn't implemented on file input * streams. Therefore, we need a buffered stream */ function outputFileStream(aFile) { var stream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream); stream.init (aFile, 0x04 | 0x08 | 0x20, 0600, 0); return stream; } /* * reads data from inputstream and write it to outputstream * * aInputStream: nsIInputStream * aOutputStream: nsIOutputStream */ function readStream (aInputStream, aOutputStream) { var avail = aInputStream.available(); while (avail) { aOutputStream.write(aInputStream.readBytes(avail), avail); avail = aInputStream.available(); } aInputStream.close(); aOutputStream.flush(); aOutputStream.close(); } /* * aFile: nsIFile * @returns: string */ function getMime(aFile) { var ms = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService); return ms.getTypeFromFile(aFile); } function HelperLoader() { this._image = null; } HelperLoader.prototype = { QueryInterface: function (iid) { if (iid.equals(Ci.imgILoad) || iid.equals(Ci.imgIDecoderObserver) || iid.equals(Ci.imgIContainerObserver) || iid.equals(Ci.nsISupports)) return this; }, /* imgILoad */ get image () { return this._image; }, set image (aValue) { this._image = aValue; }, get isMultiPartChannel() { return false; }, /* imgIDecoderObserver */ onDataAvailable: function (aRequest, aFrame, aRect) {}, onStartContainer: function (aRequest, aContainer) {}, onStartDecode: function (aRequest) {}, onStartFrame: function (aRequest, aFrame) {}, onStartRequest: function (aRequest) {}, onStopContainer: function (aRequest, aContainer) {}, onStopDecode: function (aRequest, aStatus, aStatusArg) {}, onStopFrame: function (aRequest, aFrame) {}, onStopRequest: function (aRequest, aIsLastPart) {}, /* imgIContainerObserver */ frameChanged: function (aContainer, aFrame, aDirtyRect) {} } /* * decode image data contained in a stream * * aInputStream: nsIBufferedInputStream * aInputType: string * @returns: imgIContainer */ function decodeImageData(aInputStream, aInputType) { var obj = new Object(); IMGTOOLS.decodeImageData (aInputStream, aInputType, obj); var container = obj.value; aInputStream.close(); return container; } /* * encode image data in a string * * aContainer: imgIContainer * aOutputType: string * aWidth: integer * aHeight: integer * @returns: nsIBinaryInputStream */ function encodeImageData (aContainer, aOutputType, aWidth, aHeight) { var stream = IMGTOOLS. encodeScaledImage (aContainer, aOutputType, aWidth, aHeight); var estream = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream); estream.setInputStream(stream); return estream; } /* * returns a full path name. If aFileName is relative, resolve it against * current directory * * FIXME: function tested on Linux. Test and correct it on other systems. */ function resolveFileName(aFileName) { var pathSeparator = ""; if (OS == "Windows") { pathSeparator = '\\' } else { pathSeparator = '/'; } // relativePach if (aFileName.beginsWith(pathSeparator)) { return aFileName; } return (environment["PWD"] + pathSeparator + aFileName); } /* * represent parameters set by user * * width and height of ouput image * input filename * output filename */ function settings(aWidth, aHeight, aInFile, aOutFile) { this.width = aWidth; this.height = aHeight; this.infile = aInFile; this.outfile = aOutFile; } settings.prototype = { width: 0, height: 0, infile: "", outfile: "" } /* * aStr: string formatted as width:height * @returns: array: array[0] = desired width; array[1] = desired height; * returns null if string could not be parsed */ function parseHeightAndWidth(aStr) { if (typeof aStr != "string") { return null; } var sep = ":"; var dims = aStr.split(sep); if (dims.length != 2) { return null; } function toInt (aStr) { var integer = parseInt(aStr); if (isNaN(integer)) { throw "not a number"; } return integer; } var dimsAsInt; try { dimsAsInt = dims.map(toInt); } catch(e) { return null; } return dimsAsInt; } function parsearguments() { var args = Array.prototype.slice.call(this.arguments); if (args.indexOf("-h") != -1 || args.indexOf("--help") != -1) { usage(); quit(0); } var width, height, infile, outfile; var idxOfSize = args.indexOf("-size"); if (idxOfSize != -1) { var sizeArg = args[idxOfSize + 1]; var dims = parseHeightAndWidth(sizeArg); if (dims == null) { usage(); print("\n"); error("could not parse -size argument \"" + sizeArg + "\""); } width = dims[0]; height = dims[1]; args.splice(idxOfSize, 2); } if (args.length < 2) { usage(); print("\n"); error ("not enough arguments. Please specify an input file and an output file"); } if (args.length > 2) { warn ("too many arguments. Only input file and output file will be taken. Other will be discarded"); } infile = resolveFileName(args[0]); outfile = resolveFileName(args[1]); return new settings(width, height, infile, outfile); } function main() { var settings = parsearguments(); /* * read input file */ var infile = createFile (settings.infile); var instream = inputFileStream (infile); var intype = getMime (infile); var container = decodeImageData (instream, intype); /* * write output file */ var outfile = createFile (settings.outfile); var outtype = getMime (outfile) var outstream = outputFileStream (outfile); var encodedStream = encodeImageData (container, outtype, settings.width, settings.height); readStream (encodedStream, outstream); return 0; } var ret = main(); quit (ret);