Rev 1370 |
Rev 1713 |
Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* font.cc
* DIN Is Noise is copyright (c) 2006-2020 Jagannathan Sampath
* For more information, please visit https://dinisnoise.org/
*/
#include <fstream>
#include <sstream>
#include <cmath>
#include "font.h"
using namespace std;
extern string user_data_dir;
extern ofstream dlog;
static const char eol = '\n';
font::font (const string& filename) : modified (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
{
x_cell_size = 2;
y_cell_size = 2;
charspc = 2 * x_cell_size;
headroom = 2 * y_cell_size;
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 (modified) 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 >> nchars;
int sum_char_width, sum_char_height;
max_char_width = max_char_height = sum_char_width = sum_char_height = 0;
for (int i = 0; i < nchars; i++) {
char c; file >> c;
glyph g;
int nln; file >> nln;
for (int l = 0; l < nln; ++l) {
line ln;
int npts; file >> npts;
for (int s = 0; s < npts; ++s) {
int x, y; file >> x >> y;
ln.points.push_back (point<int>(x * x_cell_size, y * y_cell_size));
}
g.lines.push_back (ln);
}
g.find_width_height ();
if (g.width > max_char_width) max_char_width = g.width;
if (g.height > max_char_height) max_char_height = g.height;
sum_char_width += g.width;
sum_char_height += g.height;
characters[c] = g;
}
avg_char_width = (int) (sum_char_width * 1.0f / nchars + 0.5);
avg_char_height = (int)(sum_char_height * 1.0f / nchars + 0.5);
lift = 2 * y_cell_size;
wordspc = 2 * charspc;
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 * x_cell_size;
}
}
void font::calc_line_height () {
extern int line_height;
line_height = fnt.max_char_height + 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 / x_cell_size) << ' ' << (int) (pt.y / y_cell_size) << ' ';
}
}
ss1 << eol;
++nc;
}
}
// write chars
file << "name " << name << 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/x_cell_size << '\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) {
const glyph& gl = characters[c];
const vector<line>& lines = gl.lines;
static const unsigned int MAX_CHAR_POINTS = 128;
static int pdata [3 * MAX_CHAR_POINTS] = {0};
glVertexPointer (3, GL_INT, 0, pdata);
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();
if (npts < MAX_CHAR_POINTS) {
int k = 0;
for (unsigned int j = 0; j < npts; ++j) {
const point<int>& p = points[j];
pdata[k++] = x + p.x;
pdata[k++] = y + p.y;
pdata[k++] = z;
}
glDrawArrays (GL_LINE_STRIP, 0, npts);
}
}
}
const map<char, glyph>& font::get_chars () {
return characters;
}
void font::set_chars (const map<char, glyph>& chars) {
characters = chars;
modified = 1;
}
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.wordspc; else {
x += fnt.kern[prevc][c];
fnt.draw_char (c, x, y, z);
x += (fnt.char_width (c) + fnt.charspc);
}
prevc = c;
}
return x;
}
int get_char_width (const string& s) {
char prevc = ' ';
int x = 0;
for (int p = 0, q = s.length(); p < q; ++p) {
char c = s[p];
if (c == ' ')
x += fnt.wordspc;
else
x = x + fnt.kern[prevc][c] + fnt.char_width (c) + fnt.charspc;
prevc = c;
}
return x;
}
int get_max_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.wordspc; else {
x += fnt.kern[prevc][c];
fnt.plot_char (c, x, y, z);
x += (fnt.char_width (c) + fnt.charspc);
}
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.wordspc; else {
x += fnt.kern[prevc][c];
fnt.write_char (c, x, y, z);
x += (fnt.char_width (c) + fnt.charspc);
}
prevc = c;
}
return x;
}
#endif