213 lines
5.3 KiB
C++
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;
|
|
}
|