-
Notifications
You must be signed in to change notification settings - Fork 2.2k
/
glyph_source.js
112 lines (89 loc) · 3.44 KB
/
glyph_source.js
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
'use strict';
var normalizeURL = require('../util/mapbox').normalizeGlyphsURL;
var getArrayBuffer = require('../util/ajax').getArrayBuffer;
var Glyphs = require('../util/glyphs');
var Protobuf = require('pbf');
module.exports = GlyphSource;
function GlyphSource(url, glyphAtlas) {
this.url = url && normalizeURL(url);
this.glyphAtlas = glyphAtlas;
this.stacks = [];
this.loading = {};
}
GlyphSource.prototype.getSimpleGlyphs = function(fontstack, glyphIDs, uid, callback) {
if (this.stacks[fontstack] === undefined) this.stacks[fontstack] = {};
var glyphs = {};
var stack = this.stacks[fontstack];
var glyphAtlas = this.glyphAtlas;
// the number of pixels the sdf bitmaps are padded by
var buffer = 3;
var missing = {};
var remaining = 0;
var range;
for (var i = 0; i < glyphIDs.length; i++) {
var glyphID = glyphIDs[i];
range = Math.floor(glyphID / 256);
if (stack[range]) {
var glyph = stack[range].glyphs[glyphID];
var rect = glyphAtlas.addGlyph(uid, fontstack, glyph, buffer);
if (glyph) glyphs[glyphID] = new SimpleGlyph(glyph, rect, buffer);
} else {
if (missing[range] === undefined) {
missing[range] = [];
remaining++;
}
missing[range].push(glyphID);
}
}
if (!remaining) callback(undefined, glyphs);
var onRangeLoaded = function(err, range, data) {
// TODO not be silent about errors
if (!err) {
var stack = this.stacks[fontstack][range] = data.stacks[0];
for (var i = 0; i < missing[range].length; i++) {
var glyphID = missing[range][i];
var glyph = stack.glyphs[glyphID];
var rect = glyphAtlas.addGlyph(uid, fontstack, glyph, buffer);
if (glyph) glyphs[glyphID] = new SimpleGlyph(glyph, rect, buffer);
}
}
remaining--;
if (!remaining) callback(undefined, glyphs);
}.bind(this);
for (var r in missing) {
this.loadRange(fontstack, r, onRangeLoaded);
}
};
// A simplified representation of the glyph containing only the properties needed for shaping.
function SimpleGlyph(glyph, rect, buffer) {
this.advance = glyph.advance;
this.left = glyph.left - buffer;
this.top = glyph.top + buffer;
this.rect = rect;
}
GlyphSource.prototype.loadRange = function(fontstack, range, callback) {
if (range * 256 > 65535) return callback('gyphs > 65535 not supported');
if (this.loading[fontstack] === undefined) this.loading[fontstack] = {};
var loading = this.loading[fontstack];
if (loading[range]) {
loading[range].push(callback);
} else {
loading[range] = [callback];
var rangeName = (range * 256) + '-' + (range * 256 + 255);
var url = glyphUrl(fontstack, rangeName, this.url);
getArrayBuffer(url, function(err, data) {
var glyphs = !err && new Glyphs(new Protobuf(new Uint8Array(data)));
for (var i = 0; i < loading[range].length; i++) {
loading[range][i](err, range, glyphs);
}
delete loading[range];
});
}
};
function glyphUrl(fontstack, range, url, subdomains) {
subdomains = subdomains || 'abc';
return url
.replace('{s}', subdomains[fontstack.length % subdomains.length])
.replace('{fontstack}', fontstack)
.replace('{range}', range);
}