1 /*
2 * font.cc
3 * DIN Is Noise is copyright (c) 2006-2021 Jagannathan Sampath
4 * DIN Is Noise is released under GNU Public License 2.0
5 * For more information, please visit https://dinisnoise.org/
6 */
7 
8 
9 #include <fstream>
10 #include <sstream>
11 #include <cmath>
12 #include "font.h"
13 using namespace std;
14 
15 extern string user_data_dir;
16 extern ofstream dlog;
17 static const char eol = '\n';
18 
font(const string & filename)19 font::font (const string& filename) : mod(0)
20 
21 #ifdef __SVG_FNT__
22   ,svg ((user_data_dir + "text.svg").c_str(), ios::out)
23 #endif
24 
25 #ifdef __PLOTTER_FNT__
26   ,hpgl ((user_data_dir + "text.hpgl").c_str(), ios::out)
27 #endif
28 
29 {
30   cellsize.x = 2;
31   cellsize.y = 2;
32   spacing.ch = 2 * cellsize.x;
33   headroom = 2 * cellsize.y;
34   fname = filename;
35   load (fname);
36 
37 #ifdef __SVG_FNT__
38   svg << "<?xml version=\"1.0\"?>" << eol;
39   svg << "<svg xmlns=\"https://www.w3.org/2000/svg\">" << eol;
40 #endif
41 
42 #ifdef __PLOTTER_FNT__
43 	hpgl << "IN;" << eol;
44 #endif
45 
46 }
47 
~font()48 font::~font () {
49   if (mod) save ();
50 #ifdef __SVG_FNT__
51   svg << "</svg>" << eol;
52 #endif
53 	dlog << "--- destroyed font ---" << endl;
54 }
55 
load(const string & fn)56 void font::load (const string& fn) {
57   string ffname (user_data_dir + fname);
58   ifstream file (ffname.c_str(), ios::in);
59   if (file) {
60     dlog << "<<< loading font from: " << ffname;
61     load (file);
62     dlog << ", done. >>>" << eol;
63   } else {
64     dlog << "!!! bad font file: " << ffname << " !!!" << endl;
65   }
66 }
67 
load(ifstream & file)68 void font::load (ifstream& file) {
69 
70   string ignore;
71 
72   characters.clear ();
73   kern.clear ();
74 
75   file >> ignore >> name;
76   file >> ignore >> ignore; // copyright
77   file >> ignore >> nchars;
78 
79   MAKETYPE (sumt, int, width, height)
80   sumt sum;
81 
82   charwidth.max = charheight.max = 0;
83   sum.width = sum.height = 0;
84 
85   for (int i = 0; i < nchars; i++) {
86       char c; file >> c;
87       glyph g;
88       int nln; file >> nln;
89       for (int l = 0; l < nln; ++l) {
90         line ln;
91         int npts; file >> npts;
92         for (int s = 0; s < npts; ++s) {
93           int x, y; file >> x >> y;
94           ln.points.push_back (point<int>(x * cellsize.x, y * cellsize.y));
95         }
96         g.lines.push_back (ln);
97       }
98 
99       g.find_width_height ();
100       if (g.width > charwidth.max) charwidth.max = g.width;
101       if (g.height > charheight.max) charheight.max = g.height;
102       sum.width += g.width;
103       sum.height += g.height;
104 
105       characters[c] = g;
106 
107   }
108 
109   charwidth.avg = (int) (sum.width * 1.0f / nchars + 0.5);
110   charheight.avg = (int)(sum.height * 1.0f / nchars + 0.5);
111 
112   lift = 2 * cellsize.y;
113 
114   spacing.word = 2 * spacing.ch;
115 
116 	calc_line_height ();
117 
118   int nkerns = 0;
119   file >> ignore >> nkerns;
120   for (int i = 0; i < nkerns; ++i) {
121     char a, b;
122     int k;
123     file >> a >> b >> k;
124     kern [a][b] = k * cellsize.x;
125   }
126 
127 }
128 
calc_line_height()129 void font::calc_line_height () {
130 	extern int line_height;
131 	line_height = fnt.charheight.max + fnt.headroom;
132 }
133 
save()134 void font::save () {
135 
136   ofstream file ((user_data_dir + fname).c_str(), ios::out);
137   if (!file) { dlog << "cannot save font in: " << fname << eol; return;} else { dlog << "saving font in: " << fname << eol;}
138 
139 	// build chars
140 	int nc = 0;
141   stringstream ss1;
142   for (map<char, glyph>::iterator i = characters.begin(), j = characters.end(); i != j; ++i) {
143     glyph& g = (*i).second;
144     vector<line>& lines = g.lines; // poly lines
145     int nlines = lines.size ();
146     if (nlines) {
147       ss1 << (*i).first << ' ';
148       ss1 << nlines << ' ';
149       for (int s = 0; s < nlines; ++s) {
150         vector < point<int> >& points = lines[s].points;
151         int npts = points.size ();
152         ss1 << npts << ' ';
153         for (int m = 0; m < npts; ++m) {
154           point<int>& pt = points[m];
155           ss1 << (int) (pt.x / cellsize.x) << ' ' << (int) (pt.y / cellsize.y) << ' ';
156         }
157       }
158       ss1 << eol;
159       ++nc;
160     }
161   }
162 
163 	// write chars
164   file << "name " << name << eol;
165   file << "copyright jagannathan_sampath_(c)_2006-2021_all_rights_reserved." << eol;
166   file << "num_chars " << nc << eol;
167   file << ss1.str();
168 
169 	// build kerns
170 	int nk = 0;
171   stringstream ss2;
172   for (map<char, map<char, int> >::iterator i = kern.begin(), j = kern.end(); i != j; ++i) {
173     char a = (*i).first;
174     map<char, int>& m = (*i).second;
175     for (map<char, int>::iterator p = m.begin(), q = m.end(); p != q; ++p) {
176       char b = (*p).first;
177       int k = (*p).second;
178       if (k != 0) {
179         ss2 << a << ' ' << b << ' ' << k/cellsize.x << '\n';
180         ++nk;
181       }
182     }
183   }
184 
185 	// write kerns
186   file << "num_kerns " << nk << eol;
187   file << ss2.str();
188 
189 }
190 
char_width(char c)191 int font::char_width (char c) { return characters[c].width;}
char_height(char c)192 int font::char_height (char c) { return characters[c].height;}
193 
draw_char(char c,int x,int y,int z)194 void font::draw_char (char c, int x, int y, int z) {
195   const glyph& gl = characters[c];
196   const vector<line>& lines = gl.lines;
197   static const unsigned int MAX_LINE_POINTS = 128; // cant exceed or crash!
198   static int pts [3 * MAX_LINE_POINTS] = {0};
199   glVertexPointer (3, GL_INT, 0, pts);
200   for (unsigned int i = 0, nlines = lines.size(); i < nlines; ++i) {
201     const line& li = lines[i];
202     const vector< point<int> >& points = li.points;
203     unsigned int npts = points.size();
204     int k = 0;
205     for (unsigned int j = 0; j < npts; ++j) {
206       const point<int>& p = points[j];
207       pts[k++] = x + p.x;
208       pts[k++] = y + p.y;
209       pts[k++] = z;
210     }
211     glDrawArrays (GL_LINE_STRIP, 0, npts);
212   }
213 }
214 
get_chars()215 const map<char, glyph>& font::get_chars () {
216   return characters;
217 }
218 
set_chars(const map<char,glyph> & chars)219 void font::set_chars (const map<char, glyph>& chars) {
220   characters = chars;
221   mod = 1;
222 }
223 
draw_string(const string & s,int x,int y,int z)224 int draw_string (const string& s, int x, int y, int z) {
225   char prevc = ' ';
226   for (int p = 0, q = s.length(); p < q; ++p) {
227       char c = s[p];
228       if (c == ' ') x += fnt.spacing.word; else {
229         x += fnt.kern[prevc][c];
230         fnt.draw_char (c, x, y, z);
231         x += (fnt.char_width (c) + fnt.spacing.ch);
232       }
233       prevc = c;
234   }
235   return x;
236 }
237 
get_char_width(const string & s)238 int get_char_width (const string& s) {
239   char prevc = ' ';
240   int x = 0;
241   for (int p = 0, q = s.length(); p < q; ++p) {
242     char c = s[p];
243     if (c == ' ')
244       x += fnt.spacing.word;
245     else
246       x = x + fnt.kern[prevc][c] + fnt.char_width (c) + fnt.spacing.ch;
247     prevc = c;
248   }
249   return x;
250 }
251 
get_char_height(const string & s)252 int get_char_height (const string& s) {
253   int h = 0;
254   for (int p = 0, q = s.length(); p < q; ++p) {
255     char c = s[p];
256     int ch = fnt.char_height (c);
257     if (ch > h) h = ch;
258   }
259   return h;
260 }
261 
262 #ifdef __PLOTTER_FNT__
263 
plot_char(char c,int x,int y,int z)264 void font::plot_char (char c, int x, int y, int z) {
265   const glyph& gl = characters[c];
266   const vector<line>& lines = gl.lines;
267   for (unsigned int i = 0, nlines = lines.size(); i < nlines; ++i) {
268     const line& li = lines[i];
269     const vector< point<int> >& points = li.points;
270     unsigned int npts = points.size();
271 		const point<int>& p0 = points[0];
272 		hpgl << "PU " << (x + p0.x) << ',' << (y + p0.y) << ';' << eol;
273 		hpgl << "PD;" << eol;
274     for (unsigned int j = 0; j < npts; ++j) {
275       const point<int>& p = points[j];
276       int xp = x + p.x, yp = y + p.y;
277       hpgl << "PA " << xp << ',' << yp << ';' << eol;
278     }
279   }
280 }
281 
plot_string(const string & s,int x,int y,int z)282 int plot_string (const string& s, int x, int y, int z) {
283   char prevc = ' ';
284   for (int p = 0, q = s.length(); p < q; ++p) {
285       char c = s[p];
286       if (c == ' ') x += fnt.spacing.word; else {
287         x += fnt.kern[prevc][c];
288         fnt.plot_char (c, x, y, z);
289         x += (fnt.char_width (c) + fnt.spacing.ch);
290       }
291       prevc = c;
292   }
293   return x;
294 }
295 #endif
296 
297 #ifdef __SVG_FNT__
write_char(char c,int x,int y,int z)298 void font::write_char (char c, int x, int y, int z) {
299   const glyph& gl = characters[c];
300   const vector<line>& lines = gl.lines;
301   for (unsigned int i = 0, nlines = lines.size(); i < nlines; ++i) {
302     const line& li = lines[i];
303     const vector< point<int> >& points = li.points;
304     unsigned int npts = points.size();
305     svg << "<g>" << eol;
306     svg << "<polyline fill=\"none\" stroke=\"black\" points=\"";
307       for (unsigned int j = 0; j < npts; ++j) {
308         const point<int>& p = points[j];
309         glVertex3i(x + p.x, y + p.y, z);
310         int xp = x + p.x, yp = y - p.y;
311         svg << xp << "," << yp << " ";
312       }
313     svg << "\"/>" << eol;
314     svg << "</g>" << eol;
315   }
316 }
317 
write_string(const string & s,int x,int y,int z)318 int write_string (const string& s, int x, int y, int z) {
319   char prevc = ' ';
320   for (int p = 0, q = s.length(); p < q; ++p) {
321     char c = s[p];
322     if (c == ' ') x += fnt.spacing.word; else {
323       x += fnt.kern[prevc][c];
324       fnt.write_char (c, x, y, z);
325       x += (fnt.char_width (c) + fnt.spacing.ch);
326     }
327     prevc = c;
328   }
329   return x;
330 }
331 #endif
332