1 /*
2     Gri - A language for scientific graphics programming
3     Copyright (C) 2008 Daniel Kelley
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; version 3 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 
20 //#define DEBUG 1
21 #include <vector>
22 #include <string>
23 #include <stack>
24 #include <math.h>
25 #include <stdio.h>
26 #include <ctype.h>
27 #include "gr.hh"
28 #include "extern.hh"
29 #include "GriPath.hh"
30 #include "superus.hh"
31 #include "defaults.hh"
32 #define NCODES 100
33 // symbol_code (p 604 new PostScript book): (1) define name, (2) Postscript
34 // code, (3) symbol-font crossref code (used for estimage of symbol
35 // size, by index_for_math_symbol() ... a bad idea, really), and (4)
36 // SVG code [broken; need to transcribe the codes from
37 // http://www.w3.org/TR/html4/sgml/entities.html#h-24.3 one by one]
38 static char *symbol_code[NCODES][4] = {
39 	// name, code in Table E.11, p604 new ps book, char-equivalent}
40 	//
41 	// Organization of list below is as in the tables in
42 	// Lamport's Latex book
43 	//
44 	// Table 3.3 Greek Letters
45 	// lowercase
46 	{(char *)"alpha", (char *)"\\141", (char *)"a", (char *)"&#945;"},
47 	{(char *)"beta", (char *)"\\142", (char *)"b", (char *)"&#946;"},
48 	{(char *)"gamma", (char *)"\\147", (char *)"g", (char *)"&#947;"},
49 	{(char *)"delta", (char *)"\\144", (char *)"d", (char *)"&#948;"},
50 	{(char *)"epsilon", (char *)"\\145", (char *)"e", (char *)"&#949;"},
51         // varepsilon
52 	{(char *)"zeta", (char *)"\\172", (char *)"z", (char *)"&#952;"},
53 	{(char *)"eta", (char *)"\\150", (char *)"h", (char *)"&#951;"},
54 	{(char *)"theta", (char *)"\\161", (char *)"q", (char *)"&#952;"},
55 	{(char *)"vartheta", (char *)"\\112", (char *)"q", (char *)"&#977;"},
56 	{(char *)"iota", (char *)"\\151", (char *)"i", (char *)"&#9531;"},
57 	{(char *)"kappa", (char *)"\\153", (char *)"k", (char *)"&#954;"},
58 	{(char *)"lambda", (char *)"\\154", (char *)"l", (char *)"&#955;"},
59 	{(char *)"mu", (char *)"\\155", (char *)"m", (char *)"&#956;"},
60 	{(char *)"nu", (char *)"\\156", (char *)"n", (char *)"&#957;"},
61 	{(char *)"xi", (char *)"\\170", (char *)"x", (char *)"&#958;"},
62         // o [not needed, really]
63 	{(char *)"pi", (char *)"\\160", (char *)"p", (char *)"&#960;"},
64 	{(char *)"varpi", (char *)"\\166", (char *)"p", (char *)"&#960;"},
65 	{(char *)"rho", (char *)"\\162", (char *)"r", (char *)"&#961;"},
66 	{(char *)"sigma", (char *)"\\163", (char *)"s", (char *)"&#963;"}, // or 962
67 	{(char *)"varsigma", (char *)"\\126", (char *)"s", (char *)"&#962;"},
68 	{(char *)"tau", (char *)"\\164", (char *)"t", (char *)"&#964;"},
69 	{(char *)"upsilon", (char *)"\\165", (char *)"u", (char *)"&#965;"},
70 	{(char *)"psi", (char *)"\\171", (char *)"y", (char *)"&#968;"},
71 	{(char *)"chi", (char *)"\\143", (char *)"c", (char *)"&#967;"},
72 	{(char *)"phi", (char *)"\\146", (char *)"f", (char *)"&#966;"},
73 	{(char *)"varphi", (char *)"\\152", (char *)"f", (char *)"&#966;"}, //?
74 	{(char *)"omega", (char *)"\\167", (char *)"w", (char *)"&#969;"},
75         //
76         // Uppercase
77 	{(char *)"Gamma", (char *)"\\107", (char *)"G", (char *)"&#915;"},
78 	{(char *)"Delta", (char *)"\\104", (char *)"D", (char *)"&#916;"},
79 	{(char *)"Theta", (char *)"\\121", (char *)"Q", (char *)"&#920;"},
80 	{(char *)"Lambda", (char *)"\\114", (char *)"L", (char *)"&#923;"},
81 	{(char *)"Xi", (char *)"\\130", (char *)"X", (char *)"&#926;"},
82 	{(char *)"Pi", (char *)"\\120", (char *)"P", (char *)"&#928;"},
83 	{(char *)"Sigma", (char *)"\\123", (char *)"S", (char *)"&#931;"},
84 	{(char *)"Upsilon", (char *)"\\241", (char *)"Y", (char *)"&#933;"},
85 	{(char *)"Phi", (char *)"\\106", (char *)"F", (char *)"&#934;"},
86 	{(char *)"Psi", (char *)"\\131", (char *)"Y", (char *)"&#936;"},
87 	{(char *)"Omega", (char *)"\\127", (char *)"W", (char *)"&#937;"},
88         // Table 3.4: Binary Operation Symbols
89 	{(char *)"pm", (char *)"\\261", (char *)"+", (char *)"&#177;"}, // guess that size is same as +
90         // mp
91 	{(char *)"times", (char *)"\\264", (char *)"x", (char *)"&#215;"}, // guess that size is same as x
92 	{(char *)"div", (char *)"\\270", (char *)"x", (char *)"&#247;"}, // guess that size is same as x
93 	{(char *)"ast", (char *)"\\052", (char *)"*", (char *)"&#8727;"}, // star
94 	{(char *)"circ", (char *)"\\260", (char *)".", (char *)"&#176;"},
95 	{(char *)"bullet", (char *)"\\267", (char *)"*", (char *)"&#8226;"}, // guess that size is same as *
96 	{(char *)"cdot", (char *)"\\327", (char *)",", (char *)"&#183;"}, // ? Georgian comma
97         // cap
98         // cup
99         // uplus
100         // sqcap
101         // sqcup
102         // vee
103 	{(char *)"wedge", (char *)"\\331", (char *)"M", (char *)"&#8743;"}, // guess that size is same as M
104         // setminus
105         // wr
106         // diamond
107         // bigtriangleup
108         // bigtriangledown
109         // triangleleft
110         // triangleright
111         // lhd
112         // rhd
113         // unlhd
114         // unrhd
115 	{(char *)"oplus", (char *)"\\305", (char *)"o", (char *)"&#8853;"},
116         // ominus
117 	{(char *)"otimes", (char *)"\\304", (char *)"o", (char *)"&#8855;"},
118         // oslash
119         // odot
120         // bigcirc
121         // dagger
122         // ddagger
123         // amalg
124         //
125         // Table 3.5: Relation Symbols
126 	{(char *)"leq", (char *)"\\243", (char *)"<", (char *)"&#8804;"}, // guess that size is same as <
127         // prec
128         // preceq
129         // ll
130 	{(char *)"subset", (char *)"\\314", (char *)"<", (char *)"&#8834;"}, // guess that size is same as <
131 	{(char *)"subseteq", (char *)"\\315", (char *)"<", (char *)"&#8838;"}, // guess that size is same as <
132         // sqsubset
133         // sqsubseteq
134         // MOVE 'in' to after 'infty'
135         // vdash
136 	{(char *)"geq", (char *)"\\263", (char *)">", (char *)"&#8805;"},
137         // succ
138         // succeq
139         // gg
140 	{(char *)"supset", (char *)"\\311", (char *)">", (char *)"&#8835;"},
141 	{(char *)"supseteq", (char *)"\\312", (char *)">", (char *)"&#8839;"},
142         // sqsupset
143         // sqsupseteq
144         // ni
145         // dashv
146 	{(char *)"equiv", (char *)"\\272", (char *)"=", (char *)"&#8801;"},
147 	{(char *)"sim", (char *)"\\176", (char *)"~", (char *)"&#8764;"},
148         // simeq
149         // asymp
150 	{(char *)"approx", (char *)"\\273", (char *)"~", (char *)"&#8774;"}, // ?
151 	{(char *)"cong", (char *)"\\100", (char *)"=", (char *)"&#8764;"}, // ?
152 	{(char *)"neq", (char *)"\\271", (char *)"=", (char *)"&#8800;"},
153         // doteq
154 	{(char *)"propto", (char *)"\\265", (char *)"~", (char *)"&#8733;"},
155         // models
156 	{(char *)"perp", (char *)"\\136", (char *)"M", (char *)"&#8869;"},
157         // mid
158         // parallel
159         // bowtie
160         // join
161         // smile
162         // frown
163         //
164         // Table 3.6: Arrow Symbols
165 	{(char *)"leftarrow", (char *)"\\254", (char *)"M", (char *)"&#8592;"},
166 	{(char *)"Leftarrow", (char *)"\\334", (char *)"M", (char *)"&#8656;"},
167 	{(char *)"rightarrow", (char *)"\\256", (char *)"M", (char *)"&#8594;"},
168 	{(char *)"Rightarrow", (char *)"\\336", (char *)"M", (char *)"&#8658;"},
169 	{(char *)"leftrightarrow", (char *)"\\253", (char *)"M", (char *)"&#8596;"},
170 	{(char *)"Leftrightarrow", (char *)"\\333", (char *)"M", (char *)"&#8660;"},
171         // mapsto
172         // hookleftarrow
173         // leftharpoonup
174         // leftharpoondown
175         // rightleftharpoons
176         // longleftarrow
177         // Longleftarrow
178         // longrightarrow
179         // Longrightarrow
180         // longleftrightarrow
181         // Longleftrightarrow
182         // longmapsto
183         // hookrightarrow
184         // rightharpoonup
185         // rightharpoon down
186         // leadsto
187 	{(char *)"uparrow", (char *)"\\255", (char *)"|", (char *)"&#8593;"},
188 	{(char *)"Uparrow", (char *)"\\335", (char *)"|", (char *)"&#8657;"},
189 	{(char *)"downarrow", (char *)"\\257", (char *)"|", (char *)"&#8595;"},
190 	{(char *)"Downarrow", (char *)"\\337", (char *)"|", (char *)"&#8659;"},
191         // updownarrow
192         // Updownarrow
193         // neararrow
194         // searrow
195         // swarrow
196         // nwarrow
197         //
198         // Table 3.7: Miscellaneous Symbols
199 	{(char *)"aleph", (char *)"\\300", (char *)"M", (char *)"&#8501;"}, // ?
200         // hbar
201         // imath
202         // jmath
203         // ell
204 	{(char *)"wp", (char *)"\\303", (char *)"M", (char *)"&#;8476"}, // BUG: figure out what this is
205 	{(char *)"Re", (char *)"\\302", (char *)"R", (char *)"&#;8476"},
206 	{(char *)"Im", (char *)"\\301", (char *)"M", (char *)"&#8465;"},
207         // mho
208 	{(char *)"prime", (char *)"\\242", (char *)"'", (char *)"&#8242;"},
209 	{(char *)"emptyset", (char *)"\\306", (char *)"M", (char *)"&#;8709"},
210 	{(char *)"nabla", (char *)"\\321", (char *)"M", (char *)"&#8711;"},
211 	{(char *)"surd", (char *)"\\326", (char *)"M", (char *)"&#8730;"},
212 	{(char *)"sqrt", (char *)"\\326", (char *)"M", (char *)"&#8730;"},
213         // top
214 	{(char *)"bot", (char *)"\\136", (char *)"M", (char *)"&#;8730"}, // BUG: no idea what this is
215         // |
216 	{(char *)"angle", (char *)"\\320", (char *)"M", (char *)"&#8736;"},
217 	{(char *)"forall", (char *)"\\042", (char *)"M", (char *)"&#8704;"},
218 	{(char *)"exists", (char *)"\\044", (char *)"M", (char *)"&#8707;"},
219 	{(char *)"neg", (char *)"\\330", (char *)"M", (char *)"&#172;"},
220         // flat
221         // natural
222         // sharp
223         // backslash
224 	{(char *)"partial", (char *)"\\266", (char *)"d", (char *)"&#8706;"},
225 	{(char *)"infty", (char *)"\\245", (char *)"M", (char *)"&#8734;"},
226         // Interpose 'int' and 'in' here to avoid clashes with 'infty'
227 	{(char *)"int", (char *)"\\362", (char *)"M", (char *)"&#8747;"},
228 	{(char *)"in", (char *)"\\316", (char *)"<", (char *)"&#8712;"},
229         // Box
230         // Diamond
231         // triangle
232 	{(char *)"clubsuit", (char *)"\\247", (char *)"M", (char *)"&#9827;"},
233 	{(char *)"diamondsuit", (char *)"\\340", (char *)"M", (char *)"&#9830;"},
234         // heartsuit
235 	{(char *)"spadesuit", (char *)"\\252", (char *)"M", (char *)"&#9829;"},
236         //
237         // Table 3.8 Variable-sized symbols
238 	{(char *)"sum", (char *)"\\345", (char *)"M", (char *)"&#8721;"},
239 	{(char *)"prod", (char *)"\\325", (char *)"M", (char *)"&#8719;"},
240         // int -- moved up to avoid name clashes
241         // oint
242         // bigcap
243         // bigcup
244         // bigsqcup
245         // bigvee
246         // bigwedge
247         // bigodot
248         // bigotimes
249         // bigoplus
250         // biguplus
251         //
252         // Table 3.10
253 	{(char *)"lfloor", (char *)"\\353", (char *)"M", (char *)"&#8970;"},
254 	{(char *)"lceil", (char *)"\\351", (char *)"M", (char *)"&#8968;"},
255 	{(char *)"langle", (char *)"\\341", (char *)"<", (char *)"&#9001;"},
256 	{(char *)"rfloor", (char *)"\\373", (char *)"M", (char *)"&#8971;"},
257 	{(char *)"rceil", (char *)"\\371", (char *)"M", (char *)"&#8969;"},
258 	{(char *)"rangle", (char *)"\\361", (char *)">", (char *)"&#9002;"}
259         // backslash SEE ABOVE
260         // \|
261         // uparrow SEE ABOVE
262         // downarrow SEE ABOVE
263         // updownarrow SEE ABOVE
264         // Uparrow SEE ABOVE
265         // Downarrow SEE ABOVE
266         // Updownarrow SEE ABOVE
267 };
268 double gr_current_descender(void);
part_string(const std::string & s)269 std::vector<std::string> part_string(const std::string &s)
270 {
271 	using namespace std;
272 	string::size_type i, lasti = 0, len = s.size();
273 	bool inmath = false;
274 	vector<string> parts;
275 	for (i = 0; i < len; i++) {
276 		if (s[i] == '$') {
277 			// \$ escapes but not \\$
278 			if (i > 0 && s[i-1] == '\\') {
279 				if (!(i > 1 && s[i-2] == '\\'))
280 					continue;
281 			}
282 			if (inmath) i++; // keep the $ at the end
283 			parts.push_back(s.substr(lasti, i-lasti));
284 			inmath = !inmath;
285 			lasti = i;
286 		}
287 	}
288 	parts.push_back(s.substr(lasti, len-lasti));
289 #if 0
290 	printf("\n\"%s\"\n", s.c_str());
291 	for (unsigned int l = 0; l < parts.size(); l++)
292 		printf("    \"%s\"\n", parts[l].c_str());
293 #endif
294 	return parts;
295 }
296 
297 const char* gr_fontname_from_id(int id);
298 
299 #define	default_fontID		gr_font_Helvetica
300 #define default_encoding        font_encoding_isolatin1
301 #define	default_fontsize_pt	12.0
302 
303 static gr_font  CurrentFont = {
304 	default_fontID,
305 	default_encoding,
306 	default_fontsize_pt
307 };
308 
309 // Q: should this be done in Moveup() routine? [then what about $N$N though]
310 #define START_NEW_TEXT {\
311     if (_output_file_type == postscript && _grWritePS) { \
312 	fprintf(_grPS, "(");\
313 	check_psfile();\
314     }\
315 }
316 
317 #define STOP_OLD_TEXT {\
318     if (_output_file_type == postscript && _grWritePS) {\
319         fprintf(_grPS, ") sh\n");\
320         check_psfile();\
321     }\
322 }
323 
324 
325 enum position {Superscript, Subscript, Inline};	// Baseline indicator
326 static std::stack<position> pstack;   // baseline position stack
327 
328 // Use spacing patterned on results of a TeX example (using Large font). All
329 // quantities are multiples of Mspace.
330 static const double SubSize = 0.75; // relative height of subscripts = 6/8
331 static const double SuperSize = 0.75; // relative height of superscripts = 6/8
332 static const double SuperMoveUp =0.625;	// Move up for super = 5/8
333 static const double SubMoveDown =0.375;	// Move down for sub = 3/8
334 
335 
336 
337 #define PS_showpage     "grestore\nshowpage\n"
338 #define PS_stroke       "s\n"
339 
340 extern FILE    *_grPS;
341 extern FILE    *_grSVG;
342 extern bool     _grNeedBegin;
343 extern bool     _grPathExists;
344 extern bool     _grWritePS;
345 
346 static void     gr_drawstring(const char *s);
347 static void     gr_drawchar_svg(char c, double xcm, double ycm, gr_fontID font_id);
348 static void     gr_drawsymbol_svg(int index, double xcm, double ycm, gr_fontID font_id);
349 static void     gr_drawstring_svg(const char *s, double xcm, double ycm, double angle);
350 //static int      index_for_math_symbol(const char *s);	// base routine
351 static double   gr_charwidth_cm(int c, int font, double fontsize_pt);
352 static void     gr_DrawChar(const char *c);
353 static void     gr_setfont_fontsize(gr_fontID newID, bool force = false);
354 static void     pstack_erase();
355 static void     MoveDown(void);
356 static void     MoveUp(void);
357 static void     MoveUp_svg(double *xcm, double *ycm);
358 static void     MoveDn_svg(double *xcm, double *ycm);
359 static void     MoveHorizontally(double em_distance);
360 static int      symbol_in_math(const char *sPtr, int *inc);
361 
362 gr_font_info font_list[] =
363 {
364 	{gr_font_Courier, (char *)"Courier"},
365 	{gr_font_CourierOblique, (char *)"Courier-Oblique"},
366 	{gr_font_CourierBold, (char *)"Courier-Bold"},
367 	{gr_font_CourierBoldOblique, (char *)"Courier-BoldOblique"},
368 	{gr_font_Helvetica, (char *)"Helvetica"},
369 	{gr_font_HelveticaBold, (char *)"Helvetica-Bold"},
370 	{gr_font_HelveticaOblique, (char *)"Helvetica-Oblique"},
371 	{gr_font_PalatinoRoman, (char *)"Palatino-Roman"},
372 	{gr_font_PalatinoItalic, (char *)"Palatino-Italic"},
373 	{gr_font_PalatinoBold, (char *)"Palatino-Bold"},
374 	{gr_font_PalatinoBoldItalic, (char *)"Palatino-BoldItalic"},
375 	{gr_font_Symbol, (char *)"Symbol"},
376 	{gr_font_TimesRoman, (char *)"Times-Roman"},
377 	{gr_font_TimesItalic, (char *)"Times-Italic"},
378 	{gr_font_TimesBold, (char *)"Times-Bold"},
379 	{gr_font_TimesBoldItalic, (char *)"Times-BoldItalic"},
380 	{gr_font_Century, (char *)"Century"},
381 	{gr_font_end_of_list, (char *)""}
382 };
383 
384 // Draw text at specified location.
385 void
gr_show_at(char * s,double xcm,double ycm,gr_textStyle style,double angle_deg)386 gr_show_at(/*const*/ char *s, double xcm, double ycm, gr_textStyle style, double angle_deg)
387 {
388 #ifdef DEBUG
389         printf("DEBUG %s:%d gr_show_at(\"%s\",xcm,ycm,style,%f)\n",__FILE__,__LINE__,s,angle_deg);
390 #endif
391 	if (0.0 == gr_currentfontsize_pt() || !strlen(s)) {
392 		return;
393 	}
394 	double          oldfontsize_pt = gr_currentfontsize_pt();
395 	gr_fontID       oldfontID = gr_currentfont();
396 	double          width_cm, ascent_cm, descent_cm;
397 	rectangle box;
398 	extern bool _warn_offpage;
399 	if (_warn_offpage
400 	    && ( xcm < OFFPAGE_LEFT
401 		 || xcm > OFFPAGE_RIGHT
402 		 || ycm < OFFPAGE_BOTTOM
403 		 || ycm > OFFPAGE_TOP)) {
404 		warning("Drawing text at a location that is offpage.");
405 	}
406 	const char *fn_svg = NULL;
407 	double r, g, b;
408 	_griState.color_text().getRGB(&r, &g, &b);
409 	switch (_output_file_type) {
410 	case  postscript:
411 		break;
412 	case svg:
413 		switch (CurrentFont.id) {
414 		case gr_font_Courier:            fn_svg = "Courier";            break;
415 		case gr_font_CourierOblique:     fn_svg = "Courier-Italic";     break;
416 		case gr_font_CourierBold:        fn_svg = "Courier-Bold";       break;
417 		case gr_font_CourierBoldOblique: fn_svg = "Courier-BoldItalic"; break;
418 		case gr_font_Helvetica:          fn_svg = "Helvetica";          break;
419 		case gr_font_HelveticaOblique:   fn_svg = "Helvetica-Italic";   break;
420 		case gr_font_HelveticaBold:      fn_svg = "Helvetica-Bold";     break;
421 		case gr_font_PalatinoRoman:
422 		case gr_font_PalatinoItalic:
423 		case gr_font_PalatinoBold:
424 		case gr_font_PalatinoBoldItalic:
425 			fn_svg = "Times";
426 			warning("SVG cannot handle Palatino font yet");
427 			break;
428 		case gr_font_Symbol:               fn_svg = "Symbol";               break;
429 		case gr_font_TimesRoman:           fn_svg = "Times";                break;
430 		case gr_font_TimesItalic:          fn_svg = "Times-Italic";         break;
431 		case gr_font_TimesBold:            fn_svg = "Times-Bold";           break;
432 		case gr_font_TimesBoldItalic:      fn_svg = "Times-BoldItalic";     break;
433 		case gr_font_Century:     	   fn_svg = "Century";  	    break;
434 		default:
435 			fn_svg = "Times";
436 			warning("SVG defaulting to Times font");
437 			break;
438 		}
439 		break;
440 	case  gif:
441 		fprintf(stderr, "INTERNAL error at %s:%d -- nothing known for GIF\n\n", __FILE__, __LINE__);
442 		exit(99);
443 		break;
444 	}
445 
446 //	if (_output_file_type == svg) {
447 //		fprintf(stderr, "%s:%d approximating drawing of '%s' NOTE: subscripts, etc won't work\n", __FILE__, __LINE__, s);
448 //	}
449 
450 	void set_ps_color(char what);
451 	set_ps_color('t');
452 	gr_setfontsize_pt(oldfontsize_pt);
453 	gr_setfont(oldfontID);
454 	gr_stringwidth(s, &width_cm, &ascent_cm, &descent_cm);
455 	switch (style) {
456 	case TEXT_LJUST:
457 		gr_moveto_cm(xcm, ycm);
458 		if (_output_file_type == postscript) {
459 			if (_grWritePS) {
460 				if (fabs(angle_deg) > 0.1)
461 					fprintf(_grPS, "%.2f rotate ", angle_deg);
462 				gr_drawstring(s);
463 			}
464 		} else if (_output_file_type == svg) {
465 			gr_drawstring_svg(s, xcm, ycm, angle_deg);
466 		} else {
467 			fprintf(stderr, "%s:%d unknown file output type\n",__FILE__,__LINE__);
468 		}
469 		// This box not tested specifically
470 		box.set(0, -descent_cm, width_cm, ascent_cm);
471 		box.rotate(angle_deg);
472 		box.shift_x(xcm);
473 		box.shift_y(ycm);
474 		break;
475 	case TEXT_RJUST:
476 		if (_output_file_type == postscript) {
477 			if (_grWritePS) {
478 				fprintf(_grPS, "%.1f %.1f m ",
479 					PT_PER_CM * (xcm - width_cm * cos(angle_deg / DEG_PER_RAD)),
480 					PT_PER_CM * (ycm - width_cm * sin(angle_deg / DEG_PER_RAD)));
481 				if (fabs(angle_deg) > 0.1)
482 					fprintf(_grPS, "%.2f rotate ", angle_deg);
483 				gr_drawstring(s);
484 			}
485 		} else if (_output_file_type == svg) {
486 			if (_grWritePS) {
487 				gr_drawstring_svg(s,
488 						  xcm - width_cm * cos(angle_deg / DEG_PER_RAD),
489 						  ycm - width_cm * sin(angle_deg / DEG_PER_RAD),
490 						  angle_deg);
491 			}
492 		} else {
493 			fprintf(stderr, "%s:%d unknown file output type\n",__FILE__,__LINE__);
494 		}
495 
496 		// This box not tested specifically
497 		box.set(-width_cm, -descent_cm, 0.0, ascent_cm);
498 		box.rotate(angle_deg);
499 		box.shift_x(xcm);
500 		box.shift_y(ycm);
501 		break;
502 	case TEXT_CENTERED:
503 		if (_output_file_type == postscript) {
504 			if (_grWritePS) {
505 #ifdef DEBUG
506 			        fprintf(_grPS, "%% DEBUG %s:%d '%s' at angle %f\n",__FILE__,__LINE__,s,angle_deg);
507 #endif
508 				fprintf(_grPS, "%.1f %.1f m ",
509 					PT_PER_CM * (xcm - 0.5 * width_cm * cos(angle_deg / DEG_PER_RAD)),
510 					PT_PER_CM * (ycm - 0.5 * width_cm * sin(angle_deg / DEG_PER_RAD)));
511 				if (fabs(angle_deg) > 0.1)
512 					fprintf(_grPS, "%.2f rotate ", angle_deg);
513 				gr_drawstring(s);
514 			}
515 		} else if (_output_file_type == svg) {
516 			if (_grWritePS) {
517 				gr_drawstring_svg(s,
518 						  xcm - 0.5 * width_cm * cos(angle_deg / DEG_PER_RAD),
519 						  ycm - 0.5 * width_cm * sin(angle_deg / DEG_PER_RAD),
520 						  angle_deg);
521 			}
522 		} else {
523 			fprintf(stderr, "%s:%d unknown file output type\n",__FILE__,__LINE__);
524 		}
525 		box.set(-width_cm/2, -descent_cm, width_cm/2, ascent_cm);
526 		box.rotate(angle_deg);
527 		box.shift_x(xcm);
528 		box.shift_y(ycm);
529 		break;
530 	default:
531 		warning("gr_show_at type is UNKNOWN\n");
532 	}
533 	switch (_output_file_type) {
534 	case  postscript:
535 		if (_grWritePS) {
536 			if (fabs(angle_deg) > 0.1)
537 				fprintf(_grPS, "%.2f rotate ", -angle_deg);
538 			check_psfile();
539 			//fprintf(_grPS, "%% gr_show_at() END\n");
540 		}
541 		break;
542 	case svg:
543 		//fprintf(_grSVG, "</text>\n");
544 		break;
545 	case gif:
546 		fprintf(stderr, "INTERNAL error at %s:%d -- nothing known for GIF\n\n", __FILE__, __LINE__);
547 		exit(99);
548 		break;
549 	default:
550 		fprintf(stderr, "%s:%d unknown file output type\n",__FILE__,__LINE__);
551 		break;		// BUG: should check filetype here
552 	}
553 	// Update bounding box
554 	bounding_box_update(box);
555 	_drawingstarted = true;
556 }
557 
558 // gr_drawstring() -- draw string, including font changes &super/subscripts
559 static void
gr_drawstring(const char * s)560 gr_drawstring(const char *s)
561 {
562         char slast = '\0';
563 	int             slen = strlen(s);
564 	bool            inmath = false;
565 	gr_fontID       original_font = gr_currentfont();
566 	gr_fontID       current_font = original_font;
567 	gr_fontID       slant_font = original_font;	// prevent compiler warning
568 	double          original_fontsize = gr_currentfontsize_pt();
569 	bool            know_slant_font = false;
570 #ifdef DEBUG
571 	printf("DEBUG %s:%d gr_drawstring(\"%s\")\n",__FILE__,__LINE__,s);
572 #endif
573 	if (slen <= 0)
574 		return;
575 	if (0.0 == gr_currentfontsize_pt())
576 		return;
577 	// Figure out slant font, if there is an appropriate one
578 	switch (original_font) {
579 	case gr_font_TimesRoman:
580 		slant_font = gr_font_TimesItalic;
581 		know_slant_font = true;
582 		break;
583 	case gr_font_TimesBold:
584 		slant_font = gr_font_TimesBoldItalic;
585 		know_slant_font = true;
586 		break;
587 	case gr_font_Helvetica:
588 		slant_font = gr_font_HelveticaOblique;
589 		know_slant_font = true;
590 		break;
591 	case gr_font_HelveticaBold:
592 		slant_font = gr_font_HelveticaBoldOblique;
593 		know_slant_font = true;
594 		break;
595 	case gr_font_Courier:
596 		slant_font = gr_font_CourierOblique;
597 		know_slant_font = true;
598 		break;
599 	case gr_font_CourierBold:
600 		slant_font = gr_font_CourierBoldOblique;
601 		know_slant_font = true;
602 		break;
603 	case gr_font_PalatinoRoman:
604 		slant_font = gr_font_PalatinoItalic;
605 		know_slant_font = true;
606 		break;
607 	case gr_font_PalatinoBold:
608 		slant_font = gr_font_PalatinoBoldItalic;
609 		know_slant_font = true;
610 		break;
611         case gr_font_Century:
612                 slant_font = gr_font_TimesItalic; // BUG: should be Century Italic
613 		know_slant_font = true;
614 		break;
615 	default:
616 		know_slant_font = false;
617 	}
618 	// Scan through whole string.
619 	START_NEW_TEXT;
620 	while (*s != '\0') {
621 #ifdef DEBUG
622 //		printf("DEBUG(%s:%d) *s= '%c'\n",__FILE__,__LINE__,*s);
623 #endif
624 		if (*s == '-' && CurrentFont.encoding == font_encoding_isolatin1) {
625 			// Use a different character to avoid looking like underscore.
626 			if (_grWritePS) {
627 				STOP_OLD_TEXT;
628 				fprintf(_grPS, "(\\255) sh\n");	// endash
629 				check_psfile();
630 				START_NEW_TEXT;
631 			}
632 			s++;
633 			continue;
634 		}
635 		// Figure out whether entering or leaving math mode; enter/leave if
636 		// find $ without preceeding \.  Thus a$b$ has math but a\$b\$ does
637 		// not.
638 		if (*s == '$' && slast != '\\') {
639 			if (inmath) {
640 #ifdef DEBUG
641 				printf("DEBUG(%s:%d) got $ so leave math mode\n",__FILE__,__LINE__);
642 				printf("DEBUG(%s:%d) pstack size is %d\n",__FILE__,__LINE__,int(pstack.size()));
643 #endif
644 
645 				// Were in math; now go back to original font.
646 				inmath = false;
647 				if (current_font != original_font) {
648 					current_font = original_font;
649 					STOP_OLD_TEXT;
650 					gr_setfont(current_font);
651 					START_NEW_TEXT;
652 				}
653 				if (!pstack.empty()) {
654 					warning("a text string ended without completing a mathematical grouping (superscript, subscript, or {block})");
655 					pstack_erase();
656 				}
657 			} else {
658 #ifdef DEBUG
659 				printf("DEBUG(%s:%d) got $ so enter math mode\n",__FILE__,__LINE__);
660 #endif
661 				// Go to Italic/Oblique font, as case may be.  Unfortunately,
662 				// PostScript uses different names for this slanted font.
663 				inmath = true;
664 				if (know_slant_font) {
665 					current_font = slant_font;
666 					STOP_OLD_TEXT;
667 					gr_setfont(current_font);
668 					START_NEW_TEXT;
669 				}
670 				if (!pstack.empty()) {
671 					warning("a text string started without an empty mathematical grouping (superscript, subscript, or {block})");
672 					pstack_erase();
673 				}
674 			}
675 			slast = *s++;
676 			continue;
677 		}
678 		// Handle math mode.  This code is a little kludgy, so be carefull.
679 		if (inmath) {
680 			if (*s == '^') {
681 				// Handle superscripts
682 				slast = *s++;
683 				if (*s == '\0') {
684 					// Odd -- nothing to superscript
685 					if (current_font != original_font) {
686 						STOP_OLD_TEXT;
687 						gr_setfontsize_pt(original_fontsize);
688 						gr_setfont(original_font);
689 					}
690 					return;
691 				} else if (*s == '{') {
692 					// Several characters to superscript
693 					pstack.push(Superscript);
694 #ifdef DEBUG
695 					printf("DEBUG(%s:%d) pushed superscript=%d onto stack to make length %d\n",__FILE__,__LINE__,Superscript, int(pstack.size()));
696 #endif
697 					MoveUp();
698 				} else if (*s == '\\') {
699 					// Math character to superscript
700 					int inc;
701 					int symbol_index = symbol_in_math(s, &inc);
702 					if (inc) {
703 						gr_fontID       oldfontID = gr_currentfont();
704 						pstack.push(Superscript);
705 #ifdef DEBUG
706 						printf("DEBUG(%s:%d) pushed subscript=%d onto stack to make length %d\n",__FILE__,__LINE__,Superscript, int(pstack.size()));
707 #endif
708 						MoveUp();
709 						STOP_OLD_TEXT;
710 						gr_setfont(gr_font_Symbol);
711 						if (_grWritePS) {
712 							fprintf(_grPS, "(%s) sh\n", symbol_code[symbol_index][1]);
713 							check_psfile();
714 						}
715 						gr_setfont(oldfontID);
716 						START_NEW_TEXT;
717 						s += inc;
718 #ifdef DEBUG
719 						printf("DEBUG(%s:%d) about to pop stack (was %d) to make length %d\n",__FILE__,__LINE__,pstack.top(), int(pstack.size())-1);
720 #endif
721 						MoveDown();
722 						pstack.pop();
723 					}
724 				} else {
725 					// Single character to superscript
726 					pstack.push(Superscript);
727 #ifdef DEBUG
728 					printf("DEBUG(%s:%d) pushed subscript=%d onto stack to make length %d\n",__FILE__,__LINE__,Subscript, int(pstack.size()));
729 #endif
730 					MoveUp();
731 					// Draw single character in math mode.  If it's a digit,
732 					// do not do in italics!
733 					if (isdigit(*s) || ispunct(*s)) {
734 						if (*s == '/' && !isdigit(slast)) {
735 							gr_DrawChar(s);
736 						} else {
737 							STOP_OLD_TEXT;
738 							gr_setfont(original_font);
739 							START_NEW_TEXT;
740 							gr_DrawChar(s);
741 							STOP_OLD_TEXT;
742 							gr_setfont(slant_font);
743 							START_NEW_TEXT;
744 						}
745 					} else {
746 						gr_DrawChar(s);
747 					}
748 					MoveDown();
749 #ifdef DEBUG
750 					printf("DEBUG(%s:%d) about to pop stack (was %d) to make length %d\n",__FILE__,__LINE__,pstack.top(), int(pstack.size())-1);
751 #endif
752 					pstack.pop();
753 				}
754 			} else if (*s == '_') {
755 				// Handle subscript
756 				slast = *s++;
757 				if (*s == '\0') {
758 					// Odd -- nothing to subscript
759 					if (current_font != original_font) {
760 						STOP_OLD_TEXT;
761 						gr_setfontsize_pt(original_fontsize);
762 						gr_setfont(original_font);
763 					}
764 					return;
765 				} else if (*s == '{') {
766 					// Several characters to subscript
767 					pstack.push(Subscript);
768 #ifdef DEBUG
769 					printf("DEBUG(%s:%d) pushed subscript=%d onto stack to make length %d\n",__FILE__,__LINE__,Subscript, int(pstack.size()));
770 #endif
771 					MoveDown();
772 				} else if (*s == '\\') {
773 					// Math character to subscript
774                                         int inc;
775 					int symbol_index = symbol_in_math(s, &inc);
776 					if (symbol_index > -1) {
777 						gr_fontID oldfontID = gr_currentfont();
778 						pstack.push(Subscript);
779 #ifdef DEBUG
780 						printf("DEBUG(%s:%d) pushed subscript=%d onto stack to make length %d\n",__FILE__,__LINE__,Subscript, int(pstack.size()));
781 #endif
782 						MoveDown();
783 						STOP_OLD_TEXT;
784 						gr_setfont(gr_font_Symbol);
785 						if (_grWritePS) {
786 							fprintf(_grPS, "(%s) sh\n", symbol_code[symbol_index][1]);
787 							check_psfile();
788 						}
789 						gr_setfont(oldfontID);
790 						START_NEW_TEXT;
791 						s += inc;
792 						MoveUp();
793 #ifdef DEBUG
794 						printf("DEBUG(%s:%d) about to pop stack (was %d) to make length %d\n",__FILE__,__LINE__,pstack.top(), int(pstack.size())-1);
795 #endif
796 						pstack.pop();
797 					}
798 				} else {
799 					// Single character to subscript
800 					pstack.push(Subscript);
801 #ifdef DEBUG
802 					printf("DEBUG(%s:%d) pushed subscript=%d onto stack to make length %d\n",__FILE__,__LINE__,Subscript, int(pstack.size()));
803 #endif
804 					MoveDown();
805 					// Draw single character in math mode.  If it's a digit,
806 					// do not do in italics!
807 					if (isdigit(*s) || ispunct(*s)) {
808 						if (*s == '/' && !isdigit(slast)) {
809 							gr_DrawChar(s);
810 						} else {
811 							STOP_OLD_TEXT;
812 							gr_setfont(original_font);
813 							START_NEW_TEXT;
814 							gr_DrawChar(s);
815 							STOP_OLD_TEXT;
816 							gr_setfont(slant_font);
817 							START_NEW_TEXT;
818 						}
819 					} else {
820 						gr_DrawChar(s);
821 					}
822 					MoveUp();
823 #ifdef DEBUG
824 					printf("DEBUG(%s:%d) about to pop stack (was %d) to make length %d\n",__FILE__,__LINE__,pstack.top(), int(pstack.size())-1);
825 #endif
826 					pstack.pop();
827 				}
828 			} else if (*s == '{') { // just a grouping, not a baseline shift
829 				pstack.push(Inline);
830 #ifdef DEBUG
831 				printf("DEBUG(%s:%d) pushed Inline=%d onto stack to make length %d\n",__FILE__,__LINE__,Inline,int(pstack.size()));
832 #endif
833 
834 			} else if (*s == '}') {	// finished with Superscript/Subscript/Inline
835 				if (pstack.size() > 0) {
836 					position p = pstack.top();
837 					if (p == Superscript) {
838 						MoveDown();
839 					} else if (p == Subscript) {
840 						MoveUp();
841 					} // ignore inline
842 #ifdef DEBUG
843 					printf("DEBUG(%s:%d) about to pop stack (was %d) to make length %d\n",__FILE__,__LINE__,pstack.top(), int(pstack.size())-1);
844 #endif
845 					pstack.pop();
846 				} else {
847 					warning("unmatched \"}\" in a mathematicsal string");
848 				}
849 			} else if (*s == '\\') {
850 				// Substitute math symbol, unless it's
851 				// an escaped string
852 				if (*(s + 1) == '$') {
853 					slast = *s++;
854 				} else if (*(s + 1) == ',') {
855 					slast = *s++;
856 					MoveHorizontally(0.1666666);	// thinspace
857 				} else if (*(s + 1) == '!') {
858 					slast = *s++;
859 					MoveHorizontally(-0.1666666);	// neg thinspace
860 				} else if (*(s + 1) == '"') {
861 					slast = *s++;
862 				} else if (*(s + 1) == '\\') {
863 					slast = *s++;
864 				} else if (*(s + 1) == '{' || *(s + 1) == '}') {
865 					STOP_OLD_TEXT;
866 					gr_setfont(original_font);
867 					START_NEW_TEXT;
868 					gr_DrawChar(s + 1);
869 					STOP_OLD_TEXT;
870 					gr_setfont(slant_font);
871 					START_NEW_TEXT;
872 					slast = *s++;
873 				} else {
874                                         int inc;
875 					int symbol_index = symbol_in_math(s, &inc);
876 					if (inc) {
877 						// math symbol in symbol font
878 						gr_fontID oldfontID = gr_currentfont();
879 						STOP_OLD_TEXT;
880 						gr_setfont(gr_font_Symbol);
881 						if (_grWritePS) {
882 							fprintf(_grPS, "(%s) sh\n", symbol_code[symbol_index][1]);
883 							check_psfile();
884 						}
885 						gr_setfont(oldfontID);
886 						START_NEW_TEXT;
887 						s += inc;
888 					} else {
889 						// Not a known math-mode symbol, so just
890 						// draw it.  Is this the right thing to do?
891 						gr_DrawChar(s + 1);
892 					}
893 				}
894 			} else {
895 				// Draw single character in math mode.
896 				// If it's a digit, do not use italics.
897 				if (isdigit(*s) || ispunct(*s)) {
898 					if (*s == '/' && !isdigit(slast)) {
899 						gr_DrawChar(s);
900 					} else {
901 						STOP_OLD_TEXT;
902 						gr_setfont(original_font);
903 						START_NEW_TEXT;
904 						gr_DrawChar(s);
905 						STOP_OLD_TEXT;
906 						gr_setfont(slant_font);
907 						START_NEW_TEXT;
908 					}
909 				} else {
910 					gr_DrawChar(s);
911 				}
912 			}
913 		} else {
914 			// draw simple character outside math mode
915 			if (*s == '\\') {
916 				if (*(s + 1) == '$') {
917 					slast = *s++;
918 				} else if (*(s + 1) == '"') {
919 					slast = *s++;
920 				} else if (*(s + 1) == '\\') {
921 					slast = *s++;
922 				}
923 			}
924 			gr_DrawChar(s);
925 		}
926 		slast = *s++;
927 	}
928 	STOP_OLD_TEXT;
929 	gr_setfontsize_pt(original_fontsize);
930 	gr_setfont(original_font);
931 	_drawingstarted = true;
932 	if (!pstack.empty()) {
933 		warning("a text string ended without completing a mathematical grouping (superscript, subscript, or {block})");
934 		pstack_erase();
935 	}
936 	return;
937 }
938 
gr_fontname_from_id(int id)939 const char* gr_fontname_from_id(int id)
940 {
941 	switch (id) {
942 	case gr_font_Courier:            return("Courier");
943 	case gr_font_CourierOblique:     return("Courier-Italic");
944 	case gr_font_CourierBold:        return("Courier-Bold");
945 	case gr_font_CourierBoldOblique: return("Courier-BoldItalic");
946 	case gr_font_Helvetica:          return("Helvetica");
947 	case gr_font_HelveticaOblique:   return("Helvetica-Italic");
948 	case gr_font_HelveticaBold:      return("Helvetica-Bold");
949 	case gr_font_PalatinoRoman:
950 	case gr_font_PalatinoItalic:
951 	case gr_font_PalatinoBold:
952 	case gr_font_PalatinoBoldItalic:
953 		warning("SVG cannot handle Palatino font yet");
954 		return("Times");
955 	case gr_font_Symbol:               return("Symbol");
956 	case gr_font_TimesRoman:           return("Times");
957 	case gr_font_TimesItalic:          return("Times-Italic");
958 	case gr_font_TimesBold:            return("Times-Bold");
959 	case gr_font_TimesBoldItalic:      return("Times-BoldItalic");
960 	case gr_font_Century:     	   return("Century");
961 	}
962 	warning("SVG defaulting to Times font");
963 	return("Times");
964 }
965 
gr_drawchar_svg(char c,double xcm,double ycm,gr_fontID font_id)966 static void gr_drawchar_svg(char c, double xcm, double ycm, gr_fontID font_id)
967 {
968 	double size = gr_currentfontsize_pt();
969 	const char *font_style;
970         //printf("%s:%d | gr_drawchar_svg('%c', ...) decodes to '%c' font_id=.s\n", __FILE__, __LINE__, c, symbol_code[int(c)][1]);//, gr_fontID);
971 	if (pstack.size() > 0)
972 		size *= SuperSize;
973 	if (isdigit(c) || font_id == gr_font_Symbol)
974 		font_style = "normal";
975 	else
976 		font_style = "italic";
977 	const char *fill = _griState.color_text().get_hexcolor().c_str();
978 	double transparency = _griState.color_text().getT();
979         fprintf(_grSVG, "<g><text x=\"%.1f\" y=\"%.1f\" font-family=\"%s\" font-size=\"%.1f\" font-style=\"%s\" fill=\"%s\" opacity=\"%.2f\" style=\"fill:%s\">%c</text></g>\n",
980                 xcm * PT_PER_CM,
981                 /*gr_page_height_pt() -*/ -ycm * PT_PER_CM,
982                 gr_fontname_from_id(font_id), size, font_style,
983                 fill,
984                 1.0 - transparency,
985                 fill,
986                 c);
987 	gr_setfont(font_id);
988 	char st[2];
989 	st[0] = c;
990 	st[1] = '\0';
991 	double w, a, d;
992 	double oldfontsize = gr_currentfontsize_pt();
993 	gr_setfontsize_pt(size);
994 	gr_stringwidth(st, &w, &a, &d); // BUG: NEED TO SET FONT FIRST
995 	gr_setfontsize_pt(oldfontsize);
996 	//*xcm += w;
997 }
gr_drawsymbol_svg(int index,double xcm,double ycm,gr_fontID font_id)998 static void gr_drawsymbol_svg(int index, double xcm, double ycm, gr_fontID font_id)
999 {
1000 	double size = gr_currentfontsize_pt();
1001 	const char *font_style;
1002         //printf("%s:%d | gr_drawchar_svg('%c', ...) decodes to '%c' font_id=.s\n", __FILE__, __LINE__, c, symbol_code[int(c)][1]);//, gr_fontID);
1003 	if (pstack.size() > 0)
1004 		size *= SuperSize;
1005 	if (font_id == gr_font_Symbol)
1006 		font_style = "normal";
1007 	else
1008 		font_style = "italic";
1009 	const char *fill = _griState.color_text().get_hexcolor().c_str();
1010 	double transparency = _griState.color_text().getT();
1011         if (font_id == gr_font_Symbol) {
1012                 fprintf(_grSVG, "<g><text x=\"%.1f\" y=\"%.1f\" font-family=\"%s\" font-size=\"%.1f\" font-style=\"%s\" fill=\"%s\" opacity=\"%.2f\" style=\"fill:%s\">%s</text></g>\n",
1013                         xcm * PT_PER_CM,
1014                         /*gr_page_height_pt() -*/ -ycm * PT_PER_CM,
1015                         gr_fontname_from_id(font_id), size, font_style,
1016                         fill,
1017                         1.0 - transparency,
1018                         fill,
1019                         symbol_code[index][3]);
1020         } else {
1021                 fprintf(_grSVG, "<g><text x=\"%.1f\" y=\"%.1f\" font-family=\"%s\" font-size=\"%.1f\" font-style=\"%s\" fill=\"%s\" opacity=\"%.2f\" style=\"fill:%s\">%s</text></g>\n",
1022                         xcm * PT_PER_CM,
1023                         /*gr_page_height_pt() -*/ -ycm * PT_PER_CM,
1024                         gr_fontname_from_id(font_id), size, font_style,
1025                         fill,
1026                         1.0 - transparency,
1027                         fill,
1028                         "?");
1029         }
1030 	gr_setfont(font_id);
1031 	char st[2];
1032 	st[0] = 'm';            // guess at width
1033 	st[1] = '\0';
1034 	double w, a, d;
1035 	double oldfontsize = gr_currentfontsize_pt();
1036 	gr_setfontsize_pt(size);
1037 	gr_stringwidth(st, &w, &a, &d); // BUG: NEED TO SET FONT FIRST
1038 	gr_setfontsize_pt(oldfontsize);
1039                                 //*xcm += w; // BUG
1040 }
1041 static void
gr_drawstring_svg(const char * s,double xcm,double ycm,double angle)1042 gr_drawstring_svg(const char *s, double xcm, double ycm, double angle)
1043 {
1044 #ifdef DEBUG
1045 	printf("DEBUG %s:%d gr_drawstring_svg(s=\"%s\", xcm=%f, ycm=%f, angle=%f)\n", __FILE__,__LINE__, s, xcm, ycm, angle);
1046 #endif
1047 	int             slen = strlen(s);
1048 	gr_fontID       original_font = gr_currentfont();
1049 	gr_fontID       slant_font = original_font;	// prevent compiler warning
1050 	double          original_fontsize = gr_currentfontsize_pt();
1051 	bool            know_slant_font = false;
1052 	if (slen <= 0)
1053 		return;
1054 	if (0.0 == gr_currentfontsize_pt())
1055 		return;
1056 	// Figure out slant font, if there is an appropriate one
1057 	switch (original_font) {
1058 	case gr_font_TimesRoman:
1059 		slant_font = gr_font_TimesItalic;
1060 		know_slant_font = true;
1061 		break;
1062 	case gr_font_TimesBold:
1063 		slant_font = gr_font_TimesBoldItalic;
1064 		know_slant_font = true;
1065 		break;
1066 	case gr_font_Helvetica:
1067 		slant_font = gr_font_HelveticaOblique;
1068 		know_slant_font = true;
1069 		break;
1070 	case gr_font_HelveticaBold:
1071 		slant_font = gr_font_HelveticaBoldOblique;
1072 		know_slant_font = true;
1073 		break;
1074 	case gr_font_Courier:
1075 		slant_font = gr_font_CourierOblique;
1076 		know_slant_font = true;
1077 		break;
1078 	case gr_font_CourierBold:
1079 		slant_font = gr_font_CourierBoldOblique;
1080 		know_slant_font = true;
1081 		break;
1082 	case gr_font_PalatinoRoman:
1083 		slant_font = gr_font_PalatinoItalic;
1084 		know_slant_font = true;
1085 		break;
1086 	case gr_font_PalatinoBold:
1087 		slant_font = gr_font_PalatinoBoldItalic;
1088 		know_slant_font = true;
1089 		break;
1090         case gr_font_Century:
1091                 slant_font = gr_font_TimesItalic; // BUG: should be Century Italic
1092 		know_slant_font = true;
1093 		break;
1094 	default:
1095 		know_slant_font = false;
1096 	}
1097 	std::vector<std::string> parts;
1098 	parts = part_string(s);
1099 	int nparts = parts.size();
1100 	double xxcm = xcm, yycm = ycm;
1101 	fprintf(_grSVG, "<g transform=\"translate(%.2f,%.2f)\">\n", xxcm*PT_PER_CM,
1102 		gr_page_height_pt() - yycm*PT_PER_CM);
1103 	xxcm = yycm = 0.0;
1104 	fprintf(_grSVG, "<g transform=\"rotate(%.1f)\">\n", -angle);
1105 	angle = 0.0;		// BUG: not sure if this is the best method
1106 	//printf("DEBUG (%s)\n", _griState.color_text().get_hexcolor().c_str());
1107 	for (int i = 0; i < nparts; i++) {
1108 		double w, a, d;
1109 		std::string p = parts[i];
1110 		gr_stringwidth(p.c_str(), &w, &a, &d);
1111 		//printf("    \"%s\" w=%f a=%f d=%f\n", parts[i].c_str(),w,a,d);
1112 		if (p[0] != '$') {
1113 			// Normal text
1114 			fprintf(_grSVG, "<g><text x=\"%.1f\" y=\"%.1f\" font-family=\"%s\" font-size=\"%.1f\" font-style=\"normal\" fill=\"%s\" opacity=\"%.2f\" style=\"fill:%s\">%s</text></g>\n",
1115 				xxcm * PT_PER_CM,
1116 				/*gr_page_height_pt() -*/ -yycm * PT_PER_CM,
1117 				gr_fontname_from_id(original_font),
1118 				original_fontsize,
1119 				_griState.color_text().get_hexcolor().c_str(),
1120 				1.0 - _griState.color_text().getT(),
1121 				_griState.color_text().get_hexcolor().c_str(),
1122 				p.c_str());
1123 			xxcm += w;
1124 			//FIXME: should s/\$/$/ first
1125 		} else {
1126 			// Math text
1127 			unsigned int ic, nc = p.size();
1128 //			w = gr_thinspace_cm() / 1.0; // put a bit of space before math
1129 //			xxcm += w;
1130 			if (p[nc-1] == '$') nc--;
1131 			fprintf(_grSVG, "<!-- math mode -->\n<g>\n");
1132 			for (ic = 1; ic < nc; ic++) {
1133 #ifdef DEBUG
1134 				printf(" -- %d [%c] --\n", ic, p[ic]);
1135 #endif
1136 				if (p[ic] == '\\') { // HEREHEREHERE
1137 					if (p[ic+1] == '\\') {
1138 						gr_drawsymbol_svg(-1, xxcm, yycm, original_font);
1139 						ic += 1;
1140 						continue;
1141 					}
1142 					int inc;
1143 					int symbol_index = symbol_in_math(p.c_str() + ic, &inc);
1144 					if (inc) {
1145 						gr_drawsymbol_svg(symbol_index, xxcm, yycm, gr_font_Symbol);
1146 						ic += inc;
1147 					} else {
1148 						gr_drawstring_svg("?", xxcm, yycm, gr_font_Symbol);
1149 					}
1150 				} else if (p[ic] == '{') {
1151 					// ignore
1152 					pstack.push(Inline);
1153 					ic++;
1154 					continue;
1155 				} else if (p[ic] == '}') {
1156 					if (pstack.size() > 0) {
1157 						position p = pstack.top();
1158 						if (p == Superscript) {
1159 							MoveDn_svg(&xxcm, &yycm);
1160 						} else if (p == Subscript) {
1161 							MoveUp_svg(&xxcm, &yycm);
1162 						}
1163 #ifdef DEBUG
1164 						printf("DEBUG(%s:%d) about to pop stack (was %d) to make length %d\n",__FILE__,__LINE__,pstack.top(), int(pstack.size())-1);
1165 #endif
1166 						pstack.pop();
1167 					} else {
1168 						warning("unmatched \"}\" in a mathematical string");
1169 					}
1170 				} else if (p[ic] == '_') {
1171 					pstack.push(Subscript);
1172 					MoveDn_svg(&xxcm, &yycm);
1173 					if (p[ic+1] != '{') { // BUG: should check for symbol
1174 						gr_drawchar_svg(p[ic+1], xxcm, yycm, original_font);
1175 						MoveUp_svg(&xxcm, &yycm);
1176 						pstack.pop();
1177 					}
1178 					ic++;
1179 					continue;
1180 				} else if (p[ic] == '^') {
1181 					pstack.push(Superscript);
1182 					MoveUp_svg(&xxcm, &yycm);
1183 					if (p[ic+1] != '{') { // BUG: should check for symbol
1184 						gr_drawchar_svg(p[ic+1], xxcm, yycm, original_font);
1185 						MoveDn_svg(&xxcm, &yycm);
1186 						pstack.pop();
1187 					}
1188 					ic++;
1189 					continue;
1190 				} else {
1191 					gr_drawchar_svg(p[ic], xxcm, yycm, original_font);
1192 				}
1193 			}
1194 			fprintf(_grSVG, "</g>\n");
1195 		}
1196 	}
1197 	fprintf(_grSVG, "</g>\n"); // rotate
1198 	fprintf(_grSVG, "</g>\n"); // translate
1199 	return;
1200 }
1201 
1202 
1203 // set fontsize in points
1204 void
gr_setfontsize_pt(double fontsize_pt)1205 gr_setfontsize_pt(double fontsize_pt)
1206 {
1207 	if (fontsize_pt < 0.0)
1208 		CurrentFont.size_pt = default_fontsize_pt;
1209 	else
1210 		CurrentFont.size_pt = fontsize_pt;
1211 	gr_setfont_fontsize(CurrentFont.id);
1212 }
1213 
1214 // Set font encoding
1215 void
gr_set_font_encoding(gr_font_encoding encoding)1216 gr_set_font_encoding(gr_font_encoding encoding)
1217 {
1218 	CurrentFont.encoding = encoding;
1219 }
1220 // Get font encoding
1221 gr_font_encoding
gr_current_font_encoding()1222 gr_current_font_encoding()
1223 {
1224 	return CurrentFont.encoding;
1225 }
1226 
1227 /*
1228  * gr_currentfont() -- find current font synopsis int gr_currentfont()
1229  * description:  gets the current font,as set by gr_setfont(). return value:
1230  * current font number.
1231  */
1232 gr_fontID
gr_currentfont()1233 gr_currentfont()
1234 {
1235 	return CurrentFont.id;
1236 }
1237 
1238 /*
1239  * gr_currentfontsize_pt() -- return current fontsize in points
1240  */
1241 double
gr_currentfontsize_pt()1242 gr_currentfontsize_pt()
1243 {
1244 	return CurrentFont.size_pt;
1245 }
1246 
1247 /*
1248  * gr_setfont() --  set new font. SYNOPSIS void	gr_setfont(int new_font)
1249  * DESCRIPTION:  Sets the font for future string drawing to 'new_font'. These
1250  * fonts are predefined: TimesRoman Helvetica Courier Symbol Palatino-Roman
1251  * Palatino-Italic.
1252  *
1253  */
1254 void
gr_setfont(gr_fontID newID,bool force)1255 gr_setfont(gr_fontID newID, bool force /* default false */)
1256 {
1257 	gr_setfont_fontsize(newID, force);
1258 }
1259 
1260 static void
gr_setfont_fontsize(gr_fontID newID,bool force)1261 gr_setfont_fontsize(gr_fontID newID, bool force)
1262 {
1263 	int             i = 0;
1264 	static bool     have_set_font = false;
1265 	static gr_font  last_font;
1266 	/* Search the font list */
1267 	while (font_list[i].id != gr_font_end_of_list) {
1268 		if (newID == font_list[i].id) {
1269 			/* Found the font, but ignore request if no change */
1270 			if (force
1271 			    || (!have_set_font
1272 				|| newID != last_font.id
1273 				|| CurrentFont.encoding != last_font.encoding
1274 				|| CurrentFont.size_pt != last_font.size_pt)) {
1275 				CurrentFont.id = newID;
1276 				if (!_grNeedBegin) {
1277 					/*
1278 					 * Don't try to write if haven't done gr_begin() yet,
1279 					 * since then will ruin things like
1280 					 * gr_setup_ps_filename();
1281 					 */
1282 					if (_grWritePS) {
1283 						switch (CurrentFont.encoding) {
1284 						case font_encoding_standard:
1285 							fprintf(_grPS, "/%s findfont ", font_list[i].name);
1286 							break;
1287 						case font_encoding_isolatin1:
1288 							if (CurrentFont.id == gr_font_Symbol)
1289 								fprintf(_grPS, "/%s findfont ", font_list[i].name);
1290 							else
1291 								fprintf(_grPS, "/%s-ISOLatin1 findfont ", font_list[i].name);
1292 							break;
1293 						}
1294 						fprintf(_grPS, "%.2f sc sf\n", CurrentFont.size_pt);
1295 					}
1296 					have_set_font = true;
1297 					last_font.id = newID;
1298 					last_font.encoding = CurrentFont.encoding;
1299 					last_font.size_pt = CurrentFont.size_pt;
1300 				}
1301 			}
1302 			return;
1303 		}
1304 		i++;
1305 	}
1306 	warning("Ignoring request for unknown font.");
1307 }
1308 
symbol_in_math(const char * sPtr,int * inc)1309 static int symbol_in_math(const char *sPtr, int *inc) // handle greek letter or symbol in math mode
1310 {
1311 	sPtr++;
1312 	*inc = 0;
1313 	for (int i = 0; i < NCODES; i++) {
1314 		int len = strlen(symbol_code[i][0]);
1315 		if (!strncmp(sPtr, symbol_code[i][0], len)) {
1316 			*inc = len;
1317 			return i;
1318 		}
1319 	}
1320 	return -1;
1321 }
1322 
1323 // Clear the position stack (doesn't STL do this??)
1324 static void
pstack_erase()1325 pstack_erase()
1326 {
1327 	while (!pstack.empty()) {
1328 		position p = pstack.top();
1329 		if (p == Superscript) {
1330 			MoveDown();
1331 		} else if (p == Subscript) {
1332 			MoveUp();
1333 		} // ignore inline
1334 #ifdef DEBUG
1335 		if (p == Superscript)
1336 			printf("\tcleared Superscript from position stack\n");
1337 		else if (p == Subscript)
1338 			printf("\tcleared Subscript from position stack\n");
1339 #endif
1340 		pstack.pop();
1341 	}
1342 }
1343 // Move left/right by indicated number of M spaces
1344 static void
MoveHorizontally(double em_distance)1345 MoveHorizontally(double em_distance)
1346 {
1347 	double          w, a, d;
1348 	gr_stringwidth("M", &w, &a, &d);
1349 	STOP_OLD_TEXT;
1350 	gr_rmoveto_cm(em_distance * w, 0.0);
1351 	START_NEW_TEXT;
1352 }
1353 
1354 // MoveUp() -- move up, shifting to smaller/larger size if necessary
MoveUp_svg(double * xcm,double * ycm)1355 static void MoveUp_svg(double *xcm, double *ycm)
1356 {
1357 #ifdef DEBUG
1358 	printf("DEBUG(%s:%d) moving text position up one level.  Stack size on entry = %d\n", __FILE__,__LINE__,(int)pstack.size());
1359 #endif
1360 	if (!pstack.size())
1361 		return;
1362 	double dy;
1363 	// See if already in subscript.
1364 	position p = pstack.top();
1365 	if (p == Subscript) {
1366 		// Moving up from subscript, so enlarge font, then undo last move
1367 		// down.
1368 		gr_setfontsize_pt(gr_currentfontsize_pt() / SubSize);
1369 		dy = SubMoveDown * gr_currentCapHeight_cm();
1370 	} else {
1371 		// Moving up from inline or superscript, so move up, then reduce font.
1372 		dy = SuperMoveUp * gr_currentCapHeight_cm();
1373 		gr_setfontsize_pt(gr_currentfontsize_pt() * SuperSize);
1374 	} // ignore Inline
1375 	*ycm += dy;
1376 }
1377 // MoveDown() -- move down, shifting to smaller/larger size if necessary
MoveDn_svg(double * xcm,double * ycm)1378 static void MoveDn_svg(double *xcm, double *ycm)
1379 {
1380 #ifdef DEBUG
1381 	printf("DEBUG(%s:%d) moving text position down one level\n", __FILE__,__LINE__);
1382 #endif
1383 	if (!pstack.size())
1384 		return;
1385 	double dy;
1386 	position p = pstack.top();
1387 	// See if already in superscript.
1388 	if (p == Superscript) {
1389 		// Moving down from superscript, so enlarge font, then undo last move up.
1390 		gr_setfontsize_pt(gr_currentfontsize_pt() / SuperSize);
1391 		dy = -SuperMoveUp * gr_currentCapHeight_cm();
1392 	} else {
1393 		// Moving down from inline or subscript, so move down, then reduce font.
1394 		dy = -SubMoveDown * gr_currentCapHeight_cm();
1395 		gr_setfontsize_pt(gr_currentfontsize_pt() * SubSize);
1396 	}
1397 	*ycm += dy;
1398 }
1399 
1400 static void
MoveUp()1401 MoveUp()
1402 {
1403 #ifdef DEBUG
1404 	printf("DEBUG(%s:%d) moving text position up one level\n", __FILE__,__LINE__);
1405 #endif
1406 	STOP_OLD_TEXT;
1407 	// See if already in subscript.
1408 	position p = pstack.top();
1409 	if (p == Subscript) {
1410 		// Moving up from subscript, so enlarge font, then undo last move
1411 		// down.
1412 		gr_setfontsize_pt(gr_currentfontsize_pt() / SubSize);
1413 		gr_rmoveto_pt(0.0, SubMoveDown * gr_currentCapHeight_cm() * PT_PER_CM);
1414 	} else {
1415 		// Moving up from inline or superscript, so move up, then reduce font.
1416 		gr_rmoveto_pt(0.0, SuperMoveUp * gr_currentCapHeight_cm() * PT_PER_CM);
1417 		gr_setfontsize_pt(gr_currentfontsize_pt() * SuperSize);
1418 	} // ignore Inline
1419 	START_NEW_TEXT;
1420 }
1421 
1422 // MoveDown() -- move down, shifting to smaller/larger size if necessary
1423 static void
MoveDown()1424 MoveDown()
1425 {
1426 #ifdef DEBUG
1427 	printf("DEBUG(%s:%d) moving text position down one level\n", __FILE__,__LINE__);
1428 #endif
1429 	STOP_OLD_TEXT;
1430 	position p = pstack.top();
1431 	// See if already in superscript.
1432 	if (p == Superscript) {
1433 		// Moving down from superscript, so enlarge font, then undo last move up.
1434 		gr_setfontsize_pt(gr_currentfontsize_pt() / SuperSize);
1435 		gr_rmoveto_pt(0.0, -SuperMoveUp * gr_currentCapHeight_cm() * PT_PER_CM);
1436 	} else {
1437 		// Moving down from inline or subscript, so move down, then reduce font.
1438 		gr_rmoveto_pt(0.0, -SubMoveDown * gr_currentCapHeight_cm() * PT_PER_CM);
1439 		gr_setfontsize_pt(gr_currentfontsize_pt() * SubSize);
1440 	}
1441 	START_NEW_TEXT;
1442 }
1443 
1444 static void
gr_DrawChar(const char * c)1445 gr_DrawChar(const char *c)
1446 {
1447 	extern bool     _grWritePS;
1448 	if (_grWritePS) {
1449 		extern FILE *_grPS;
1450 		switch (*c) {
1451 		case '\\':
1452 			fprintf(_grPS, "\\\\");
1453 			break;
1454 		case '(':
1455 			fprintf(_grPS, "\\(");
1456 			break;
1457 		case ')':
1458 			fprintf(_grPS, "\\)");
1459 			break;
1460 		default:
1461 			fprintf(_grPS, "%c", *c);
1462 			break;
1463 		}
1464 		check_psfile();
1465 	}
1466 	_drawingstarted = true;
1467 }
1468 
1469 // Draw indicated text in a "whiteout" box of indicated color, left-right
1470 // centered at the indicated (x,y) locn specified in user-units.  The text
1471 // and box will be rotated by gr_currenttextangle_deg() degrees, measured
1472 // counterclockwise from the horizontal.
1473 void
gr_show_in_box(GriString & s,const GriColor & text_color,const GriColor & box_color,double x,double y,double angle_deg)1474 gr_show_in_box(/*const*/GriString &s,
1475 	       const GriColor& text_color,
1476 	       const GriColor& box_color,
1477 	       double x,	// cm units
1478 	       double y,
1479 	       double angle_deg)
1480 {
1481 	GriColor old_text_color = _griState.color_text();
1482 	GriColor old_line_color = _griState.color_line();
1483 
1484 	double          width, ascent, descent;
1485 	double          x0, y0, dx, dy, dx_rot, dy_rot;
1486 	double          thin_space = gr_thinspace_cm();
1487 	if (0.0 == gr_currentfontsize_pt())
1488 		return;
1489 	gr_stringwidth(s.getValue(), &width, &ascent, &descent);
1490 	x0 = x;			// save
1491 	y0 = y;
1492 
1493 	// White out below text.
1494 	dx = -0.5 * width - thin_space;
1495 	dy = -thin_space;
1496 	gr_rotate_xy(dx, dy, angle_deg, &dx_rot, &dy_rot);
1497 
1498 	static GriPath p(5);
1499 	p.clear();
1500 	p.push_back(x0 + dx_rot, y0 + dy_rot, 'm');
1501 
1502 	dx = -dx;
1503 	gr_rotate_xy(dx, dy, angle_deg, &dx_rot, &dy_rot);
1504 	p.push_back(x0 + dx_rot, y0 + dy_rot, 'l');
1505 
1506 	dx = 0.5 * width + thin_space;
1507 	dy = ascent + thin_space;
1508 	gr_rotate_xy(dx, dy, angle_deg, &dx_rot, &dy_rot);
1509 	p.push_back(x0 + dx_rot, y0 + dy_rot, 'l');
1510 
1511 	dx = -dx;
1512 	gr_rotate_xy(dx, dy, angle_deg, &dx_rot, &dy_rot);
1513 	p.push_back(x0 + dx_rot, y0 + dy_rot, 'l');
1514 
1515 	p.push_back(x0 + dx_rot, y0 + dy_rot, 'l');
1516 
1517 	_griState.set_color_line(box_color);
1518 	p.fill(units_cm);
1519 
1520 	bounding_box_update(p.bounding_box(units_cm));
1521 
1522 	// Draw text
1523 	_griState.set_color_text(text_color);
1524 	dx = -0.5 * width;
1525 	dy = 0.0;
1526 	gr_rotate_xy(dx, dy, angle_deg, &dx_rot, &dy_rot);
1527 	gr_show_at(s.getValue(),
1528 		   x0 + dx_rot,
1529 		   y0 + dy_rot,
1530 		   TEXT_LJUST,
1531 		   angle_deg);
1532 	_griState.set_color_line(old_line_color);
1533 	_griState.set_color_text(old_text_color);
1534 	_drawingstarted = true;
1535 }
1536 
1537 // Rotate (x,y) into (xx,yy), through `angle' degrees counterclockwise.
1538 void
gr_rotate_xy(double x,double y,double angle,double * xx,double * yy)1539 gr_rotate_xy(double x, double y, double angle, double *xx, double *yy)
1540 {
1541 	angle /= DEG_PER_RAD;	// convert to radians
1542 	double c = cos(angle);
1543 	double s = sin(angle);
1544 	*xx = c * x - s * y;
1545 	*yy = s * x + c * y;
1546 }
1547 
1548 // Get the width, ascent and descent of string s, in current font.
1549 // BUG: Ascent and descent are inaccurate.
1550 // BUG: Smaller size of super/subscripts not accounted for.
1551 void
gr_stringwidth(const char * s,double * w,double * a,double * d)1552 gr_stringwidth(const char *s, double *w, double *a, double *d)
1553 {
1554 	*w = *a = *d = 0.0;
1555 	if (strlen(s) == 0)
1556 		return;
1557 	bool            used_supers = false;
1558 	bool            used_subs = false;
1559 	bool            inmath = false;
1560 	bool            oldWritePS = _grWritePS;
1561 	double          oldfontsize_pt = gr_currentfontsize_pt();
1562 	_grWritePS = false;
1563 	while (*s != '\0') {
1564 		// figure out whether entering or leaving math mode
1565 		if (*s == '$' && *(s - 1) != '\\') {
1566 #ifdef DEBUG
1567 			printf("DEBUG %s:%d toggling inmath; rest of string is \"%s\"\n",__FILE__,__LINE__,s);
1568 #endif
1569 			inmath = (inmath ? false : true);
1570 			s++;
1571 			continue;
1572 		}
1573 		// handle math mode differently
1574 		// ?? BUG Superscripts and subscripts are printed smaller, but
1575 		// ?? BUG their size is assumed to be the same as normal chars.
1576 		if (inmath) {
1577 			if (*s == '^')	// handle superscript
1578 				used_supers = true;
1579 			else if (*s == '_')	// handle subscript
1580 				used_subs = true;
1581 			else if (*s == '{')	// ignore groups while computing string length
1582 				;		// EMPTY
1583 			else if (*s == '}')	// ignore groups
1584 				;		// EMPTY
1585 			else if (*s == '\\') {	// handle synonym
1586 				// First catch thinspace commands
1587 				if (*(s + 1) == ',') {
1588 					// Thinspace = Mwidth/6
1589 					*w += gr_charwidth_cm((int)'M', CurrentFont.id, CurrentFont.size_pt) / 6.0;
1590 					s += 1;
1591 				} else if (*(s + 1) == '!') {
1592 					// Negative thinspace = -Mwidth/6
1593 					*w -= gr_charwidth_cm((int)'M', CurrentFont.id, CurrentFont.size_pt) / 6.0;
1594 					s += 1;
1595 				} else {
1596                                         int inc;
1597 					int symbol_index = symbol_in_math(s, &inc);
1598 					if (symbol_index > -1) {
1599 						gr_fontID oldfontID = CurrentFont.id;
1600 						s += inc;
1601                                                 // *w += gr_charwidth_cm("m" symbol_code[symbol_index][1], gr_font_Symbol, CurrentFont.size_pt);
1602                                                 *w += gr_charwidth_cm('m', gr_font_Symbol, CurrentFont.size_pt); //  BUG
1603 						CurrentFont.id = oldfontID;
1604 					} else {
1605 						// it's not a known math symbol
1606 						*w += gr_charwidth_cm('\\', CurrentFont.id, CurrentFont.size_pt);
1607 					}
1608 				}
1609 			} else {
1610 				// We are in math-mode, but it's not a special character. Add
1611 				// appropriate amount for either super/subscript or normal
1612 				// character.
1613 				*w += gr_charwidth_cm((int) *s, CurrentFont.id, CurrentFont.size_pt);
1614 			}
1615 		} else {
1616 			// not inmath
1617 			*w += gr_charwidth_cm((int) *s, CurrentFont.id, CurrentFont.size_pt);
1618 		}
1619 		s++;
1620 	}
1621 	// Calculate ascent/descent.  BUG: doesn't take math chars into acct
1622 	*a = gr_currentCapHeight_cm() * (1 + (used_supers ? 1 : 0) *
1623 					 (SuperSize + SuperMoveUp - 1));
1624 #if 0				// before 2.054
1625 	*d = gr_currentCapHeight_cm() * (1.0 + 0.5 * (int) used_subs);
1626 #endif
1627 	*d = gr_current_descender() * (1 + (used_subs ? 1.0 : 0.0) *
1628 				       (SubSize + SubMoveDown - 1));
1629 #ifdef DEBUG
1630 	printf("DEBUG %s:%d gr_stringwidth(s=\"%s\",...) RETURNING w=%f, a=%f d=%f\n", __FILE__,__LINE__,s,*w,*a,*d);
1631 #endif
1632 	// reset fontsize ... can't do with gr_setfontsize_pt()
1633 	// because that would call this function in infinite recursion.
1634 	CurrentFont.size_pt = oldfontsize_pt;
1635 	_grWritePS = oldWritePS;
1636 }
1637 
1638 #if 0
1639 // return index (for size-table) for a character (given as integer)
1640 static int index_for_math_symbol(const char *s)
1641 {
1642 	//printf("index_for_math_symbol(%s)\n",s);
1643 	if (!s) return (int)'?';
1644 	for (int i = 0; i < NCODES; i++) {
1645 		//printf("  %3d (%s) (%s)\n", i, symbol_code[i][1],symbol_code[i][2]);
1646 		if (!strncmp(s, symbol_code[i][1], strlen(symbol_code[i][1]))) {
1647 			//printf(" match\n");
1648 			return (int) *symbol_code[i][2];
1649 		}
1650 		//printf("  no match\n");
1651 	}
1652 	//printf("index_for_math_symbol(%s) cannot find a match\n", s);
1653 	return (int) 'M';	// a guess, since we have no clue
1654 }
1655 #endif
1656 
1657 // Return thinspace (=1/6 of width of "M" in current font), in cm
1658 double
gr_thinspace_cm()1659 gr_thinspace_cm()
1660 {
1661 	return (gr_charwidth_cm(int('M'), CurrentFont.id, CurrentFont.size_pt) / 6.0);
1662 }
1663 
1664 // Return width of quad (= width of "M" in current font), in cm
1665 double
gr_quad_cm()1666 gr_quad_cm()
1667 {
1668 	return (gr_charwidth_cm((int) 'M', CurrentFont.id, CurrentFont.size_pt));
1669 }
1670 
1671 // Following page should substituted as output from
1672 // ~kelley/src/gri/src/get_font_metrics
1673 
1674 struct font_metric {
1675 	float XHeight;
1676 	float CapHeight;
1677 	float Ascender;
1678 	float Descender;
1679 	float width[128];
1680 };
1681 //
1682 // Following font metric generated by `get_font_metrics'
1683 // perlscript from Font Metric file `/usr/openwin/lib/X11/fonts/F3/afm/Courier.afm'.
1684 // All measurement in centimetres, given a pointsize of 1.0
1685 //
1686 
1687 // Created by Perl script get_font_metrics.pl
1688 struct font_metric CenturyRoman = {
1689     0.016369, // XHeight
1690     0.025471, // CapHeight
1691     0.026000, // Ascender
1692     -0.007232, // Descender
1693     { // Widths of first 128 characters
1694         0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1695         0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1696         0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1697         0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1698         0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1699         0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1700         0.0000000, 0.0000000, 0.0098072, 0.0104422, 0.0137231,
1701         0.0196144, 0.0196144, 0.0293864, 0.0287514, 0.0071967,
1702         0.0117475, 0.0117475, 0.0176389, 0.0213783, 0.0098072,
1703         0.0117475, 0.0098072, 0.0098072, 0.0196144, 0.0196144,
1704         0.0196144, 0.0196144, 0.0196144, 0.0196144, 0.0196144,
1705         0.0196144, 0.0196144, 0.0196144, 0.0098072, 0.0098072,
1706         0.0213783, 0.0213783, 0.0213783, 0.0156633, 0.0259997,
1707         0.0254706, 0.0254706, 0.0254706, 0.0274461, 0.0254706,
1708         0.0235303, 0.0274461, 0.0293864, 0.0143581, 0.0196144,
1709         0.0274461, 0.0235303, 0.0333022, 0.0287514, 0.0274461,
1710         0.0235303, 0.0274461, 0.0254706, 0.0222250, 0.0235303,
1711         0.0287514, 0.0254706, 0.0346075, 0.0248356, 0.0248356,
1712         0.0215547, 0.0117475, 0.0213783, 0.0117475, 0.0213783,
1713         0.0176389, 0.0071967, 0.0196144, 0.0196144, 0.0156633,
1714         0.0202494, 0.0176389, 0.0117475, 0.0189442, 0.0215547,
1715         0.0111125, 0.0104422, 0.0209197, 0.0111125, 0.0313619,
1716         0.0215547, 0.0176389, 0.0202494, 0.0196144, 0.0156633,
1717         0.0163336, 0.0137231, 0.0215547, 0.0189442, 0.0274461,
1718         0.0189442, 0.0189442, 0.0169686, 0.0117475, 0.0213783,
1719         0.0117475, 0.0213783, 0.0000000
1720     }
1721 };
1722 
1723 struct font_metric Courier = {
1724 	0.015028,	// XHeight
1725 	0.019826,	// CapHeight
1726 	0.022190,	// Ascender
1727 	-0.005539,	// Descender
1728 	{	// Widths of first 128 characters
1729 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1730 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1731 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1732 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1733 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1734 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1735 		0.0000000, 0.0000000, 0.0211667, 0.0211667, 0.0211667,
1736 		0.0211667, 0.0211667, 0.0211667, 0.0211667, 0.0211667,
1737 		0.0211667, 0.0211667, 0.0211667, 0.0211667, 0.0211667,
1738 		0.0211667, 0.0211667, 0.0211667, 0.0211667, 0.0211667,
1739 		0.0211667, 0.0211667, 0.0211667, 0.0211667, 0.0211667,
1740 		0.0211667, 0.0211667, 0.0211667, 0.0211667, 0.0211667,
1741 		0.0211667, 0.0211667, 0.0211667, 0.0211667, 0.0211667,
1742 		0.0211667, 0.0211667, 0.0211667, 0.0211667, 0.0211667,
1743 		0.0211667, 0.0211667, 0.0211667, 0.0211667, 0.0211667,
1744 		0.0211667, 0.0211667, 0.0211667, 0.0211667, 0.0211667,
1745 		0.0211667, 0.0211667, 0.0211667, 0.0211667, 0.0211667,
1746 		0.0211667, 0.0211667, 0.0211667, 0.0211667, 0.0211667,
1747 		0.0211667, 0.0211667, 0.0211667, 0.0211667, 0.0211667,
1748 		0.0211667, 0.0211667, 0.0211667, 0.0211667, 0.0211667,
1749 		0.0211667, 0.0211667, 0.0211667, 0.0211667, 0.0211667,
1750 		0.0211667, 0.0211667, 0.0211667, 0.0211667, 0.0211667,
1751 		0.0211667, 0.0211667, 0.0211667, 0.0211667, 0.0211667,
1752 		0.0211667, 0.0211667, 0.0211667, 0.0211667, 0.0211667,
1753 		0.0211667, 0.0211667, 0.0211667, 0.0211667, 0.0211667,
1754 		0.0211667, 0.0211667, 0.0000000
1755 	}
1756 };
1757 //
1758 // Following font metric generated by `get_font_metrics'
1759 // perlscript from Font Metric file `/usr/openwin/lib/X11/fonts/F3/afm/Helvetica.afm'.
1760 // All measurement in centimetres, given a pointsize of 1.0
1761 //
1762 struct font_metric Helvetica = {
1763 	0.018450,	// XHeight
1764 	0.025329,	// CapHeight
1765 	0.025329,	// Ascender
1766 	-0.007302,	// Descender
1767 	{	// Widths of first 128 characters
1768 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1769 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1770 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1771 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1772 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1773 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1774 		0.0000000, 0.0000000, 0.0098072, 0.0098072, 0.0125236,
1775 		0.0196144, 0.0196144, 0.0313619, 0.0235303, 0.0078317,
1776 		0.0117475, 0.0117475, 0.0137231, 0.0206022, 0.0098072,
1777 		0.0117475, 0.0098072, 0.0098072, 0.0196144, 0.0196144,
1778 		0.0196144, 0.0196144, 0.0196144, 0.0196144, 0.0196144,
1779 		0.0196144, 0.0196144, 0.0196144, 0.0098072, 0.0098072,
1780 		0.0206022, 0.0206022, 0.0206022, 0.0196144, 0.0358069,
1781 		0.0235303, 0.0235303, 0.0254706, 0.0254706, 0.0235303,
1782 		0.0215547, 0.0274461, 0.0254706, 0.0098072, 0.0176389,
1783 		0.0235303, 0.0196144, 0.0293864, 0.0254706, 0.0274461,
1784 		0.0235303, 0.0274461, 0.0254706, 0.0235303, 0.0215547,
1785 		0.0254706, 0.0235303, 0.0333022, 0.0235303, 0.0235303,
1786 		0.0215547, 0.0098072, 0.0098072, 0.0098072, 0.0165453,
1787 		0.0196144, 0.0078317, 0.0196144, 0.0196144, 0.0176389,
1788 		0.0196144, 0.0196144, 0.0098072, 0.0196144, 0.0196144,
1789 		0.0078317, 0.0078317, 0.0176389, 0.0078317, 0.0293864,
1790 		0.0196144, 0.0196144, 0.0196144, 0.0196144, 0.0117475,
1791 		0.0176389, 0.0098072, 0.0196144, 0.0176389, 0.0254706,
1792 		0.0176389, 0.0176389, 0.0176389, 0.0117828, 0.0091722,
1793 		0.0117828, 0.0206022, 0.0000000
1794 	}
1795 };
1796 //
1797 // Following font metric generated by `get_font_metrics'
1798 // perlscript from Font Metric file `/usr/openwin/lib/X11/fonts/F3/afm/Helvetica-Oblique.afm'.
1799 // All measurement in centimetres, given a pointsize of 1.0
1800 //
1801 struct font_metric Helvetica_Oblique = {
1802 	0.018450,	// XHeight
1803 	0.025329,	// CapHeight
1804 	0.025329,	// Ascender
1805 	-0.007302,	// Descender
1806 	{	// Widths of first 128 characters
1807 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1808 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1809 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1810 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1811 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1812 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1813 		0.0000000, 0.0000000, 0.0098072, 0.0098072, 0.0125236,
1814 		0.0196144, 0.0196144, 0.0313619, 0.0235303, 0.0078317,
1815 		0.0117475, 0.0117475, 0.0137231, 0.0206022, 0.0098072,
1816 		0.0117475, 0.0098072, 0.0098072, 0.0196144, 0.0196144,
1817 		0.0196144, 0.0196144, 0.0196144, 0.0196144, 0.0196144,
1818 		0.0196144, 0.0196144, 0.0196144, 0.0098072, 0.0098072,
1819 		0.0206022, 0.0206022, 0.0206022, 0.0196144, 0.0358069,
1820 		0.0235303, 0.0235303, 0.0254706, 0.0254706, 0.0235303,
1821 		0.0215547, 0.0274461, 0.0254706, 0.0098072, 0.0176389,
1822 		0.0235303, 0.0196144, 0.0293864, 0.0254706, 0.0274461,
1823 		0.0235303, 0.0274461, 0.0254706, 0.0235303, 0.0215547,
1824 		0.0254706, 0.0235303, 0.0333022, 0.0235303, 0.0235303,
1825 		0.0215547, 0.0098072, 0.0098072, 0.0098072, 0.0165453,
1826 		0.0196144, 0.0078317, 0.0196144, 0.0196144, 0.0176389,
1827 		0.0196144, 0.0196144, 0.0098072, 0.0196144, 0.0196144,
1828 		0.0078317, 0.0078317, 0.0176389, 0.0078317, 0.0293864,
1829 		0.0196144, 0.0196144, 0.0196144, 0.0196144, 0.0117475,
1830 		0.0176389, 0.0098072, 0.0196144, 0.0176389, 0.0254706,
1831 		0.0176389, 0.0176389, 0.0176389, 0.0117828, 0.0091722,
1832 		0.0117828, 0.0206022, 0.0000000
1833 	}
1834 };
1835 //
1836 // Following font metric generated by `get_font_metrics'
1837 // perlscript from Font Metric file `/usr/openwin/lib/X11/fonts/F3/afm/Helvetica-Bold.afm'.
1838 // All measurement in centimetres, given a pointsize of 1.0
1839 //
1840 struct font_metric Helvetica_Bold = {
1841 	0.018768,	// XHeight
1842 	0.025329,	// CapHeight
1843 	0.025329,	// Ascender
1844 	-0.007302,	// Descender
1845 	{	// Widths of first 128 characters
1846 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1847 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1848 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1849 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1850 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1851 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1852 		0.0000000, 0.0000000, 0.0098072, 0.0117475, 0.0167217,
1853 		0.0196144, 0.0196144, 0.0313619, 0.0254706, 0.0098072,
1854 		0.0117475, 0.0117475, 0.0137231, 0.0206022, 0.0098072,
1855 		0.0117475, 0.0098072, 0.0098072, 0.0196144, 0.0196144,
1856 		0.0196144, 0.0196144, 0.0196144, 0.0196144, 0.0196144,
1857 		0.0196144, 0.0196144, 0.0196144, 0.0117475, 0.0117475,
1858 		0.0206022, 0.0206022, 0.0206022, 0.0215547, 0.0343958,
1859 		0.0254706, 0.0254706, 0.0254706, 0.0254706, 0.0235303,
1860 		0.0215547, 0.0274461, 0.0254706, 0.0098072, 0.0196144,
1861 		0.0254706, 0.0215547, 0.0293864, 0.0254706, 0.0274461,
1862 		0.0235303, 0.0274461, 0.0254706, 0.0235303, 0.0215547,
1863 		0.0254706, 0.0235303, 0.0333022, 0.0235303, 0.0235303,
1864 		0.0215547, 0.0117475, 0.0098072, 0.0117475, 0.0206022,
1865 		0.0196144, 0.0098072, 0.0196144, 0.0215547, 0.0196144,
1866 		0.0215547, 0.0196144, 0.0117475, 0.0215547, 0.0215547,
1867 		0.0098072, 0.0098072, 0.0196144, 0.0098072, 0.0313619,
1868 		0.0215547, 0.0215547, 0.0215547, 0.0215547, 0.0137231,
1869 		0.0196144, 0.0117475, 0.0215547, 0.0196144, 0.0274461,
1870 		0.0196144, 0.0196144, 0.0176389, 0.0137231, 0.0098778,
1871 		0.0137231, 0.0206022, 0.0000000
1872 	}
1873 };
1874 //
1875 // Following font metric generated by `get_font_metrics'
1876 // perlscript from Font Metric file `/usr/openwin/lib/X11/fonts/F3/afm/Palatino-Roman.afm'.
1877 // All measurement in centimetres, given a pointsize of 1.0
1878 //
1879 struct font_metric PalatinoRoman = {
1880 	0.016545,	// XHeight
1881 	0.024412,	// CapHeight
1882 	0.025612,	// Ascender
1883 	-0.009913,	// Descender
1884 	{	// Widths of first 128 characters
1885 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1886 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1887 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1888 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1889 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1890 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1891 		0.0000000, 0.0000000, 0.0088194, 0.0098072, 0.0130881,
1892 		0.0176389, 0.0176389, 0.0296333, 0.0274461, 0.0098072,
1893 		0.0117475, 0.0117475, 0.0137231, 0.0213783, 0.0088194,
1894 		0.0117475, 0.0088194, 0.0213783, 0.0176389, 0.0176389,
1895 		0.0176389, 0.0176389, 0.0176389, 0.0176389, 0.0176389,
1896 		0.0176389, 0.0176389, 0.0176389, 0.0088194, 0.0088194,
1897 		0.0213783, 0.0213783, 0.0213783, 0.0156633, 0.0263525,
1898 		0.0274461, 0.0215547, 0.0250119, 0.0273050, 0.0215547,
1899 		0.0196144, 0.0269169, 0.0293511, 0.0118886, 0.0117475,
1900 		0.0256117, 0.0215547, 0.0333728, 0.0293158, 0.0277283,
1901 		0.0213078, 0.0277283, 0.0235656, 0.0185208, 0.0216253,
1902 		0.0274461, 0.0254706, 0.0352778, 0.0235303, 0.0235303,
1903 		0.0235303, 0.0117475, 0.0213783, 0.0117475, 0.0213783,
1904 		0.0176389, 0.0098072, 0.0176389, 0.0195086, 0.0156633,
1905 		0.0215547, 0.0168981, 0.0117475, 0.0196144, 0.0205317,
1906 		0.0102658, 0.0082550, 0.0196144, 0.0102658, 0.0311503,
1907 		0.0205317, 0.0192617, 0.0212019, 0.0197556, 0.0139347,
1908 		0.0149578, 0.0115006, 0.0212725, 0.0199319, 0.0294217,
1909 		0.0182033, 0.0196144, 0.0176389, 0.0117475, 0.0213783,
1910 		0.0117475, 0.0213783, 0.0000000
1911 	}
1912 };
1913 // Created by Perl script get_font_metrics.pl
1914 struct font_metric PalatinoItalic = {
1915     0.017004, // XHeight
1916     0.024412, // CapHeight
1917     0.025859, // Ascender
1918     -0.009737, // Descender
1919     { // Widths of first 128 characters
1920         0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1921         0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1922         0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1923         0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1924         0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1925         0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1926         0.0000000, 0.0000000, 0.0088194, 0.0117475, 0.0176389,
1927         0.0176389, 0.0176389, 0.0313619, 0.0274461, 0.0098072,
1928         0.0117475, 0.0117475, 0.0137231, 0.0213783, 0.0088194,
1929         0.0117475, 0.0088194, 0.0104422, 0.0176389, 0.0176389,
1930         0.0176389, 0.0176389, 0.0176389, 0.0176389, 0.0176389,
1931         0.0176389, 0.0176389, 0.0176389, 0.0088194, 0.0088194,
1932         0.0213783, 0.0213783, 0.0213783, 0.0176389, 0.0263525,
1933         0.0254706, 0.0215547, 0.0235303, 0.0274461, 0.0215547,
1934         0.0196144, 0.0254706, 0.0274461, 0.0117475, 0.0117475,
1935         0.0235303, 0.0196144, 0.0333022, 0.0274461, 0.0274461,
1936         0.0215547, 0.0274461, 0.0235303, 0.0196144, 0.0215547,
1937         0.0274461, 0.0254706, 0.0333022, 0.0254706, 0.0235303,
1938         0.0235303, 0.0117475, 0.0213783, 0.0117475, 0.0213783,
1939         0.0176389, 0.0098072, 0.0156633, 0.0163336, 0.0143581,
1940         0.0176389, 0.0137231, 0.0098072, 0.0176389, 0.0176389,
1941         0.0098072, 0.0098072, 0.0156633, 0.0098072, 0.0274461,
1942         0.0196144, 0.0156633, 0.0176389, 0.0163336, 0.0137231,
1943         0.0137231, 0.0117475, 0.0196144, 0.0176389, 0.0254706,
1944         0.0176389, 0.0176389, 0.0156633, 0.0117475, 0.0213783,
1945         0.0117475, 0.0213783, 0.0000000
1946     }
1947 };
1948 
1949 
1950 
1951 // Created by Perl script get_font_metrics.pl
1952 struct font_metric PalatinoBold = {
1953     0.016616, // XHeight
1954     0.024024, // CapHeight
1955     0.025400, // Ascender
1956     -0.009102, // Descender
1957     { // Widths of first 128 characters
1958         0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1959         0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1960         0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1961         0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1962         0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1963         0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
1964         0.0000000, 0.0000000, 0.0088194, 0.0098072, 0.0141817,
1965         0.0176389, 0.0176389, 0.0313619, 0.0293864, 0.0098072,
1966         0.0117475, 0.0117475, 0.0156633, 0.0213783, 0.0088194,
1967         0.0117475, 0.0088194, 0.0104422, 0.0176389, 0.0176389,
1968         0.0176389, 0.0176389, 0.0176389, 0.0176389, 0.0176389,
1969         0.0176389, 0.0176389, 0.0176389, 0.0088194, 0.0088194,
1970         0.0213783, 0.0213783, 0.0213783, 0.0156633, 0.0263525,
1971         0.0274461, 0.0235303, 0.0254706, 0.0293864, 0.0215547,
1972         0.0196144, 0.0293864, 0.0293864, 0.0137231, 0.0137231,
1973         0.0274461, 0.0215547, 0.0352778, 0.0293864, 0.0293864,
1974         0.0215547, 0.0293864, 0.0254706, 0.0215547, 0.0235303,
1975         0.0274461, 0.0274461, 0.0352778, 0.0235303, 0.0235303,
1976         0.0235303, 0.0117475, 0.0213783, 0.0117475, 0.0213783,
1977         0.0176389, 0.0098072, 0.0176389, 0.0215547, 0.0156633,
1978         0.0215547, 0.0176389, 0.0137231, 0.0196144, 0.0215547,
1979         0.0117475, 0.0117475, 0.0215547, 0.0117475, 0.0313619,
1980         0.0215547, 0.0196144, 0.0215547, 0.0215547, 0.0137231,
1981         0.0156633, 0.0117475, 0.0215547, 0.0196144, 0.0293864,
1982         0.0176389, 0.0196144, 0.0176389, 0.0109361, 0.0213783,
1983         0.0109361, 0.0213783, 0.0000000
1984     }
1985 };
1986 
1987 
1988 //
1989 // Following font metric generated by `get_font_metrics'
1990 // perlscript from Font Metric file `/usr/openwin/lib/X11/fonts/F3/afm/Symbol.afm'.
1991 // All measurement in centimetres, given a pointsize of 1.0
1992 //
1993 struct font_metric Symbol = {
1994 	0.000000,	// XHeight
1995 	0.000000,	// CapHeight
1996 	0.000000,	// Ascender
1997 	0.000000,	// Descender
1998 	{	// Widths of first 128 characters
1999 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
2000 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
2001 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
2002 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
2003 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
2004 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
2005 		0.0000000, 0.0000000, 0.0088194, 0.0117475, 0.0251531,
2006 		0.0176389, 0.0193675, 0.0293864, 0.0274461, 0.0154869,
2007 		0.0117475, 0.0117475, 0.0176389, 0.0193675, 0.0088194,
2008 		0.0193675, 0.0088194, 0.0098072, 0.0176389, 0.0176389,
2009 		0.0176389, 0.0176389, 0.0176389, 0.0176389, 0.0176389,
2010 		0.0176389, 0.0176389, 0.0176389, 0.0098072, 0.0098072,
2011 		0.0193675, 0.0193675, 0.0193675, 0.0156633, 0.0193675,
2012 		0.0254706, 0.0235303, 0.0254706, 0.0215900, 0.0215547,
2013 		0.0269169, 0.0212725, 0.0254706, 0.0117475, 0.0222603,
2014 		0.0254706, 0.0242006, 0.0313619, 0.0254706, 0.0254706,
2015 		0.0270933, 0.0261408, 0.0196144, 0.0208844, 0.0215547,
2016 		0.0243417, 0.0154869, 0.0270933, 0.0227542, 0.0280458,
2017 		0.0215547, 0.0117475, 0.0304447, 0.0117475, 0.0232128,
2018 		0.0176389, 0.0176389, 0.0222603, 0.0193675, 0.0193675,
2019 		0.0174272, 0.0154869, 0.0183797, 0.0144992, 0.0212725,
2020 		0.0116064, 0.0212725, 0.0193675, 0.0193675, 0.0203200,
2021 		0.0183797, 0.0193675, 0.0193675, 0.0183797, 0.0193675,
2022 		0.0212725, 0.0154869, 0.0203200, 0.0251531, 0.0242006,
2023 		0.0173919, 0.0242006, 0.0174272, 0.0169333, 0.0070556,
2024 		0.0169333, 0.0193675, 0.0000000
2025 	}
2026 };
2027 //
2028 // Following font metric generated by `get_font_metrics'
2029 // perlscript from Font Metric file `/usr/openwin/lib/X11/fonts/F3/afm/Times-Roman.afm'.
2030 // All measurement in centimetres, given a pointsize of 1.0
2031 //
2032 struct font_metric TimesRoman = {
2033 	0.015875,	// XHeight
2034 	0.023354,	// CapHeight
2035 	0.024095,	// Ascender
2036 	-0.007655,	// Descender
2037 	{	// Widths of first 128 characters
2038 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
2039 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
2040 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
2041 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
2042 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
2043 		0.0000000, 0.0000000, 0.0000000, 0.0000000, 0.0000000,
2044 		0.0000000, 0.0000000, 0.0088194, 0.0117475, 0.0143933,
2045 		0.0176389, 0.0176389, 0.0293864, 0.0274461, 0.0117475,
2046 		0.0117475, 0.0117475, 0.0176389, 0.0198967, 0.0088194,
2047 		0.0117475, 0.0088194, 0.0098072, 0.0176389, 0.0176389,
2048 		0.0176389, 0.0176389, 0.0176389, 0.0176389, 0.0176389,
2049 		0.0176389, 0.0176389, 0.0176389, 0.0098072, 0.0098072,
2050 		0.0198967, 0.0198967, 0.0198967, 0.0156633, 0.0324908,
2051 		0.0254706, 0.0235303, 0.0235303, 0.0254706, 0.0215547,
2052 		0.0196144, 0.0254706, 0.0254706, 0.0117475, 0.0137231,
2053 		0.0254706, 0.0215547, 0.0313619, 0.0254706, 0.0254706,
2054 		0.0196144, 0.0254706, 0.0235303, 0.0196144, 0.0215547,
2055 		0.0254706, 0.0254706, 0.0333022, 0.0254706, 0.0254706,
2056 		0.0215547, 0.0117475, 0.0098072, 0.0117475, 0.0165453,
2057 		0.0176389, 0.0117475, 0.0156633, 0.0176389, 0.0156633,
2058 		0.0176389, 0.0156633, 0.0117475, 0.0176389, 0.0176389,
2059 		0.0098072, 0.0098072, 0.0176389, 0.0098072, 0.0274461,
2060 		0.0176389, 0.0176389, 0.0176389, 0.0176389, 0.0117475,
2061 		0.0137231, 0.0098072, 0.0176389, 0.0176389, 0.0254706,
2062 		0.0176389, 0.0176389, 0.0156633, 0.0169333, 0.0070556,
2063 		0.0169333, 0.0190853, 0.0000000
2064 	}
2065 };
2066 
2067 /*
2068  * gr_charwidth_cm(char c, int font, double fontsize_pt)
2069  *
2070  * RETURN VALUE the width of the character, in centimetres
2071  *
2072  * Font info created by the `get_font_metrics' perlscript, in the Gri src
2073  * directory.  This looks in the OpenWindows font metrics files to figure the
2074  * pertintent stuff out.  (You might have to edit this for different
2075  * machines).
2076  */
2077 static double
gr_charwidth_cm(int c,int font,double fontsize_pt)2078 gr_charwidth_cm(int c, int font, double fontsize_pt)
2079 {
2080 	unsigned char   i = (int) c;
2081 	if (i > 127)
2082 		return fontsize_pt * 0.0211663;	/* error, but guess Courier size
2083 						 * anyway */
2084 	switch (font) {
2085 	case gr_font_TimesRoman:
2086 		return fontsize_pt * TimesRoman.width[i];
2087 	case gr_font_Courier:
2088 		return fontsize_pt * 0.0211663;	/* Courier has fixed width */
2089 	case gr_font_Symbol:
2090 		return fontsize_pt * Symbol.width[i];
2091 	case gr_font_Helvetica:
2092 		return fontsize_pt * Helvetica.width[i];
2093 	case gr_font_HelveticaBold:
2094 		return fontsize_pt * Helvetica_Bold.width[i];
2095 	case gr_font_PalatinoRoman:
2096 		return fontsize_pt * PalatinoRoman.width[i];
2097 	case gr_font_Century:
2098                 return fontsize_pt * CenturyRoman.width[i];
2099 	default:
2100 		break;
2101 		/* Guess similar size to Helvetica */
2102 	}
2103 	return (fontsize_pt * Helvetica.width[i]);
2104 }
2105 
2106 double
gr_current_descender()2107 gr_current_descender()		// descender, in positive cm
2108 {
2109 	switch (CurrentFont.id) {
2110 	case gr_font_Courier:
2111 		return (-CurrentFont.size_pt * Courier.Descender);
2112 	case gr_font_Helvetica:
2113 		return(-CurrentFont.size_pt * Helvetica.Descender);
2114 	case gr_font_Symbol:
2115 		return(-CurrentFont.size_pt * Symbol.Descender);
2116 	case gr_font_TimesRoman:
2117 		return(-CurrentFont.size_pt * TimesRoman.Descender);
2118 	case gr_font_Century:
2119 		return(-CurrentFont.size_pt * CenturyRoman.Descender);
2120 	case gr_font_PalatinoRoman:
2121 		return(-CurrentFont.size_pt * PalatinoRoman.Descender);
2122 	case gr_font_PalatinoItalic:
2123 		return(-CurrentFont.size_pt * PalatinoItalic.Descender);
2124 	case gr_font_PalatinoBold:
2125 		return(-CurrentFont.size_pt * PalatinoBold.Descender);
2126 	default:
2127 		break;
2128 	}
2129 	return CurrentFont.size_pt * 0.025329; // Guess similar to Helvetica
2130 }
2131 
2132 
2133 // gr_currentCapHeight_cm() -- find height of capital characters
2134 double
gr_currentCapHeight_cm()2135 gr_currentCapHeight_cm()
2136 {
2137 	double          height_cm = 0.0;
2138 	switch (CurrentFont.id) {
2139 	case gr_font_Courier:
2140 		height_cm = (CurrentFont.size_pt * Courier.CapHeight);
2141 		break;
2142 	case gr_font_Helvetica:
2143 		height_cm = (CurrentFont.size_pt * Helvetica.CapHeight);
2144 		break;
2145 	case gr_font_Symbol:
2146 		height_cm = (CurrentFont.size_pt * Symbol.CapHeight);
2147 		break;
2148 	case gr_font_TimesRoman:
2149 		height_cm = (CurrentFont.size_pt * TimesRoman.CapHeight);
2150 		break;
2151 	case gr_font_Century:
2152 	        height_cm = (CurrentFont.size_pt * CenturyRoman.CapHeight);
2153 		break;
2154 	case gr_font_PalatinoRoman:
2155 	        height_cm = (CurrentFont.size_pt * PalatinoRoman.CapHeight);
2156 		break;
2157 	case gr_font_PalatinoItalic:
2158 	        height_cm = (CurrentFont.size_pt * PalatinoItalic.CapHeight);
2159 		break;
2160 	case gr_font_PalatinoBold:
2161 	        height_cm = (CurrentFont.size_pt * PalatinoBold.CapHeight);
2162 		break;
2163 	default:
2164 		/*
2165 		 * Don't know this font.
2166 		 */
2167 		break;
2168 	}
2169 	if (height_cm > 0.0)
2170 		return height_cm;
2171 	else
2172 		return CurrentFont.size_pt * 0.025329;	/* Guess Helvetica */
2173 }
2174