163 lines
3.7 KiB
JavaScript
163 lines
3.7 KiB
JavaScript
|
|
||
|
|
||
|
/*!
|
||
|
* Stylus - plugin - url
|
||
|
* Copyright (c) Automattic <developer.wordpress.com>
|
||
|
* MIT Licensed
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Module dependencies.
|
||
|
*/
|
||
|
|
||
|
var utils = require('../utils')
|
||
|
, nodes = require('../nodes')
|
||
|
, fs = require('fs')
|
||
|
, path = require('path')
|
||
|
, sax = require('sax');
|
||
|
|
||
|
/**
|
||
|
* Initialize a new `Image` with the given `ctx` and `path.
|
||
|
*
|
||
|
* @param {Evaluator} ctx
|
||
|
* @param {String} path
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
var Image = module.exports = function Image(ctx, path) {
|
||
|
this.ctx = ctx;
|
||
|
this.path = utils.lookup(path, ctx.paths);
|
||
|
if (!this.path) throw new Error('failed to locate file ' + path);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Open the image for reading.
|
||
|
*
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
Image.prototype.open = function(){
|
||
|
this.fd = fs.openSync(this.path, 'r');
|
||
|
this.length = fs.fstatSync(this.fd).size;
|
||
|
this.extname = path.extname(this.path).slice(1);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Close the file.
|
||
|
*
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
Image.prototype.close = function(){
|
||
|
if (this.fd) fs.closeSync(this.fd);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Return the type of image, supports:
|
||
|
*
|
||
|
* - gif
|
||
|
* - png
|
||
|
* - jpeg
|
||
|
* - svg
|
||
|
*
|
||
|
* @return {String}
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
Image.prototype.type = function(){
|
||
|
var type
|
||
|
, buf = new Buffer(4);
|
||
|
|
||
|
fs.readSync(this.fd, buf, 0, 4, 0);
|
||
|
|
||
|
// GIF
|
||
|
if (0x47 == buf[0] && 0x49 == buf[1] && 0x46 == buf[2]) type = 'gif';
|
||
|
|
||
|
// PNG
|
||
|
else if (0x50 == buf[1] && 0x4E == buf[2] && 0x47 == buf[3]) type = 'png';
|
||
|
|
||
|
// JPEG
|
||
|
else if (0xff == buf[0] && 0xd8 == buf[1]) type = 'jpeg';
|
||
|
|
||
|
// SVG
|
||
|
else if ('svg' == this.extname) type = this.extname;
|
||
|
|
||
|
return type;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Return image dimensions `[width, height]`.
|
||
|
*
|
||
|
* @return {Array}
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
Image.prototype.size = function(){
|
||
|
var type = this.type()
|
||
|
, width
|
||
|
, height
|
||
|
, buf
|
||
|
, offset
|
||
|
, blockSize
|
||
|
, parser;
|
||
|
|
||
|
function uint16(b) { return b[1] << 8 | b[0]; }
|
||
|
function uint32(b) { return b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]; }
|
||
|
|
||
|
// Determine dimensions
|
||
|
switch (type) {
|
||
|
case 'jpeg':
|
||
|
buf = new Buffer(this.length);
|
||
|
fs.readSync(this.fd, buf, 0, this.length, 0);
|
||
|
offset = 4;
|
||
|
blockSize = buf[offset] << 8 | buf[offset + 1];
|
||
|
|
||
|
while (offset < this.length) {
|
||
|
offset += blockSize;
|
||
|
if (offset >= this.length || 0xff != buf[offset]) break;
|
||
|
// SOF0 or SOF2 (progressive)
|
||
|
if (0xc0 == buf[offset + 1] || 0xc2 == buf[offset + 1]) {
|
||
|
height = buf[offset + 5] << 8 | buf[offset + 6];
|
||
|
width = buf[offset + 7] << 8 | buf[offset + 8];
|
||
|
} else {
|
||
|
offset += 2;
|
||
|
blockSize = buf[offset] << 8 | buf[offset + 1];
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case 'png':
|
||
|
buf = new Buffer(8);
|
||
|
// IHDR chunk width / height uint32_t big-endian
|
||
|
fs.readSync(this.fd, buf, 0, 8, 16);
|
||
|
width = uint32(buf);
|
||
|
height = uint32(buf.slice(4, 8));
|
||
|
break;
|
||
|
case 'gif':
|
||
|
buf = new Buffer(4);
|
||
|
// width / height uint16_t little-endian
|
||
|
fs.readSync(this.fd, buf, 0, 4, 6);
|
||
|
width = uint16(buf);
|
||
|
height = uint16(buf.slice(2, 4));
|
||
|
break;
|
||
|
case 'svg':
|
||
|
offset = Math.min(this.length, 1024);
|
||
|
buf = new Buffer(offset);
|
||
|
fs.readSync(this.fd, buf, 0, offset, 0);
|
||
|
buf = buf.toString('utf8');
|
||
|
parser = sax.parser(true);
|
||
|
parser.onopentag = function(node) {
|
||
|
if ('svg' == node.name && node.attributes.width && node.attributes.height) {
|
||
|
width = parseInt(node.attributes.width, 10);
|
||
|
height = parseInt(node.attributes.height, 10);
|
||
|
}
|
||
|
};
|
||
|
parser.write(buf).close();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ('number' != typeof width) throw new Error('failed to find width of "' + this.path + '"');
|
||
|
if ('number' != typeof height) throw new Error('failed to find height of "' + this.path + '"');
|
||
|
|
||
|
return [width, height];
|
||
|
};
|