UNPKG

librsvg-prebuilt

Version:

Prebuilt librsvg binding. Parse SVG files and render them as PNG, PDF, SVG, or raw memory buffer images.

394 lines (353 loc) 11.5 kB
'use strict'; var binding = require('bindings')('rsvg'); var Writable = require('stream').Writable; var util = require('util'); /** * Represents one SVG file to be rendered. You can optionally pass the SVG file * directly as an argument (Buffer or string) to the constructor. Otherwise the * object is a writable stream and you can pipe a file into it. Optionally you * can initialize the writable stream with specific options if you pass an * object. * * @see [LibRSVG Default Constructor]{@link * https://developer.gnome.org/rsvg/2.40/RsvgHandle.html#rsvg-handle-new} * @see [LibRSVG Constructor From Data]{@link * https://developer.gnome.org/rsvg/2.40/RsvgHandle.html#rsvg-handle-new-from-data} * @see [Writable Stream Constructor]{@link * http://nodejs.org/api/stream.html#stream_new_stream_writable_options} * * @constructor * @param {(Buffer|string|Object)} [buffer] - SVG file. */ function Rsvg(buffer) { var self = this; // Create proper options for the writable stream super constructor. var options; if (Buffer.isBuffer(buffer)) { options = {}; } else if (typeof(buffer) === 'string') { buffer = new Buffer(buffer); options = {}; } else if (buffer === undefined || buffer === null) { options = {}; } else if (typeof(buffer) === 'object') { options = buffer; buffer = null; } else { throw new TypeError('Invalid argument: buffer'); } // Inheritance pattern: Invoke super constructor. Writable.call(self, options); // Create new instance of binding. try { self.handle = new binding.Rsvg(buffer); } catch (error) { throw new Error('Rsvg load failure: ' + error.message); } if (buffer) { process.nextTick(function() { self.emit('load'); }); } // When finished piping into this object, we need to tell the binding that by // invoking the `close()` method. self.on('finish', function() { try { self.handle.close(); } catch (error) { if (error.message !== self.lastErrorMessage) { self.lastErrorMessage = error.message; self.emit('error', new Error('Rsvg close failure: ' + error.message)); } return; } self.emit('load'); }); } // Inherit from writable stream. util.inherits(Rsvg, Writable); /** * Base URI. * @member {string} */ Rsvg.prototype.baseURI = null; // Define getter/setter for `baseURI` property. Object.defineProperty(Rsvg.prototype, 'baseURI', { configurable: true, enumerable: true, get: function() { return this.handle.getBaseURI(); }, set: function(uri) { this.handle.setBaseURI(uri); } }); /** * Horizontal resolution. Allowed values: >= 0. * @deprecated since version 2.0 * @member {number} */ Rsvg.prototype.dpiX = 90; // Define getter/setter for deprecated `dpiX` property. Object.defineProperty(Rsvg.prototype, 'dpiX', { configurable: true, enumerable: true, get: util.deprecate(function() { return this.handle.getDPIX(); }, 'Rsvg#dpiX: DPI does not affect rendering.'), set: util.deprecate(function(dpi) { this.handle.setDPIX(dpi); }, 'Rsvg#dpiX: DPI does not affect rendering.') }); /** * Vertical resolution. Allowed values: >= 0. * @deprecated since version 2.0 * @member {number} */ Rsvg.prototype.dpiY = 90; // Define getter/setter for deprecated `dpiY` property. Object.defineProperty(Rsvg.prototype, 'dpiY', { configurable: true, enumerable: true, get: util.deprecate(function() { return this.handle.getDPIY(); }, 'Rsvg#dpiY: DPI does not affect rendering.'), set: util.deprecate(function(dpi) { this.handle.setDPIY(dpi); }, 'Rsvg#dpiY: DPI does not affect rendering.') }); /** * Image width. Always integer. * @readonly * @member {number} */ Rsvg.prototype.width = 0; // Define getter for `width` property. Object.defineProperty(Rsvg.prototype, 'width', { configurable: true, enumerable: true, get: function() { return this.handle.getWidth(); } }); /** * Image height. Always integer. * @readonly * @member {number} */ Rsvg.prototype.height = 0; // Define getter for `height` property. Object.defineProperty(Rsvg.prototype, 'height', { configurable: true, enumerable: true, get: function() { return this.handle.getHeight(); } }); /** * @see [Node.JS API]{@link * http://nodejs.org/api/stream.html#stream_writable_write_chunk_encoding_callback_1} * @private */ Rsvg.prototype._write = function(data, encoding, callback) { try { this.handle.write(data); } catch (error) { this.lastErrorMessage = error.message; callback(new Error('Rsvg write failure: ' + error.message)); return; } callback(); }; /** * Get the DPI for the outgoing pixbuf. * * @deprecated since version 2.0 * @returns {{x: number, y: number}} */ Rsvg.prototype.getDPI = util.deprecate(function() { return this.handle.getDPI(); }, 'Rsvg#getDPI(): DPI does not affect rendering.'); /** * Set the DPI for the outgoing pixbuf. Common values are 75, 90, and 300 DPI. * Passing null to x or y will reset the DPI to whatever the default value * happens to be (usually 90). You can set both x and y by specifying only the * first argument. * * @deprecated since version 2.0 * @param {number} x - Horizontal resolution. * @param {number} [y] - Vertical resolution. Set to the same as X if left out. */ Rsvg.prototype.setDPI = util.deprecate(function(x, y) { this.handle.setDPI(x, y); }, 'Rsvg#setDPI(): DPI does not affect rendering.'); /** * Get the SVG's size or the size/position of a subelement if id is given. The * id must begin with "#". * * @param {string} [id] - Subelement to determine the size and position of. * @returns {{width: number, height: number, x: number, y: number}} */ Rsvg.prototype.dimensions = function(id) { return this.handle.dimensions(id); }; /** * Checks whether the subelement with given id exists in the SVG document. * * @param {string} id - Subelement to check existence of. * @returns {boolean} */ Rsvg.prototype.hasElement = function(id) { return this.handle.hasElement(id); }; /** * Find the drawing area, ie. the smallest area that has image content in the * SVG document. * * @returns {{width: number, height: number, x: number, y: number}} */ Rsvg.prototype.autocrop = function() { var area = this.handle.autocrop(); area.x = area.x.toFixed(3) * 1; area.y = area.y.toFixed(3) * 1; area.width = area.width.toFixed(3) * 1; area.height = area.height.toFixed(3) * 1; return area; }; /** * Base render method. Valid high-level formats are: png, pdf, svg, raw. You * can also specify the pixel structure of raw images: argb32 (default), rgb24, * a8, a1, rgb16_565, and rgb30 (only enabled for Cairo >= 1.12). You can read * more about the low-level pixel formats in the [Cairo Documentation]{@link * http://cairographics.org/manual/cairo-Image-Surfaces.html#cairo-format-t}. * * If the element property is given, only that subelement is rendered. * * The PNG format is the slowest of them all, since it takes time to encode the * image as a PNG buffer. * * @param {Object} [options] - Rendering options. * @param {string} [options.format] - One of the formats listed above. * @param {number} [options.width] - Output image width, should be an integer. * @param {number} [options.height] - Output image height, should be an integer. * @param {string} [options.id] - Subelement to render. * @returns {{data: Buffer, format: string, width: number, height: number}} */ Rsvg.prototype.render = function(options) { if (arguments.length > 1 || typeof(options) !== 'object') { return this._renderArgs.apply(this, arguments); } options = options || {}; var img = this.handle.render( options.width, options.height, options.format, options.id ); if (this.width + this.height > 0 && img.data.length == 0) { // sometimes render fails and returns zero-sized buffer, see zerobuffer test // just rerender image return this.render(options); } return img; }; /** * @deprecated since version 2.0 * @private */ Rsvg.prototype._renderArgs = util.deprecate(function(width, height, format, id) { return this.handle.render(width, height, format ? format.toLowerCase() : null, id); }, 'Rsvg#render(): Call render({ format, width, height, ... }) instead.'); /** * Render the SVG as a raw memory buffer image. This can be used to create an * image that is imported into other image libraries. This render method is * usually very fast. * * The pixel format is ARGB and each pixel is 4 bytes, ie. the buffer size is * width*height*4. There are no memory "spaces" between rows in the image, like * there can be when calling the base render method with pixel formats like A8. * * @deprecated since version 2.0 * @param {number} width - Output image width, should be an integer. * @param {number} height - Output image height, should be an integer. * @param {string} [id] - Subelement to render. * @returns {{data: Buffer, format: string, pixelFormat: string, width: number, height: number}} */ Rsvg.prototype.renderRaw = util.deprecate(function(width, height, id) { return this.render({ format: 'raw', width: width, height: height, element: id }); }, 'Rsvg#renderRaw(): Call render({ format: "raw" }) instead.'); /** * Render the SVG as a PNG image. * * @deprecated since version 2.0 * @param {number} width - Output image width, should be an integer. * @param {number} height - Output image height, should be an integer. * @param {string} [id] - Subelement to render. * @returns {{data: Buffer, format: string, width: number, height: number}} */ Rsvg.prototype.renderPNG = util.deprecate(function(width, height, id) { return this.render({ format: 'png', width: width, height: height, element: id }); }, 'Rsvg#renderPNG(): Call render({ format: "png" }) instead.'); /** * Render the SVG as a PDF document. * * @deprecated since version 2.0 * @param {number} width - Output document width, should be an integer. * @param {number} height - Output document height, should be an integer. * @param {string} [id] - Subelement to render. * @returns {{data: Buffer, format: string, width: number, height: number}} */ Rsvg.prototype.renderPDF = util.deprecate(function(width, height, id) { return this.render({ format: 'pdf', width: width, height: height, element: id }); }, 'Rsvg#renderPDF(): Call render({ format: "pdf" }) instead.'); /** * Render the SVG as an SVG. This seems superfluous, but it can be used to * normalize the input SVG. However you can not be sure that the resulting SVG * file is smaller than the input. It's not a SVG compression engine. You can * be sure that the output SVG follows a more stringent structure. * * @deprecated since version 2.0 * @param {number} width - Output document width, should be an integer. * @param {number} height - Output document height, should be an integer. * @param {string} [id] - Subelement to render. * @returns {{data: string, format: string, width: number, height: number}} */ Rsvg.prototype.renderSVG = util.deprecate(function(width, height, id) { return this.render({ format: 'svg', width: width, height: height, element: id }); }, 'Rsvg#renderSVG(): Call render({ format: "svg" }) instead.'); /** * String representation of this SVG render object. * @returns {string} */ Rsvg.prototype.toString = function() { var obj = {}; var baseURI = this.baseURI; if (baseURI) { obj.baseURI = baseURI; } obj.width = this.width; obj.height = this.height; return '{ [' + this.constructor.name + ']' + util.inspect(obj).slice(1); }; // Export the Rsvg object. exports.Rsvg = Rsvg;