// Class to store the data of a font while it is being processed. // This class can be safely cloned using the default copy constructor. #pragma once #include #include #include #include #include #include namespace mcufont { class DataFile { public: typedef std::vector pixels_t; struct dictentry_t { pixels_t replacement; // The expanded version of this block. int score; // Number of bytes that having this entry saves. bool ref_encode; // Encode using references to other dictionary entries. dictentry_t(): score(0), ref_encode(false) {} }; struct glyphentry_t { pixels_t data; // The full data of the glyph. std::vector chars; // Characters that this glyph represents. int width; // Tracking width of the character. }; struct fontinfo_t { std::string name; // Name of the typeface int max_width; // Width of the character bounding box. int max_height; // Height of the character bounding box. int baseline_x; // X coordinate (from left) of the baseline. int baseline_y; // Y coordinate (from top) of the baseline. int line_height; // Line height (vertical advance). int flags; }; static const int FLAG_MONOSPACE = 0x01; static const int FLAG_BW = 0x02; // Construct from data in memory. DataFile(const std::vector &dictionary, const std::vector &glyphs, const fontinfo_t &fontinfo); // Save to a file (custom format) void Save(std::ostream &file) const; // Load from a file (custom format) // Returns nullptr if load fails. static std::unique_ptr Load(std::istream &file); // Get or set an entry in the dictionary. The size of the dictionary // is constant. Entries 0 to 23 are reserved for special purposes. static const size_t dictionarysize = 256 - 24; const dictentry_t &GetDictionaryEntry(size_t index) const { return m_dictionary.at(index); } void SetDictionaryEntry(size_t index, const dictentry_t &value); const std::vector &GetDictionary() const { return m_dictionary; } // Get the index of the dictionary entry with the lowest score. size_t GetLowScoreIndex() const { return m_lowscoreindex; } // Get an entry in the glyph table. size_t GetGlyphCount() const { return m_glyphtable.size(); } const glyphentry_t &GetGlyphEntry(size_t index) const { return m_glyphtable.at(index); } const std::vector &GetGlyphTable() const { return m_glyphtable; } // Create a map of char indices to glyph indices std::map GetCharToGlyphMap() const; // Get the information that applies to all glyphs. const fontinfo_t &GetFontInfo() const { return m_fontinfo; } // Show a glyph as text. std::string GlyphToText(size_t index) const; // Get the random generator seed // The seed is stored in the datafile to get deterministic behaviour // for debugging. uint32_t GetSeed() const { return m_seed; } void SetSeed(uint32_t seed) { m_seed = seed; } private: std::vector m_dictionary; std::vector m_glyphtable; fontinfo_t m_fontinfo; uint32_t m_seed; size_t m_lowscoreindex; void UpdateLowScoreIndex(); }; std::ostream& operator<<(std::ostream& os, const DataFile::pixels_t& str); std::istream& operator>>(std::istream& is, DataFile::pixels_t& str); } #ifdef CXXTEST_RUNNING #include using namespace mcufont; class DataFileTests: public CxxTest::TestSuite { public: void testFileLoad() { std::istringstream s(testfile); std::unique_ptr f = DataFile::Load(s); TS_ASSERT_EQUALS(f->GetFontInfo().name, "Sans Serif"); TS_ASSERT_EQUALS(f->GetFontInfo().max_width, 4); TS_ASSERT_EQUALS(f->GetFontInfo().max_height, 6); TS_ASSERT_EQUALS(f->GetDictionaryEntry(0).score, 5); TS_ASSERT_EQUALS(f->GetDictionaryEntry(1).score, 13); TS_ASSERT_EQUALS(f->GetGlyphCount(), 3); DataFile::pixels_t expected = { 0,15,0,15, 0,15,0,15, 0,15,0,15, 0,15,0,15, 0,15,0,15, 0,15,0,15 }; TS_ASSERT_EQUALS(f->GetGlyphEntry(0).data.size(), 24); TS_ASSERT(f->GetGlyphEntry(0).data == expected); } void testFileSave() { std::istringstream is1(testfile); std::unique_ptr f1 = DataFile::Load(is1); std::ostringstream os; f1->Save(os); std::string text = os.str(); std::istringstream is2(text); std::unique_ptr f2 = DataFile::Load(is2); TS_ASSERT_EQUALS(f1->GetFontInfo().name, f2->GetFontInfo().name); TS_ASSERT(f1->GetGlyphEntry(0).data == f2->GetGlyphEntry(0).data); } private: static constexpr const char *testfile = "Version 1\n" "FontName Sans Serif\n" "MaxWidth 4\n" "MaxHeight 6\n" "BaselineX 1\n" "BaselineY 1\n" "DictEntry 5 0 0F0F0\n" "DictEntry 13 0 F0F0F0\n" "DictEntry 1 0 F0F0F0\n" "Glyph 1,2,3 4 0F0F0F0F0F0F0F0F0F0F0F0F\n" "Glyph 4 4 0F0F0F0F0F0F0F0F0F0F0F0F\n" "Glyph 5 4 0F0F0F0F0F0F0F0F0F0F0F0F\n"; }; #endif