Rev 2107 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* font.cc
* DIN Is Noise is copyright (c) 2006-2025 Jagannathan Sampath
* DIN Is Noise is released under GNU Public License 2.0
* For more information, please visit https://dinisnoise.org/
*/
#include <fstream>
#include <sstream>
#include <cmath>
#include "font.h"
#include "log.h"
#include "random.h"
#include "chrono.h"
using namespace std;
extern string user_data_dir;
static const char eol = '\n';
font::font (const string& filename) : mod(0)
#ifdef __SVG_FNT__
,svg ((user_data_dir + "text.svg").c_str(), ios::out)
#endif
#ifdef __PLOTTER_FNT__
,hpgl ((user_data_dir + "text.hpgl").c_str(), ios::out)
#endif
{
cellsize.x = 2;
cellsize.y = 2;
spacing.ch = 2 * cellsize.x;
headroom = 2 * cellsize.y;
fname = filename;
load (fname);
#ifdef __SVG_FNT__
svg << "<?xml version=\"1.0\"?>" << eol;
svg << "<svg xmlns=\"https://www.w3.org/2000/svg\">" << eol;
#endif
#ifdef __PLOTTER_FNT__
hpgl << "IN;" << eol;
#endif
}
font::~font () {
if (mod) save ();
#ifdef __SVG_FNT__
svg << "</svg>" << eol;
#endif
dlog << "--- destroyed font ---" << endl;
}
void font::load (const string& fn) {
string ffname (user_data_dir + fname);
ifstream file (ffname.c_str(), ios::in);
if (file) {
dlog << "<<< loading font from: " << ffname;
load (file);
dlog << ", done. >>>" << eol;
} else {
dlog << "!!! bad font file: " << ffname << " !!!" << endl;
}
}
void font::load (ifstream& file) {
string ignore;
characters.clear ();
kern.clear ();
file >> ignore >> name;
file >> ignore >> ignore; // copyright
file >> ignore >> nchars;
MAKETYPE (sumt, int, width, height)
sumt sum;
charwidth.max = charheight.max = 0;
sum.width = sum.height = 0;
for (int i = 0; i < nchars; i++) {
char c; file >> c;
glyph g;
file >> g.nln;
for (int l = 0; l < g.nln; ++l) {
line ln;
file >> ln.npts;
for (int s = 0; s < ln.npts; ++s) {
int x, y; file >> x >> y;
ln.points.push_back (point<int>(x * cellsize.x, y * cellsize.y));
}
g.lines.push_back (ln);
}
g.find_width_height ();
if (g.width > charwidth.max) charwidth.max = g.width;
if (g.height > charheight.max) charheight.max = g.height;
sum.width += g.width;
sum.height += g.height;
characters[c] = g;
}
charwidth.avg = (int) (sum.width * 1.0f / nchars + 0.5);
charheight.avg = (int)(sum.height * 1.0f / nchars + 0.5);
lift = 2 * cellsize.y;
spacing.word = 2 * spacing.ch;
calc_line_height ();
int nkerns = 0;
file >> ignore >> nkerns;
for (int i = 0; i < nkerns; ++i) {
char a, b;
int k;
file >> a >> b >> k;
kern [a][b] = k * cellsize.x;
}
}
void font::calc_line_height () {
extern int line_height;
line_height = fnt.charheight.max + fnt.headroom;
}
void font::save () {
ofstream file ((user_data_dir + fname).c_str(), ios::out);
if (!file) { dlog << "cannot save font in: " << fname << eol; return;} else { dlog << "saving font in: " << fname << eol;}
// build chars
int nc = 0;
stringstream ss1;
for (map<char, glyph>::iterator i = characters.begin(), j = characters.end(); i != j; ++i) {
glyph& g = (*i).second;
vector<line>& lines = g.lines; // poly lines
int nlines = lines.size ();
if (nlines) {
ss1 << (*i).first << ' ';
ss1 << nlines << ' ';
for (int s = 0; s < nlines; ++s) {
vector < point<int> >& points = lines[s].points;
int npts = points.size ();
ss1 << npts << ' ';
for (int m = 0; m < npts; ++m) {
point<int>& pt = points[m];
ss1 << (int) (pt.x / cellsize.x) << ' ' << (int) (pt.y / cellsize.y) << ' ';
}
}
ss1 << eol;
++nc;
}
}
// write chars
file << "name " << name << eol;
file << "copyright jagannathan_sampath_(c)_2006-2024_all_rights_reserved." << eol;
file << "num_chars " << nc << eol;
file << ss1.str();
// build kerns
int nk = 0;
stringstream ss2;
for (map<char, map<char, int> >::iterator i = kern.begin(), j = kern.end(); i != j; ++i) {
char a = (*i).first;
map<char, int>& m = (*i).second;
for (map<char, int>::iterator p = m.begin(), q = m.end(); p != q; ++p) {
char b = (*p).first;
int k = (*p).second;
if (k != 0) {
ss2 << a << ' ' << b << ' ' << k/cellsize.x << '\n';
++nk;
}
}
}
// write kerns
file << "num_kerns " << nk << eol;
file << ss2.str();
}
int font::char_width (char c) { return characters[c].width;}
int font::char_height (char c) { return characters[c].height;}
void font::draw_char (char c, int x, int y, int z) {
static const int MAX_LINE_POINTS = 32; // cant exceed or crash!
static int pts [3 * MAX_LINE_POINTS] = {0};
glyph& gl = characters[c];
vector<line>& lines = gl.lines;
glVertexPointer (3, GL_INT, 0, pts);
for (int i = 0; i < gl.nln; ++i) {
line& li = lines[i];
vector< point<int> >& points = li.points;
int q = 0, npts = li.npts;
for (int j = 0; j < npts; ++j) {
const point<int>& p = points[j];
pts[q++] = x + p.x;
pts[q++] = y + p.y;
pts[q++] = z;
}
glDrawArrays (GL_LINE_STRIP, 0, npts);
}
}
int draw_string (const string& s, int x, int y, int z) {
char prevc = ' ';
for (int p = 0, q = s.length(); p < q; ++p) {
char c = s[p];
if (c == ' ') x += fnt.spacing.word; else {
x += fnt.kern[prevc][c];
fnt.draw_char (c, x, y, z);
x += (fnt.char_width (c) + fnt.spacing.ch);
}
prevc = c;
}
return x;
}
const map<char, glyph>& font::get_chars () {
return characters;
}
void font::set_chars (const map<char, glyph>& chars) {
characters = chars;
mod = 1;
}
void get_char_width_height (const string& s, int& x, int& y) {
x = 0; y = 0;
int q = s.length ();
if (q) {
char prevc = ' ';
for (int p = 0; p < q; ++p) {
char c = s[p];
if (c == ' ')
x += fnt.spacing.word;
else
x = x + fnt.kern[prevc][c] + fnt.char_width (c) + fnt.spacing.ch;
int ch = fnt.char_height (c);
if (ch > y) y = ch;
prevc = c;
}
}
}
int get_char_width (const string& s) {
int x = 0;
int q = s.length ();
if (q) {
char prevc = ' ';
for (int p = 0; p < q; ++p) {
char c = s[p];
if (c == ' ')
x += fnt.spacing.word;
else
x = x + fnt.kern[prevc][c] + fnt.char_width (c) + fnt.spacing.ch;
prevc = c;
}
}
return x;
}
int get_char_height (const string& s) {
int h = 0;
for (int p = 0, q = s.length(); p < q; ++p) {
char c = s[p];
int ch = fnt.char_height (c);
if (ch > h) h = ch;
}
return h;
}
#ifdef __PLOTTER_FNT__
void font::plot_char (char c, int x, int y, int z) {
const glyph& gl = characters[c];
const vector<line>& lines = gl.lines;
for (unsigned int i = 0, nlines = lines.size(); i < nlines; ++i) {
const line& li = lines[i];
const vector< point<int> >& points = li.points;
unsigned int npts = points.size();
const point<int>& p0 = points[0];
hpgl << "PU " << (x + p0.x) << ',' << (y + p0.y) << ';' << eol;
hpgl << "PD;" << eol;
for (unsigned int j = 0; j < npts; ++j) {
const point<int>& p = points[j];
int xp = x + p.x, yp = y + p.y;
hpgl << "PA " << xp << ',' << yp << ';' << eol;
}
}
}
int plot_string (const string& s, int x, int y, int z) {
char prevc = ' ';
for (int p = 0, q = s.length(); p < q; ++p) {
char c = s[p];
if (c == ' ') x += fnt.spacing.word; else {
x += fnt.kern[prevc][c];
fnt.plot_char (c, x, y, z);
x += (fnt.char_width (c) + fnt.spacing.ch);
}
prevc = c;
}
return x;
}
#endif
#ifdef __SVG_FNT__
void font::write_char (char c, int x, int y, int z) {
const glyph& gl = characters[c];
const vector<line>& lines = gl.lines;
for (unsigned int i = 0, nlines = lines.size(); i < nlines; ++i) {
const line& li = lines[i];
const vector< point<int> >& points = li.points;
unsigned int npts = points.size();
svg << "<g>" << eol;
svg << "<polyline fill=\"none\" stroke=\"black\" points=\"";
for (unsigned int j = 0; j < npts; ++j) {
const point<int>& p = points[j];
glVertex3i(x + p.x, y + p.y, z);
int xp = x + p.x, yp = y - p.y;
svg << xp << "," << yp << " ";
}
svg << "\"/>" << eol;
svg << "</g>" << eol;
}
}
int write_string (const string& s, int x, int y, int z) {
char prevc = ' ';
for (int p = 0, q = s.length(); p < q; ++p) {
char c = s[p];
if (c == ' ') x += fnt.spacing.word; else {
x += fnt.kern[prevc][c];
fnt.write_char (c, x, y, z);
x += (fnt.char_width (c) + fnt.spacing.ch);
}
prevc = c;
}
return x;
}
#endif