Rought font extractor that probably has a memory error causing it to behave mysteriously, and the designer now uses a json file of the characters that will work.
parent
9ab064126f
commit
a9e25668fb
@ -0,0 +1,207 @@ |
||||
#include <fstream> |
||||
#include <iostream> |
||||
#include <chrono> // for operator""s, chrono_literals |
||||
#include <thread> // for sleep_for |
||||
#include "dbc.hpp" |
||||
#include <filesystem> |
||||
#include <fcntl.h> |
||||
#include "constants.hpp" |
||||
#include <fmt/core.h> |
||||
#include <locale> |
||||
#include <codecvt> |
||||
#include <vector> |
||||
#include <nlohmann/json.hpp> |
||||
#include <ft2build.h> |
||||
#include <SFML/Graphics/Font.hpp> |
||||
#include FT_FREETYPE_H |
||||
#include FT_TRUETYPE_TABLES_H |
||||
#include FT_TRUETYPE_IDS_H |
||||
|
||||
using namespace nlohmann; |
||||
using namespace fmt; |
||||
namespace fs = std::filesystem; |
||||
using std::string, std::wstring, std::vector; |
||||
|
||||
const size_t CLEAR_CACHE_POINT=100; |
||||
|
||||
struct FontIndex { |
||||
size_t cm_index; // charmap index
|
||||
string glyph; |
||||
}; |
||||
|
||||
struct FontExtractor { |
||||
size_t $clear_count = 1; |
||||
wchar_t ui_base_char = L'█'; |
||||
vector<FontIndex> $index; |
||||
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> $converter; |
||||
vector<string> $charmap; |
||||
vector<wchar_t> $wcharmap; |
||||
sf::Font $font; |
||||
sf::FloatRect $grid_bounds; |
||||
int $font_size; |
||||
fs::path $font_path; |
||||
json $results; |
||||
|
||||
FontExtractor(fs::path font_path) : |
||||
$font_path(font_path) |
||||
{ |
||||
bool good = $font.loadFromFile($font_path.string()); |
||||
dbc::check(good, format("failed to load font {}", $font_path.string())); |
||||
$font.setSmooth(false); |
||||
|
||||
for(int i = 100; i < 200; i++) { |
||||
auto glyph = $font.getGlyph(ui_base_char, i, false); |
||||
if(glyph.bounds.width > 0 && glyph.bounds.height > 0) { |
||||
$grid_bounds = glyph.bounds; |
||||
$font_size = i; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
dbc::check($grid_bounds.width > 0 && $grid_bounds.height > 0, "couldn't find a valid font size"); |
||||
|
||||
println("!!!!!!!!!!!!!!!!!!!!! FONT SIZE {}", $font_size); |
||||
} |
||||
|
||||
void configure_font() { |
||||
FT_ULong charcode; |
||||
FT_UInt gindex; |
||||
FT_String buf[32] = {0}; |
||||
FT_Library library; |
||||
FT_Face face; |
||||
|
||||
auto error = FT_Init_FreeType(&library); |
||||
dbc::check(!error, "Failed to initialize freetype library."); |
||||
|
||||
error = FT_New_Face(library, FONT_FILE_NAME, 0, &face); |
||||
|
||||
dbc::check(face->num_faces == 1, format("Font {} has {} but I only support 1.", FONT_FILE_NAME, face->num_faces)); |
||||
dbc::check(face->charmap, "Font doesn't have a charmap."); |
||||
|
||||
println("Font charmaps {}", face->num_charmaps); |
||||
auto active = FT_Get_Charmap_Index(face->charmap); |
||||
|
||||
for(int i = 0; i < face->num_charmaps; i++) { |
||||
auto format = FT_Get_CMap_Format(face->charmaps[i]); |
||||
auto lang_id = FT_Get_CMap_Language_ID(face->charmaps[i]); |
||||
|
||||
println("{}: {} {}, active: {}, platform: {}, encoding: {}, ms_unicode: {}, language: {}", |
||||
i, |
||||
format >= 0 ? "format" : "synthetic", |
||||
format, |
||||
i == active, |
||||
face->charmaps[i]->platform_id, |
||||
face->charmaps[i]->encoding_id, |
||||
TT_MS_ID_UNICODE_CS, |
||||
lang_id); |
||||
|
||||
// BUG: this is windows only
|
||||
if(face->charmaps[i]->encoding_id == TT_MS_ID_UNICODE_CS) { |
||||
FT_Set_Charmap(face, face->charmaps[i]); |
||||
} |
||||
} |
||||
|
||||
bool has_names = FT_HAS_GLYPH_NAMES(face); |
||||
|
||||
// get the first char of the map
|
||||
charcode = FT_Get_First_Char(face, &gindex); |
||||
|
||||
// go through every char and pre-convert
|
||||
for(int i = 0; gindex; i++) { |
||||
if(has_names) FT_Get_Glyph_Name(face, gindex, buf, 32 ); |
||||
|
||||
// store it in my pre-convert charmap for later
|
||||
wstring wcodestr{(wchar_t)charcode}; |
||||
string codestr = from_unicode(wcodestr); |
||||
$charmap.emplace_back(codestr); |
||||
$wcharmap.push_back(charcode); |
||||
|
||||
// keep going
|
||||
charcode = FT_Get_Next_Char(face, charcode, &gindex ); |
||||
} |
||||
|
||||
FT_Done_Face(face); |
||||
FT_Done_FreeType(library); |
||||
|
||||
println("FreeType extracted {} glyphs.", $charmap.size()); |
||||
} |
||||
|
||||
size_t next_valid_char(size_t cur_char) { |
||||
for(size_t i = cur_char+1; i < $wcharmap.size(); i++) { |
||||
wchar_t test_char = $wcharmap[i]; |
||||
if($font.hasGlyph(test_char)) { |
||||
auto glyph = $font.getGlyph(test_char, $font_size, false); |
||||
auto bounds = glyph.bounds; |
||||
|
||||
// skip bad chars
|
||||
if(bounds.width <= 0 || bounds.height <= 0) continue; |
||||
|
||||
if(bounds.width <= $grid_bounds.width && |
||||
bounds.height <= $grid_bounds.height) { |
||||
return i; |
||||
} |
||||
} |
||||
|
||||
clear_font_cache(); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
void extract_valid() { |
||||
size_t cur_char = next_valid_char(1); |
||||
|
||||
while(cur_char != 0) { |
||||
string out = from_unicode(wstring_at(cur_char)); |
||||
$results.push_back(out); |
||||
cur_char = next_valid_char(cur_char + 1); |
||||
} |
||||
} |
||||
|
||||
string from_unicode(wstring input) { |
||||
try { |
||||
return $converter.to_bytes(input); |
||||
} catch(...) { |
||||
return $converter.to_bytes(L"?"); |
||||
} |
||||
} |
||||
|
||||
string at(size_t x) { |
||||
return $index[x].glyph; |
||||
} |
||||
|
||||
wstring wstring_at(size_t cm_index) { |
||||
return wstring{$wcharmap[cm_index]}; |
||||
} |
||||
|
||||
void clear_font_cache() { |
||||
if($clear_count % CLEAR_CACHE_POINT == 0) { |
||||
bool good = $font.loadFromFile($font_path.string()); |
||||
dbc::check(good, format("failed to load font {}", $font_path.string())); |
||||
$font.setSmooth(false); |
||||
|
||||
$clear_count++; |
||||
} |
||||
} |
||||
}; |
||||
|
||||
int main(int argc, char *argv[]) { |
||||
if(argc != 3) { |
||||
println("USAGE: fontextractor <input> <output>"); |
||||
return 1; |
||||
} |
||||
|
||||
fs::path font_path = argv[1]; |
||||
fs::path out_path = argv[2]; |
||||
|
||||
dbc::check(fs::exists(font_path), "ERROR: input file doesn't exist"); |
||||
|
||||
FontExtractor fex(font_path); |
||||
fex.configure_font(); |
||||
fex.extract_valid(); |
||||
|
||||
std::ofstream json_out(out_path, std::ios::binary); |
||||
json_out << fex.$results.dump(2) << std::endl; |
||||
|
||||
println("Wrote {} chars to {}.", fex.$results.size(), out_path.string()); |
||||
} |
Loading…
Reference in new issue