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