1 #include <mbgl/text/glyph_pbf.hpp>
2 
3 #include <protozero/pbf_reader.hpp>
4 
5 namespace mbgl {
6 
parseGlyphPBF(const GlyphRange & glyphRange,const std::string & data)7 std::vector<Glyph> parseGlyphPBF(const GlyphRange& glyphRange, const std::string& data) {
8     std::vector<Glyph> result;
9     result.reserve(256);
10 
11     protozero::pbf_reader glyphs_pbf(data);
12 
13     while (glyphs_pbf.next(1)) {
14         auto fontstack_pbf = glyphs_pbf.get_message();
15         while (fontstack_pbf.next(3)) {
16             auto glyph_pbf = fontstack_pbf.get_message();
17 
18             Glyph glyph;
19             protozero::data_view glyphData;
20 
21             bool hasID = false, hasWidth = false, hasHeight = false, hasLeft = false,
22                  hasTop = false, hasAdvance = false;
23 
24             while (glyph_pbf.next()) {
25                 switch (glyph_pbf.tag()) {
26                 case 1: // id
27                     glyph.id = glyph_pbf.get_uint32();
28                     hasID = true;
29                     break;
30                 case 2: // bitmap
31                     glyphData = glyph_pbf.get_view();
32                     break;
33                 case 3: // width
34                     glyph.metrics.width = glyph_pbf.get_uint32();
35                     hasWidth = true;
36                     break;
37                 case 4: // height
38                     glyph.metrics.height = glyph_pbf.get_uint32();
39                     hasHeight = true;
40                     break;
41                 case 5: // left
42                     glyph.metrics.left = glyph_pbf.get_sint32();
43                     hasLeft = true;
44                     break;
45                 case 6: // top
46                     glyph.metrics.top = glyph_pbf.get_sint32();
47                     hasTop = true;
48                     break;
49                 case 7: // advance
50                     glyph.metrics.advance = glyph_pbf.get_uint32();
51                     hasAdvance = true;
52                     break;
53                 default:
54                     glyph_pbf.skip();
55                     break;
56                 }
57             }
58 
59             // Only treat this glyph as a correct glyph if it has all required fields. It also
60             // needs to satisfy a few metrics conditions that ensure that the glyph isn't bogus.
61             // All other glyphs are malformed.  We're also discarding all glyphs that are outside
62             // the expected glyph range.
63             if (!hasID || !hasWidth || !hasHeight || !hasLeft || !hasTop || !hasAdvance ||
64                 glyph.metrics.width >= 256 || glyph.metrics.height >= 256 ||
65                 glyph.metrics.left < -128 || glyph.metrics.left >= 128 ||
66                 glyph.metrics.top < -128 || glyph.metrics.top >= 128 ||
67                 glyph.metrics.advance >= 256 ||
68                 glyph.id < glyphRange.first || glyph.id > glyphRange.second) {
69                 continue;
70             }
71 
72             // If the area of width/height is non-zero, we need to adjust the expected size
73             // with the implicit border size, otherwise we expect there to be no bitmap at all.
74             if (glyph.metrics.width && glyph.metrics.height) {
75                 const Size size {
76                     glyph.metrics.width + 2 * Glyph::borderSize,
77                     glyph.metrics.height + 2 * Glyph::borderSize
78                 };
79 
80                 if (size.area() != glyphData.size()) {
81                     continue;
82                 }
83 
84                 glyph.bitmap = AlphaImage(size, reinterpret_cast<const uint8_t*>(glyphData.data()), glyphData.size());
85             }
86 
87             result.push_back(std::move(glyph));
88         }
89     }
90 
91     return result;
92 }
93 
94 } // namespace mbgl
95