UNPKG

4.32 kBJavaScriptView Raw
1'use strict';
2
3/* eslint-disable consistent-return */
4
5var Transform = require('stream').Transform;
6
7var STATE_IDENTIFY = 0; // look for '<'
8var STATE_PARSE = 1; // extract width and height from svg tag
9var STATE_IGNORE = 2; // we got all the data we want, skip the rest
10
11// max size for pre-svg-tag comments plus svg tag itself
12var MAX_DATA_LENGTH = 65536;
13
14var SVG_HEADER_RE = /<svg\s[^>]+>/;
15var SVG_WIDTH_RE = /[^-]\bwidth="([^%]+?)"|[^-]\bwidth='([^%]+?)'/;
16var SVG_HEIGHT_RE = /\bheight="([^%]+?)"|\bheight='([^%]+?)'/;
17var SVG_VIEWBOX_RE = /\bview[bB]ox="(.+?)"|\bview[bB]ox='(.+?)'/;
18var SVG_UNITS_RE = /in$|mm$|cm$|pt$|pc$|px$|em$|ex$/;
19
20
21function isWhiteSpace(chr) {
22 return chr === 0x20 || chr === 0x09 || chr === 0x0D || chr === 0x0A;
23}
24
25// Filter NaN, Infinity, < 0
26function isFinitePositive(val) {
27 return typeof val === 'number' && isFinite(val) && val > 0;
28}
29
30function svgAttrs(str) {
31 var width = str.match(SVG_WIDTH_RE);
32 var height = str.match(SVG_HEIGHT_RE);
33 var viewbox = str.match(SVG_VIEWBOX_RE);
34
35 return {
36 width: width && (width[1] || width[2]),
37 height: height && (height[1] || height[2]),
38 viewbox: viewbox && (viewbox[1] || viewbox[2])
39 };
40}
41
42
43function units(str) {
44 if (!SVG_UNITS_RE.test(str)) return 'px';
45
46 return str.match(SVG_UNITS_RE)[0];
47}
48
49
50function parseSvg(str) {
51 if (!SVG_HEADER_RE.test(str)) return;
52
53 var attrs = svgAttrs(str.match(SVG_HEADER_RE)[0]);
54 var width = parseFloat(attrs.width);
55 var height = parseFloat(attrs.height);
56
57 // Extract from direct values
58
59 if (attrs.width && attrs.height) {
60 if (!isFinitePositive(width) || !isFinitePositive(height)) return;
61
62 return {
63 width: width,
64 height: height,
65 type: 'svg',
66 mime: 'image/svg+xml',
67 wUnits: units(attrs.width),
68 hUnits: units(attrs.height)
69 };
70 }
71
72 // Extract from viewbox
73
74 var parts = (attrs.viewbox || '').split(' ');
75 var viewbox = {
76 width: parts[2],
77 height: parts[3]
78 };
79 var vbWidth = parseFloat(viewbox.width);
80 var vbHeight = parseFloat(viewbox.height);
81
82 if (!isFinitePositive(vbWidth) || !isFinitePositive(vbHeight)) return;
83 if (units(viewbox.width) !== units(viewbox.height)) return;
84
85 var ratio = vbWidth / vbHeight;
86
87 if (attrs.width) {
88 if (!isFinitePositive(width)) return;
89
90 return {
91 width: width,
92 height: width / ratio,
93 type: 'svg',
94 mime: 'image/svg+xml',
95 wUnits: units(attrs.width),
96 hUnits: units(attrs.width)
97 };
98 }
99
100 if (attrs.height) {
101 if (!isFinitePositive(height)) return;
102
103 return {
104 width: height * ratio,
105 height: height,
106 type: 'svg',
107 mime: 'image/svg+xml',
108 wUnits: units(attrs.height),
109 hUnits: units(attrs.height)
110 };
111 }
112
113 return {
114 width: vbWidth,
115 height: vbHeight,
116 type: 'svg',
117 mime: 'image/svg+xml',
118 wUnits: units(viewbox.width),
119 hUnits: units(viewbox.height)
120 };
121}
122
123
124module.exports = function () {
125 var state = STATE_IDENTIFY;
126 var data_len = 0;
127 var str = '';
128
129 var parser = new Transform({
130 readableObjectMode: true,
131 transform: function transform(chunk, encoding, next) {
132 switch (state) {
133 case STATE_IDENTIFY:
134 var i = 0, max = chunk.length;
135
136 while (i < max && isWhiteSpace(chunk[i])) i++;
137
138 if (i >= max) {
139 data_len += chunk.length;
140
141 if (data_len > MAX_DATA_LENGTH) {
142 state = STATE_IGNORE;
143 parser.push(null);
144 }
145
146 } else if (chunk[i] === 0x3c /* < */) {
147 state = STATE_PARSE;
148 return transform(chunk, encoding, next);
149
150 } else {
151 state = STATE_IGNORE;
152 parser.push(null);
153 }
154
155 break;
156
157 case STATE_PARSE:
158 str += chunk.toString();
159
160 var result = parseSvg(str);
161
162 if (result) {
163 parser.push(result);
164 parser.push(null);
165 break;
166 }
167
168 data_len += chunk.length;
169
170 if (data_len > MAX_DATA_LENGTH) {
171 state = STATE_IGNORE;
172 parser.push(null);
173 }
174
175 break;
176 }
177
178 next();
179 },
180
181 flush: function () {
182 state = STATE_IGNORE;
183 parser.push(null);
184 }
185 });
186
187 return parser;
188};