version 2.8
datafile.hh
1 // Class to store the data of a font while it is being processed.
2 // This class can be safely cloned using the default copy constructor.
3 
4 #pragma once
5 #include <cstdint>
6 #include <vector>
7 #include <string>
8 #include <fstream>
9 #include <memory>
10 #include <map>
11 
12 namespace mcufont
13 {
14 
15 class DataFile
16 {
17 public:
18  typedef std::vector<uint8_t> pixels_t;
19 
20  struct dictentry_t
21  {
22  pixels_t replacement; // The expanded version of this block.
23  int score; // Number of bytes that having this entry saves.
24  bool ref_encode; // Encode using references to other dictionary entries.
25 
26  dictentry_t(): score(0), ref_encode(false) {}
27  };
28 
29  struct glyphentry_t
30  {
31  pixels_t data; // The full data of the glyph.
32  std::vector<int> chars; // Characters that this glyph represents.
33  int width; // Tracking width of the character.
34  };
35 
36  struct fontinfo_t
37  {
38  std::string name; // Name of the typeface
39  int max_width; // Width of the character bounding box.
40  int max_height; // Height of the character bounding box.
41  int baseline_x; // X coordinate (from left) of the baseline.
42  int baseline_y; // Y coordinate (from top) of the baseline.
43  int line_height; // Line height (vertical advance).
44  int flags;
45  };
46 
47  static const int FLAG_MONOSPACE = 0x01;
48  static const int FLAG_BW = 0x02;
49 
50  // Construct from data in memory.
51  DataFile(const std::vector<dictentry_t> &dictionary,
52  const std::vector<glyphentry_t> &glyphs,
53  const fontinfo_t &fontinfo);
54 
55  // Save to a file (custom format)
56  void Save(std::ostream &file) const;
57 
58  // Load from a file (custom format)
59  // Returns nullptr if load fails.
60  static std::unique_ptr<DataFile> Load(std::istream &file);
61 
62  // Get or set an entry in the dictionary. The size of the dictionary
63  // is constant. Entries 0 to 23 are reserved for special purposes.
64  static const size_t dictionarysize = 256 - 24;
65  const dictentry_t &GetDictionaryEntry(size_t index) const
66  { return m_dictionary.at(index); }
67  void SetDictionaryEntry(size_t index, const dictentry_t &value);
68  const std::vector<dictentry_t> &GetDictionary() const
69  { return m_dictionary; }
70 
71  // Get the index of the dictionary entry with the lowest score.
72  size_t GetLowScoreIndex() const
73  { return m_lowscoreindex; }
74 
75  // Get an entry in the glyph table.
76  size_t GetGlyphCount() const
77  { return m_glyphtable.size(); }
78  const glyphentry_t &GetGlyphEntry(size_t index) const
79  { return m_glyphtable.at(index); }
80  const std::vector<glyphentry_t> &GetGlyphTable() const
81  { return m_glyphtable; }
82 
83  // Create a map of char indices to glyph indices
84  std::map<size_t, size_t> GetCharToGlyphMap() const;
85 
86  // Get the information that applies to all glyphs.
87  const fontinfo_t &GetFontInfo() const
88  { return m_fontinfo; }
89 
90  // Show a glyph as text.
91  std::string GlyphToText(size_t index) const;
92 
93  // Get the random generator seed
94  // The seed is stored in the datafile to get deterministic behaviour
95  // for debugging.
96  uint32_t GetSeed() const { return m_seed; }
97  void SetSeed(uint32_t seed) { m_seed = seed; }
98 
99 private:
100  std::vector<dictentry_t> m_dictionary;
101  std::vector<glyphentry_t> m_glyphtable;
102  fontinfo_t m_fontinfo;
103  uint32_t m_seed;
104 
105  size_t m_lowscoreindex;
106 
107  void UpdateLowScoreIndex();
108 };
109 
110 std::ostream& operator<<(std::ostream& os, const DataFile::pixels_t& str);
111 std::istream& operator>>(std::istream& is, DataFile::pixels_t& str);
112 
113 }
114 
115 #ifdef CXXTEST_RUNNING
116 #include <cxxtest/TestSuite.h>
117 
118 using namespace mcufont;
119 
120 class DataFileTests: public CxxTest::TestSuite
121 {
122 public:
123  void testFileLoad()
124  {
125  std::istringstream s(testfile);
126  std::unique_ptr<DataFile> f = DataFile::Load(s);
127 
128  TS_ASSERT_EQUALS(f->GetFontInfo().name, "Sans Serif");
129  TS_ASSERT_EQUALS(f->GetFontInfo().max_width, 4);
130  TS_ASSERT_EQUALS(f->GetFontInfo().max_height, 6);
131  TS_ASSERT_EQUALS(f->GetDictionaryEntry(0).score, 5);
132  TS_ASSERT_EQUALS(f->GetDictionaryEntry(1).score, 13);
133  TS_ASSERT_EQUALS(f->GetGlyphCount(), 3);
134 
135  DataFile::pixels_t expected = {
136  0,15,0,15, 0,15,0,15, 0,15,0,15, 0,15,0,15, 0,15,0,15, 0,15,0,15
137  };
138  TS_ASSERT_EQUALS(f->GetGlyphEntry(0).data.size(), 24);
139  TS_ASSERT(f->GetGlyphEntry(0).data == expected);
140  }
141 
142  void testFileSave()
143  {
144  std::istringstream is1(testfile);
145  std::unique_ptr<DataFile> f1 = DataFile::Load(is1);
146 
147  std::ostringstream os;
148  f1->Save(os);
149 
150  std::string text = os.str();
151  std::istringstream is2(text);
152  std::unique_ptr<DataFile> f2 = DataFile::Load(is2);
153 
154  TS_ASSERT_EQUALS(f1->GetFontInfo().name, f2->GetFontInfo().name);
155  TS_ASSERT(f1->GetGlyphEntry(0).data == f2->GetGlyphEntry(0).data);
156  }
157 
158 private:
159  static constexpr const char *testfile =
160  "Version 1\n"
161  "FontName Sans Serif\n"
162  "MaxWidth 4\n"
163  "MaxHeight 6\n"
164  "BaselineX 1\n"
165  "BaselineY 1\n"
166  "DictEntry 5 0 0F0F0\n"
167  "DictEntry 13 0 F0F0F0\n"
168  "DictEntry 1 0 F0F0F0\n"
169  "Glyph 1,2,3 4 0F0F0F0F0F0F0F0F0F0F0F0F\n"
170  "Glyph 4 4 0F0F0F0F0F0F0F0F0F0F0F0F\n"
171  "Glyph 5 4 0F0F0F0F0F0F0F0F0F0F0F0F\n";
172 };
173 
174 #endif