Files
2025-10-12 09:13:56 +02:00

213 lines
5.3 KiB
C++

/*
* MCU renderer fontconv
* BDF import
*
* (C) 2023 Gissio
*
* License: MIT
*/
#include <cstdlib>
#include <fstream>
#include "import_bdf.h"
#include "utils.h"
using namespace std;
enum BDFState
{
BDF_STATE_ROOT,
BDF_STATE_PROPERTIES,
BDF_STATE_CHAR,
BDF_STATE_BITMAP
};
static string filter(string &line)
{
string output;
for (char c : line)
{
if (c >= ' ')
output += c;
}
return output;
}
static string getBDFString(string &value)
{
return value.substr(1, value.size() - 2);
}
static bool getBDFValue(string line, string key, string &value)
{
if (line.size() > key.size())
key += " ";
bool match = to_upper(line).starts_with(to_upper(key));
if (match)
value = line.substr(key.size());
return match;
}
static uint8_t getHexByte(string str)
{
return strtoul(str.substr(0, 2).c_str(), NULL, 16);
}
Font loadBDFFont(string filename,
set<Charcode> &charcodeSet)
{
Font font;
ifstream file(filename);
if (!file.good())
throw runtime_error("could not open '" +
filename +
"'");
string line;
uint32_t lineNumber = 1;
BDFState bdfState = BDF_STATE_ROOT;
string value;
Charcode charcode;
Glyph glyph;
uint32_t glyphY;
string familyName;
string weightName;
while (getline(file, line))
{
line = filter(line);
switch (bdfState)
{
case BDF_STATE_ROOT:
if (getBDFValue(line, "FONT", value))
font.name = value;
else if (getBDFValue(line, "SIZE", value))
{
vector<string> parameters = split(value, ' ');
font.capHeight = stoi(parameters[0]);
}
else if (getBDFValue(line, "STARTPROPERTIES", value))
bdfState = BDF_STATE_PROPERTIES;
else if (getBDFValue(line, "STARTCHAR", value))
bdfState = BDF_STATE_CHAR;
break;
case BDF_STATE_PROPERTIES:
if (getBDFValue(line, "FAMILY_NAME", value))
familyName = getBDFString(value);
else if (getBDFValue(line, "WEIGHT_NAME", value))
weightName = getBDFString(value);
else if (getBDFValue(line, "COPYRIGHT", value))
font.copyright = getBDFString(value);
else if (getBDFValue(line, "FONT_ASCENT", value))
font.ascent = stoi(value);
else if (getBDFValue(line, "FONT_DESCENT", value))
font.descent = stoi(value);
else if (getBDFValue(line, "CAP_HEIGHT", value))
font.capHeight = stoi(value);
else if (getBDFValue(line, "ENDPROPERTIES", value))
bdfState = BDF_STATE_ROOT;
break;
case BDF_STATE_CHAR:
if (getBDFValue(line, "ENCODING", value))
charcode = stoi(value);
if (getBDFValue(line, "DWIDTH", value))
{
vector<string> parameters = split(value, ' ');
glyph.advance = stoi(parameters[0]);
}
else if (getBDFValue(line, "BBX", value))
{
vector<string> parameters = split(value, ' ');
if (parameters.size() != 4)
throw runtime_error("bdf file invalid (line " +
to_string(lineNumber) +
")");
glyph.width = stoi(parameters[0]);
glyph.height = stoi(parameters[1]);
glyph.left = stoi(parameters[2]);
glyph.bottom = stoi(parameters[3]);
glyph.bitmap.resize(glyph.width * glyph.height);
}
else if (getBDFValue(line, "BITMAP", value))
{
bdfState = BDF_STATE_BITMAP;
glyphY = 0;
}
break;
case BDF_STATE_BITMAP:
if (getBDFValue(line, "ENDCHAR", value))
{
bdfState = BDF_STATE_ROOT;
if (charcodeSet.empty() ||
charcodeSet.contains(charcode))
font.add(charcode, glyph);
}
else
{
uint32_t glyphX = 0;
while (line.size() >= 2)
{
uint8_t n = getHexByte(line);
uint32_t pixelNum = 0;
while ((glyphX < glyph.width) && (pixelNum < 8))
{
uint8_t alpha = (n & 0x80) ? 0xff : 0;
glyph.bitmap[glyphY * glyph.width + glyphX] = alpha;
n <<= 1;
pixelNum++;
glyphX++;
}
line = line.substr(2);
}
glyphY++;
}
break;
}
lineNumber++;
}
if (font.ascent == INT_MIN)
font.ascent = font.boundingBoxBottom + font.boundingBoxHeight;
if (font.descent == INT_MIN)
font.descent = font.boundingBoxBottom;
if (familyName != "")
font.name = familyName;
if (weightName != "")
{
font.name += " ";
font.name += weightName;
}
return font;
}