image.js
3.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/*!
* 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];
};