litehtml/containers/test/Font.cpp
2024-05-20 00:40:31 +03:00

219 lines
5.0 KiB
C++

#define _CRT_SECURE_NO_WARNINGS
#include "Font.h"
#include "canvas_ity.hpp"
using namespace canvas_ity;
string readfile(string filename);
void draw_image(canvas& cvs, int x, int y, const Bitmap& bmp);
bool set_font(canvas& cvs, const string& raw_font_data, int pixel_size);
void set_color(canvas& cvs, brush_type type, color c);
RasterFont::size_name RasterFont::installed_fonts[] =
{
{ 12, "terminus-12px.yaff" },
{ 14, "terminus-14px.yaff" },
{ 16, "terminus-16px.yaff" },
{ 18, "terminus-18px.yaff" },
{ 20, "terminus-20px.yaff" },
{ 22, "terminus-22px.yaff" },
{ 24, "terminus-24px.yaff" },
{ 28, "terminus-28px.yaff" },
{ 32, "terminus-32px.yaff" },
{ 0, "" }
};
string get_font_dir()
{
string font_cpp = __FILE__;
auto i = font_cpp.find_last_of("\\/");
return font_cpp.substr(0, i) + "/fonts/";
}
RasterFont::RasterFont(int size, int weight)
{
// find most suitable font
string name;
if (size == 16 && weight == 700)
name = "terminus-16px-bold.yaff";
else
{
int min_diff = 1000;
int n = 0;
for (int i = 0; installed_fonts[i].size; i++)
{
int diff = abs(installed_fonts[i].size - size);
if (diff < min_diff)
{
min_diff = diff;
n = i;
}
}
name = installed_fonts[n].name;
}
load(get_font_dir() + name);
}
RasterFont* RasterFont::create(string name, int size, int weight)
{
if (lowcase(name) == "terminus")
return new RasterFont(size, weight);
return nullptr;
}
Bitmap RasterFont::get_glyph(int ch, color color)
{
if (glyphs[ch].width == 0)
{
Bitmap bmp(width, height, transparent);
bmp.draw_rect(1, 1, width - 2, height - 2, color);
return bmp;
}
else if (color != black)
{
Bitmap bmp = glyphs[ch];
bmp.replace_color(black, color);
return bmp;
}
else
{
return glyphs[ch];
}
}
// load .yaff font file in an ad hoc manner (can't parse arbitrary yaff files)
void RasterFont::load(string filename)
{
string text = readfile(filename);
string_vector lines = split_string(text, "\n", "", "");
int i;
// parse header
for (i = 0; i < (int)lines.size(); i++)
{
string line = lines[i];
trim(line);
if (line.empty() || line[0] == '#') continue; // skip empty lines and comments
auto sep = line.find(':');
if (sep == string::npos) return; // line without ':' - error
auto key = line.substr(0, sep); trim(key);
auto val = line.substr(sep + 1); trim(val);
if (val.empty()) break; // end of header
if (key == "cell-size") sscanf(val.c_str(), "%d %d", &width, &height);
else if (key == "ascent") ascent = atoi(val.c_str());
else if (key == "descent") descent = atoi(val.c_str());
}
// parse glyphs
// only u+NNNN: label is recognized, all others are skipped
auto parse_key = [&]() {
int ch = -1;
for (; i < (int)lines.size(); i++)
{
string line = lines[i];
trim(line);
if (line.empty()) continue;
if (line.find(':') == string::npos) break; // start of glyph data
if (line.substr(0, 2) == "u+")
sscanf(line.c_str(), "u+%X:", (unsigned int*) &ch);
}
return ch;
};
auto parse_glyph = [&](int ch) {
int glyph_width = (int)trim(lines[i]).size();
Bitmap& glyph = glyphs[ch] = Bitmap(glyph_width, height, transparent);
for (int y = 0; i < (int)lines.size() && y < height; i++, y++)
{
string line = trim(lines[i]);
for (int x = 0; x < min((int)line.size(), glyph_width); x++)
{
if (line[x] == '@')
glyph.set_pixel(x, y, black);
}
}
};
while (i < (int)lines.size())
{
int ch = parse_key();
if (ch < 0) break;
parse_glyph(ch);
}
x_height = glyphs[(int)'x'].find_picture(transparent).height;
}
int RasterFont::text_width(string text)
{
utf8_to_utf32 utf32(text);
int width_ = 0;
for (const char32_t* p = utf32; *p; p++)
width_ += get_glyph(*p, black).width;
return width_;
}
void RasterFont::draw_text(canvas& cvs, string text, color color, int x, int y)
{
utf8_to_utf32 utf32(text);
for (const char32_t* p = utf32; *p; p++)
{
Bitmap glyph = get_glyph(*p, color);
draw_image(cvs, x, y, glyph);
x += glyph.width;
}
}
// keys must be in lowcase
string_map OutlineFont::installed_fonts =
{
{ "ahem", "ahem.ttf" }
};
OutlineFont* OutlineFont::create(string name, int size)
{
lcase(name);
if (installed_fonts.count(name))
return new OutlineFont(name, size);
return nullptr;
}
OutlineFont::OutlineFont(string name, int size) : name(name), size(size)
{
string filename = at(installed_fonts, name);
data = readfile(get_font_dir() + filename);
canvas cvs;
set_font(cvs, data, size);
cvs.get_font_metrics(ascent, descent, height, x_height);
}
int OutlineFont::text_width(string text)
{
canvas cvs;
set_font(cvs, data, size);
return (int)ceil(cvs.measure_text(text.c_str()));
}
void OutlineFont::draw_text(canvas& cvs, string text, color color, int x, int y)
{
set_font(cvs, data, size);
cvs.text_baseline = top;
set_color(cvs, fill_style, color);
cvs.fill_text(text.c_str(), (float)x, (float)y);
}
Font* Font::create(string name, int size, int weight)
{
if (RasterFont* font = RasterFont::create(name, size, weight))
return font;
return OutlineFont::create(name, size);
}