forked from SidOfc/leather
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsvg.js
More file actions
135 lines (114 loc) · 3.65 KB
/
svg.js
File metadata and controls
135 lines (114 loc) · 3.65 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
import {lazystream} from '../util.js';
const UNITS = {
m: 3779.5275590551178,
cm: 37.795275590551178,
mm: 3.7795275590551178,
pc: 0.1111111111111111,
pt: 1.3333333333333333,
in: 96,
ex: 8,
px: 1,
em: 16,
rem: 16,
};
const WIDTH_PATTERN = /width=(["'])([^%]+?)\1/i;
const HEIGHT_PATTERN = /height=(["'])([^%]+?)\1/i;
const VIEWBOX_PATTERN = /viewbox=(["'])([^\1]+?)\1/i;
const DIMENSION_PATTERN = new RegExp(
`^([0-9.]+(?:e\\d+)?)(${Object.keys(UNITS).join('|')})?$`
);
function parseDimension(value) {
const matches = value.toLowerCase().match(DIMENSION_PATTERN);
if (matches)
return Math.round(Number(matches[1]) * (UNITS[matches[2]] || UNITS.px));
}
function extractWidth(data) {
const matches = data.match(WIDTH_PATTERN);
if (matches) return parseDimension(matches[2]);
}
function extractHeight(data) {
const matches = data.match(HEIGHT_PATTERN);
if (matches) return parseDimension(matches[2]);
}
function extractViewbox(data) {
const matches = data.match(VIEWBOX_PATTERN);
if (matches) {
const [width, height] = matches[2]
.split(' ')
.slice(2)
.map(parseDimension);
return {width, height, ratio: width / height};
}
return {width: 0, height: 0, ratio: 1};
}
export function attributes(input) {
const stream = lazystream(input);
const bytes = [];
let startIndex = null;
let insideAttr = false;
while (stream.more()) {
const byte = stream.take()[0];
if (byte === 0x3c) {
// found potential opening tag "<" character
const nextByte = stream.take()[0];
if (nextByte === 0x73 || nextByte === 0x53) {
// next byte is either "s" or "S", assume
// that we have found the opening svg tag
stream.skip(3);
startIndex = stream.position();
}
} else if (!Number.isInteger(startIndex)) {
// we have not yet found svg opening tag,
// continue reading the next byte immediately
continue;
} else if (byte === 0x5c) {
// encountered a backslash, ignore next byte
stream.take();
} else if (byte === 0x22 || byte === 0x27) {
// encountered single or double quote,
// assume we are entering an attribute value
insideAttr = !insideAttr;
bytes.push(byte);
} else if (!insideAttr && byte === 0x3e) {
// encountered ">" character while not
// inside an attribute value, assume
// svg opening tag end
break;
} else {
// store bytes so we can convert them
// to a string for attribute parsing
bytes.push(byte);
}
}
const data = Buffer.from(bytes).toString();
const width = extractWidth(data);
const height = extractHeight(data);
const result = {
width: 0,
height: 0,
size: stream.size(),
mime: 'image/svg+xml',
};
stream.close();
if (width && height) {
Object.assign(result, {width, height});
} else {
const viewbox = extractViewbox(data);
if (width)
Object.assign(result, {
width,
height: Math.floor(width / viewbox.ratio),
});
else if (height)
Object.assign(result, {
width: Math.floor(height * viewbox.ratio),
height,
});
else
Object.assign(result, {
width: viewbox.width,
height: viewbox.height,
});
}
return result;
}