aboutsummaryrefslogtreecommitdiffstats
path: root/tools/mcufontencoder/src/exporttools.cc
blob: b58ee8ec9c542c61dcc8731ba62366589435cf21 (plain)
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
#include "exporttools.hh"
#include <iomanip>
#include <set>

namespace mcufont {
    
    
// Convert a file name to a valid C identifier
std::string filename_to_identifier(std::string name)
{
    // If the name contains path separators (/ or \), take only the last part.
    size_t pos = name.find_last_of("/\\");
    if (pos != std::string::npos)
        name = name.substr(pos + 1);
    
    // If the name contains a file extension, strip it.
    pos = name.find_first_of(".");
    if (pos != std::string::npos)
        name = name.substr(0, pos);
    
    // Replace any special characters with _.
    for (pos = 0; pos < name.size(); pos++)
    {
        if (!isalnum(name.at(pos)))
            name.at(pos) = '_';
    }
    
    return name;
}

// Write a vector of integers as line-wrapped hex/integer data for initializing const array.
void wordwrap_vector(std::ostream &out, const std::vector<unsigned> &data,
                     const std::string &prefix, size_t width)
{
    int values_per_column = (width <= 2) ? 16 : 8;
    
    std::ios::fmtflags flags(out.flags());
    out << prefix;
    out << std::hex << std::setfill('0');
    for (size_t i = 0; i < data.size(); i++)
    {
        if (i % values_per_column == 0 && i != 0)
            out << std::endl << prefix;
        
        out << "0x" << std::setw(width) << (int)data.at(i) << ", ";
    }
    out.flags(flags);
}

// Write a vector of integers as a C constant array of given datatype.
 void write_const_table(std::ostream &out, const std::vector<unsigned> &data,
                        const std::string &datatype, const std::string &tablename,
                        size_t width)
{
    out << "static const " << datatype << " " << tablename;
    out << "[" << data.size() << "] = {" << std::endl;
    wordwrap_vector(out, data, "    ", width);
    out << std::endl << "};" << std::endl;
    out << std::endl;
}    

int get_min_x_advance(const DataFile &datafile)
{
    int min = datafile.GetGlyphEntry(0).width;
    
    for (const DataFile::glyphentry_t &g : datafile.GetGlyphTable())
    {
        if (min > g.width)
            min = g.width;
    }
    
    return min;
}

int get_max_x_advance(const DataFile &datafile)
{
    int max = 0;
    
    for (const DataFile::glyphentry_t &g : datafile.GetGlyphTable())
    {
        if (max < g.width)
            max = g.width;
    }
    
    return max;
}

// Select the character to use as a fallback.
int select_fallback_char(const DataFile &datafile)
{
    std::set<int> chars;
    
    size_t i = 0;
    for (const DataFile::glyphentry_t &g: datafile.GetGlyphTable())
    {
        for (size_t c: g.chars)
        {
            chars.insert(c);
        }
        i++;
    }
    
    if (chars.count(0xFFFD))
        return 0xFFFD; // Unicode replacement character
    
    if (chars.count(0))
        return 0; // Used by many BDF fonts as replacement char
    
    if (chars.count('?'))
        return '?';
    
    return ' ';
}

// Decide how to best divide the characters in the font into ranges.
// Limitations are:
//  - Gaps longer than minimum_gap should result in separate ranges.
//  - Each range can have encoded data size of at most maximum_size.
std::vector<char_range_t> compute_char_ranges(const DataFile &datafile,
    std::function<size_t(size_t)> get_encoded_glyph_size,
    size_t maximum_size,
    size_t minimum_gap)
{
    std::vector<char_range_t> result;
    std::map<size_t, size_t> char_to_glyph = datafile.GetCharToGlyphMap();
    std::vector<size_t> chars;
    
    // Get list of all characters in numeric order.
    for (auto iter : char_to_glyph)
        chars.push_back(iter.first);
    
    // Pick out ranges until we have processed all characters
    size_t i = 0;
    while (i < chars.size())
    {
        char_range_t range;
        range.first_char = chars.at(i);
        
        // Find the point where there is a gap larger than minimum_gap.
        i++;
        while (i < chars.size() && chars.at(i) - chars.at(i - 1) < minimum_gap)
            i++;
        
        uint16_t last_char = chars.at(i - 1);
        
        // Then store the indices of glyphs for each character
        size_t data_length = 0;
        for (size_t j = range.first_char; j <= last_char; j++)
        {
            if (char_to_glyph.count(j) == 0)
            {
                // Missing character
                range.glyph_indices.push_back(-1);
                continue;
            }
            
            int glyph_index = char_to_glyph[j];
            
            // Monitor the amount of the data in the range and split it
            // if it grows too large.
            data_length += get_encoded_glyph_size(glyph_index);
            if (data_length > maximum_size)
            {
                last_char = j - 1;
                break;
            }
            
            range.glyph_indices.push_back(glyph_index);
        }
        
        range.char_count = last_char - range.first_char + 1;
        result.push_back(range);
    }
    
    return result;
}
    
    
}