]> git.lyx.org Git - lyx.git/blob - src/mathed/MathSupport.cpp
Implement computation of spacing according to the TeXBook
[lyx.git] / src / mathed / MathSupport.cpp
1 /**
2  * \file MathSupport.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author André Pönitz
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "MathSupport.h"
15
16 #include "InsetMathFont.h"
17 #include "InsetMathSymbol.h"
18 #include "MathData.h"
19 #include "MathParser.h"
20 #include "MathStream.h"
21
22 #include "MetricsInfo.h"
23
24 #include "frontends/FontLoader.h"
25 #include "frontends/FontMetrics.h"
26 #include "frontends/Painter.h"
27
28 #include "support/debug.h"
29 #include "support/docstream.h"
30 #include "support/lassert.h"
31 #include "support/lyxlib.h"
32
33 #include <map>
34 #include <algorithm>
35
36 using namespace std;
37
38 namespace lyx {
39
40 using frontend::Painter;
41
42
43 ///
44 class Matrix {
45 public:
46         ///
47         Matrix(int, double, double);
48         ///
49         void transform(double &, double &);
50 private:
51         ///
52         double m_[2][2];
53 };
54
55
56 Matrix::Matrix(int code, double x, double y)
57 {
58         double const cs = (code & 1) ? 0 : (1 - code);
59         double const sn = (code & 1) ? (2 - code) : 0;
60         m_[0][0] =  cs * x;
61         m_[0][1] =  sn * x;
62         m_[1][0] = -sn * y;
63         m_[1][1] =  cs * y;
64 }
65
66
67 void Matrix::transform(double & x, double & y)
68 {
69         double xx = m_[0][0] * x + m_[0][1] * y;
70         double yy = m_[1][0] * x + m_[1][1] * y;
71         x = xx;
72         y = yy;
73 }
74
75
76
77 namespace {
78
79 /*
80  * Internal struct of a drawing: code n x1 y1 ... xn yn, where code is:
81  * 0 = end, 1 = line, 2 = polyline, 3 = square line, 4 = square polyline
82  */
83
84
85 double const parenthHigh[] = {
86         2, 13,
87         0.9840, 0.0014, 0.7143, 0.0323, 0.4603, 0.0772,
88         0.2540, 0.1278, 0.1746, 0.1966, 0.0952, 0.3300,
89         0.0950, 0.5000, 0.0952, 0.6700, 0.1746, 0.8034,
90         0.2540, 0.8722, 0.4603, 0.9228, 0.7143, 0.9677,
91         0.9840, 0.9986,
92         0
93 };
94
95
96 double const parenth[] = {
97         2, 13,
98         0.9930, 0.0071, 0.7324, 0.0578, 0.5141, 0.1126,
99         0.3380, 0.1714, 0.2183, 0.2333, 0.0634, 0.3621,
100         0.0141, 0.5000, 0.0563, 0.6369, 0.2113, 0.7647,
101         0.3310, 0.8276, 0.5070, 0.8864, 0.7254, 0.9412,
102         0.9930, 0.9919,
103         0
104 };
105
106
107 double const brace[] = {
108         2, 21,
109         0.9492, 0.0020, 0.9379, 0.0020, 0.7458, 0.0243,
110         0.5819, 0.0527, 0.4859, 0.0892, 0.4463, 0.1278,
111         0.4463, 0.3732, 0.4011, 0.4199, 0.2712, 0.4615,
112         0.0734, 0.4919, 0.0113, 0.5000, 0.0734, 0.5081,
113         0.2712, 0.5385, 0.4011, 0.5801, 0.4463, 0.6268,
114         0.4463, 0.8722, 0.4859, 0.9108, 0.5819, 0.9473,
115         0.7458, 0.9757, 0.9379, 0.9980, 0.9492, 0.9980,
116         0
117 };
118
119
120 double const mapsto[] = {
121         2, 3,
122         0.75, 0.015, 0.95, 0.5, 0.75, 0.985,
123         1, 0.015, 0.475, 0.945, 0.475,
124         1, 0.015, 0.015, 0.015, 0.985,
125         0
126 };
127
128
129 double const lhook[] = {
130         2, 3,
131         0.25, 0.015, 0.05, 0.5, 0.25, 0.985,
132         1, 0.015, 0.475, 0.7, 0.475,
133         2, 5,
134         0.7, 0.015, 0.825, 0.15, 0.985, 0.25,
135         0.825, 0.35, 0.7, 0.475,
136         0
137 };
138
139
140 double const rhook[] = {
141         2, 3,
142         0.75, 0.015, 0.95, 0.5, 0.75, 0.985,
143         1, 0.3, 0.475, 0.985, 0.475,
144         2, 5,
145         0.3, 0.015, 0.175, 0.15, 0.05, 0.25,
146         0.175, 0.35, 0.3, 0.475,
147         0
148 };
149
150
151 double const LRArrow[] = {
152         2, 3,
153         0.25, 0.015, 0.05, 0.5, 0.25, 0.985,
154         2, 3,
155         0.75, 0.015, 0.95, 0.5, 0.75, 0.985,
156         1, 0.2, 0.8, 0.8, 0.8,
157         1, 0.2, 0.2, 0.8, 0.2,
158         0
159 };
160
161
162 double const LArrow[] = {
163         2, 3,
164         0.25, 0.015, 0.05, 0.5, 0.25, 0.985,
165         1, 0.2, 0.8, 0.985, 0.8,
166         1, 0.2, 0.2, 0.985, 0.2,
167         0
168 };
169
170
171 double const lharpoondown[] = {
172         2, 2,
173         0.015, 0.5, 0.25, 0.985,
174         1, 0.02, 0.475, 0.985, 0.475,
175         0
176 };
177
178
179 double const lharpoonup[] = {
180         2, 2,
181         0.25, 0.015, 0.015, 0.5,
182         1, 0.02, 0.525, 0.985, 0.525,
183         0
184 };
185
186
187 double const lrharpoons[] = {
188         2, 2,
189         0.25, 0.015, 0.015, 0.225,
190         1, 0.02, 0.23, 0.985, 0.23,
191         2, 2,
192         0.75, 0.985, 0.985, 0.775,
193         1, 0.02, 0.7, 0.980, 0.7,
194         0
195 };
196
197
198 double const rlharpoons[] = {
199         2, 2,
200         0.75, 0.015, 0.985, 0.225,
201         1, 0.02, 0.23, 0.985, 0.23,
202         2, 2,
203         0.25, 0.985, 0.015, 0.775,
204         1, 0.02, 0.7, 0.980, 0.7,
205         0
206 };
207
208
209 double const arrow[] = {
210         4, 7,
211         0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
212         0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
213         0.9500, 0.7500,
214         3, 0.5000, 0.1500, 0.5000, 0.9500,
215         0
216 };
217
218
219 double const Arrow[] = {
220         4, 7,
221         0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
222         0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
223         0.9500, 0.7500,
224         3, 0.3500, 0.5000, 0.3500, 0.9500,
225         3, 0.6500, 0.5000, 0.6500, 0.9500,
226         0
227 };
228
229
230 double const udarrow[] = {
231         2, 3,
232         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
233         2, 3,
234         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,
235         1, 0.5, 0.1,  0.5, 0.9,
236         0
237 };
238
239
240 double const Udarrow[] = {
241         2, 3,
242         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
243         2, 3,
244         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,
245         1, 0.35, 0.2, 0.35, 0.8,
246         1, 0.65, 0.2, 0.65, 0.8,
247         0
248 };
249
250
251 double const brack[] = {
252         2, 4,
253         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,  0.95, 0.95,
254         0
255 };
256
257
258 double const dbrack[] = {
259         2, 4,
260         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,  0.95, 0.95,
261         2, 2,
262         0.50, 0.05,  0.50, 0.95,
263         0
264 };
265
266
267 double const corner[] = {
268         2, 3,
269         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,
270         0
271 };
272
273
274 double const angle[] = {
275         2, 3,
276         1, 0,  0.05, 0.5,  1, 1,
277         0
278 };
279
280
281 double const slash[] = {
282         1, 0.95, 0.05, 0.05, 0.95,
283         0
284 };
285
286
287 double const hline[] = {
288         1, 0.00, 0.5, 1.0, 0.5,
289         0
290 };
291
292
293 double const ddot[] = {
294         1, 0.2, 0.5, 0.3, 0.5,
295         1, 0.7, 0.5, 0.8, 0.5,
296         0
297 };
298
299
300 double const dddot[] = {
301         1, 0.1,  0.5, 0.2,  0.5,
302         1, 0.45, 0.5, 0.55, 0.5,
303         1, 0.8,  0.5, 0.9,  0.5,
304         0
305 };
306
307
308 double const ddddot[] = {
309         1, 0.1,  0.5, 0.2,  0.5,
310         1, 0.45, 0.5, 0.55, 0.5,
311         1, 0.8,  0.5, 0.9,  0.5,
312         1, 1.15, 0.5, 1.25, 0.5,
313         0
314 };
315
316
317 double const hline3[] = {
318         1, 0.1,   0,  0.15,  0,
319         1, 0.475, 0,  0.525, 0,
320         1, 0.85,  0,  0.9,   0,
321         0
322 };
323
324
325 double const dline3[] = {
326         1, 0.1,   0.1,   0.15,  0.15,
327         1, 0.475, 0.475, 0.525, 0.525,
328         1, 0.85,  0.85,  0.9,   0.9,
329         0
330 };
331
332
333 double const hlinesmall[] = {
334         1, 0.4, 0.5, 0.6, 0.5,
335         0
336 };
337
338
339 double const ring[] = {
340         2, 5,
341         0.5, 0.8,  0.8, 0.5,  0.5, 0.2,  0.2, 0.5,  0.5, 0.8,
342         0
343 };
344
345
346 double const vert[] = {
347         1, 0.5, 0.05,  0.5, 0.95,
348         0
349 };
350
351
352 double const  Vert[] = {
353         1, 0.3, 0.05,  0.3, 0.95,
354         1, 0.7, 0.05,  0.7, 0.95,
355         0
356 };
357
358
359 double const tilde[] = {
360         2, 4,
361         0.00, 0.8,  0.25, 0.2,  0.75, 0.8,  1.00, 0.2,
362         0
363 };
364
365
366 struct deco_struct {
367         double const * data;
368         int angle;
369 };
370
371 struct named_deco_struct {
372         char const * name;
373         double const * data;
374         int angle;
375 };
376
377 named_deco_struct deco_table[] = {
378         // Decorations
379         {"widehat",             angle,        3 },
380         {"widetilde",           tilde,        0 },
381         {"underbar",            hline,        0 },
382         {"underline",           hline,        0 },
383         {"overline",            hline,        0 },
384         {"underbrace",          brace,        1 },
385         {"overbrace",           brace,        3 },
386         {"overleftarrow",       arrow,        1 },
387         {"overrightarrow",      arrow,        3 },
388         {"overleftrightarrow",  udarrow,      1 },
389         {"xhookleftarrow",      lhook,        0 },
390         {"xhookrightarrow",     rhook,        0 },
391         {"xleftarrow",          arrow,        1 },
392         {"xLeftarrow",          LArrow,       0 },
393         {"xleftharpoondown",    lharpoondown, 0 },
394         {"xleftharpoonup",      lharpoonup,   0 },
395         {"xleftrightharpoons",  lrharpoons,   0 },
396         {"xleftrightarrow",     udarrow,      1 },
397         {"xLeftrightarrow",     LRArrow,      0 },
398         {"xmapsto",             mapsto,       0 },
399         {"xrightarrow",         arrow,        3 },
400         {"xRightarrow",         LArrow,       2 },
401         {"xrightharpoondown",   lharpoonup,   2 },
402         {"xrightharpoonup",     lharpoondown, 2 },
403         {"xrightleftharpoons",  rlharpoons,   0 },
404         {"underleftarrow",      arrow,        1 },
405         {"underrightarrow",     arrow,        3 },
406         {"underleftrightarrow", udarrow,      1 },
407         {"undertilde",          tilde,        0 },
408         {"utilde",              tilde,        0 },
409
410         // Delimiters
411         {"(",              parenth,    0 },
412         {")",              parenth,    2 },
413         {"{",              brace,      0 },
414         {"}",              brace,      2 },
415         {"lbrace",         brace,      0 },
416         {"rbrace",         brace,      2 },
417         {"[",              brack,      0 },
418         {"]",              brack,      2 },
419         {"llbracket",      dbrack,     0 },
420         {"rrbracket",      dbrack,     2 },
421         {"|",              vert,       0 },
422         {"/",              slash,      0 },
423         {"slash",          slash,      0 },
424         {"vert",           vert,       0 },
425         {"lvert",          vert,       0 },
426         {"rvert",          vert,       0 },
427         {"Vert",           Vert,       0 },
428         {"lVert",          Vert,       0 },
429         {"rVert",          Vert,       0 },
430         {"'",              slash,      1 },
431         {"<",              angle,      0 },
432         {">",              angle,      2 },
433         {"\\",             slash,      1 },
434         {"backslash",      slash,      1 },
435         {"langle",         angle,      0 },
436         {"lceil",          corner,     0 },
437         {"lfloor",         corner,     1 },
438         {"rangle",         angle,      2 },
439         {"rceil",          corner,     3 },
440         {"rfloor",         corner,     2 },
441         {"downarrow",      arrow,      2 },
442         {"Downarrow",      Arrow,      2 },
443         {"uparrow",        arrow,      0 },
444         {"Uparrow",        Arrow,      0 },
445         {"updownarrow",    udarrow,    0 },
446         {"Updownarrow",    Udarrow,    0 },
447
448         // Accents
449         {"ddot",           ddot,       0 },
450         {"dddot",          dddot,      0 },
451         {"ddddot",         ddddot,     0 },
452         {"hat",            angle,      3 },
453         {"grave",          slash,      1 },
454         {"acute",          slash,      0 },
455         {"tilde",          tilde,      0 },
456         {"bar",            hline,      0 },
457         {"dot",            hlinesmall, 0 },
458         {"check",          angle,      1 },
459         {"breve",          parenth,    1 },
460         {"vec",            arrow,      3 },
461         {"mathring",       ring,       0 },
462
463         // Dots
464         {"dots",           hline3,     0 },
465         {"ldots",          hline3,     0 },
466         {"cdots",          hline3,     0 },
467         {"vdots",          hline3,     1 },
468         {"ddots",          dline3,     0 },
469         {"adots",          dline3,     1 },
470         {"iddots",         dline3,     1 },
471         {"dotsb",          hline3,     0 },
472         {"dotsc",          hline3,     0 },
473         {"dotsi",          hline3,     0 },
474         {"dotsm",          hline3,     0 },
475         {"dotso",          hline3,     0 }
476 };
477
478
479 map<docstring, deco_struct> deco_list;
480
481 // sort the table on startup
482 class init_deco_table {
483 public:
484         init_deco_table() {
485                 unsigned const n = sizeof(deco_table) / sizeof(deco_table[0]);
486                 for (named_deco_struct * p = deco_table; p != deco_table + n; ++p) {
487                         deco_struct d;
488                         d.data  = p->data;
489                         d.angle = p->angle;
490                         deco_list[from_ascii(p->name)] = d;
491                 }
492         }
493 };
494
495 static init_deco_table dummy;
496
497
498 deco_struct const * search_deco(docstring const & name)
499 {
500         map<docstring, deco_struct>::const_iterator p = deco_list.find(name);
501         return p == deco_list.end() ? 0 : &(p->second);
502 }
503
504
505 } // namespace anon
506
507
508 int mathed_font_em(FontInfo const & font)
509 {
510         return theFontMetrics(font).em();
511 }
512
513 /* The math units. Quoting TeX by Topic, p.205:
514  *
515  * Spacing around mathematical objects is measured in mu units. A mu
516  * is 1/18th part of \fontdimen6 of the font in family 2 in the
517  * current style, the ‘quad’ value of the symbol font.
518  *
519  * A \thickmuskip (default value in plain TeX: 5mu plus 5mu) is
520  * inserted around (binary) relations, except where these are preceded
521  * or followed by other relations or punctuation, and except if they
522  * follow an open, or precede a close symbol.
523  *
524  * A \medmuskip (default value in plain TeX: 4mu plus 2mu minus 4mu)
525  * is put around binary operators.
526  *
527  * A \thinmuskip (default value in plain TeX: 3mu) follows after
528  * punctuation, and is put around inner objects, except where these
529  * are followed by a close or preceded by an open symbol, and except
530  * if the other object is a large operator or a binary relation.
531  *
532  * See the file MathClass.cpp for a formal implementation of the rules
533  * above.
534  */
535
536 int mathed_thinmuskip(FontInfo font)
537 {
538         font.setFamily(SYMBOL_FAMILY);
539         return support::iround(3.0 / 18 * theFontMetrics(font).em());
540 }
541
542
543 int mathed_medmuskip(FontInfo font)
544 {
545         font.setFamily(SYMBOL_FAMILY);
546         return support::iround(4.0 / 18 * theFontMetrics(font).em());
547 }
548
549
550 int mathed_thickmuskip(FontInfo font)
551 {
552         font.setFamily(SYMBOL_FAMILY);
553         return support::iround(5.0 / 18 * theFontMetrics(font).em());
554 }
555
556
557 int mathed_char_width(FontInfo const & font, char_type c)
558 {
559         return theFontMetrics(font).width(c);
560 }
561
562
563 int mathed_char_kerning(FontInfo const & font, char_type c)
564 {
565         frontend::FontMetrics const & fm = theFontMetrics(font);
566         return fm.rbearing(c) - fm.width(c);
567 }
568
569
570 void mathed_string_dim(FontInfo const & font,
571                        docstring const & s,
572                        Dimension & dim)
573 {
574         frontend::FontMetrics const & fm = theFontMetrics(font);
575         dim.asc = 0;
576         dim.des = 0;
577         for (docstring::const_iterator it = s.begin();
578              it != s.end();
579              ++it) {
580                 dim.asc = max(dim.asc, fm.ascent(*it));
581                 dim.des = max(dim.des, fm.descent(*it));
582         }
583         dim.wid = fm.width(s);
584 }
585
586
587 int mathed_string_width(FontInfo const & font, docstring const & s)
588 {
589         return theFontMetrics(font).width(s);
590 }
591
592
593 void mathed_draw_deco(PainterInfo & pi, int x, int y, int w, int h,
594         docstring const & name)
595 {
596         if (name == ".") {
597                 pi.pain.line(x + w/2, y, x + w/2, y + h,
598                           Color_cursor, Painter::line_onoffdash);
599                 return;
600         }
601
602         deco_struct const * mds = search_deco(name);
603         if (!mds) {
604                 lyxerr << "Deco was not found. Programming error?" << endl;
605                 lyxerr << "name: '" << to_utf8(name) << "'" << endl;
606                 return;
607         }
608
609         int const n = (w < h) ? w : h;
610         int const r = mds->angle;
611         double const * d = mds->data;
612
613         if (h > 70 && (name == "(" || name == ")"))
614                 d = parenthHigh;
615
616         Matrix mt(r, w, h);
617         Matrix sqmt(r, n, n);
618
619         if (r > 0 && r < 3)
620                 y += h;
621
622         if (r >= 2)
623                 x += w;
624
625         for (int i = 0; d[i]; ) {
626                 int code = int(d[i++]);
627                 if (code & 1) {  // code == 1 || code == 3
628                         double xx = d[i++];
629                         double yy = d[i++];
630                         double x2 = d[i++];
631                         double y2 = d[i++];
632                         if (code == 3)
633                                 sqmt.transform(xx, yy);
634                         else
635                                 mt.transform(xx, yy);
636                         mt.transform(x2, y2);
637                         pi.pain.line(
638                                 int(x + xx + 0.5), int(y + yy + 0.5),
639                                 int(x + x2 + 0.5), int(y + y2 + 0.5),
640                                 pi.base.font.color());
641                 } else {
642                         int xp[32];
643                         int yp[32];
644                         int const n = int(d[i++]);
645                         for (int j = 0; j < n; ++j) {
646                                 double xx = d[i++];
647                                 double yy = d[i++];
648 //           lyxerr << ' ' << xx << ' ' << yy << ' ';
649                                 if (code == 4)
650                                         sqmt.transform(xx, yy);
651                                 else
652                                         mt.transform(xx, yy);
653                                 xp[j] = int(x + xx + 0.5);
654                                 yp[j] = int(y + yy + 0.5);
655                                 //  lyxerr << "P[" << j ' ' << xx << ' ' << yy << ' ' << x << ' ' << y << ']';
656                         }
657                         pi.pain.lines(xp, yp, n, pi.base.font.color());
658                 }
659         }
660 }
661
662
663 void mathedSymbolDim(MetricsInfo & mi, Dimension & dim, latexkeys const * sym)
664 {
665         LASSERT((bool)sym, return);
666         //lyxerr << "metrics: symbol: '" << sym->name
667         //      << "' in font: '" << sym->inset
668         //      << "' drawn as: '" << sym->draw
669         //      << "'" << endl;
670
671         bool const italic_upcase_greek = sym->inset == "cmr" &&
672                 sym->extra == "mathalpha" &&
673                 mi.base.fontname == "mathit";
674         std::string const font = italic_upcase_greek ? "cmm" : sym->inset;
675         Changer dummy = mi.base.changeFontSet(font);
676         mathed_string_dim(mi.base.font, sym->draw, dim);
677 }
678
679
680 void mathedSymbolDraw(PainterInfo & pi, int x, int y, latexkeys const * sym)
681 {
682         LASSERT((bool)sym, return);
683         //lyxerr << "drawing: symbol: '" << sym->name
684         //      << "' in font: '" << sym->inset
685         //      << "' drawn as: '" << sym->draw
686         //      << "'" << endl;
687
688         bool const italic_upcase_greek = sym->inset == "cmr" &&
689                 sym->extra == "mathalpha" &&
690                 pi.base.fontname == "mathit";
691         std::string const font = italic_upcase_greek ? "cmm" : sym->inset;
692
693         Changer dummy = pi.base.changeFontSet(font);
694         pi.draw(x, y, sym->draw);
695 }
696
697
698 void metricsStrRedBlack(MetricsInfo & mi, Dimension & dim, docstring const & str)
699 {
700         FontInfo font = mi.base.font;
701         augmentFont(font, "mathnormal");
702         mathed_string_dim(font, str, dim);
703 }
704
705
706 void drawStrRed(PainterInfo & pi, int x, int y, docstring const & str)
707 {
708         FontInfo f = pi.base.font;
709         augmentFont(f, "mathnormal");
710         f.setColor(Color_latex);
711         pi.pain.text(x, y, str, f);
712 }
713
714
715 void drawStrBlack(PainterInfo & pi, int x, int y, docstring const & str)
716 {
717         FontInfo f = pi.base.font;
718         augmentFont(f, "mathnormal");
719         f.setColor(Color_foreground);
720         pi.pain.text(x, y, str, f);
721 }
722
723
724 void math_font_max_dim(FontInfo const & font, int & asc, int & des)
725 {
726         frontend::FontMetrics const & fm = theFontMetrics(font);
727         asc = fm.maxAscent();
728         des = fm.maxDescent();
729 }
730
731
732 struct fontinfo {
733         string cmd_;
734         FontFamily family_;
735         FontSeries series_;
736         FontShape  shape_;
737         ColorCode        color_;
738 };
739
740
741 FontFamily const inh_family = INHERIT_FAMILY;
742 FontSeries const inh_series = INHERIT_SERIES;
743 FontShape  const inh_shape  = INHERIT_SHAPE;
744
745
746 // mathnormal should be the first, otherwise the fallback further down
747 // does not work
748 fontinfo fontinfos[] = {
749         // math fonts
750         // Color_math determines which fonts are math (see isMathFont)
751         {"mathnormal",    ROMAN_FAMILY, MEDIUM_SERIES,
752                           ITALIC_SHAPE, Color_math},
753         {"mathbf",        inh_family, BOLD_SERIES,
754                           inh_shape, Color_math},
755         {"mathcal",       CMSY_FAMILY, inh_series,
756                           inh_shape, Color_math},
757         {"mathfrak",      EUFRAK_FAMILY, inh_series,
758                           inh_shape, Color_math},
759         {"mathrm",        ROMAN_FAMILY, inh_series,
760                           UP_SHAPE, Color_math},
761         {"mathsf",        SANS_FAMILY, inh_series,
762                           inh_shape, Color_math},
763         {"mathbb",        MSB_FAMILY, inh_series,
764                           inh_shape, Color_math},
765         {"mathtt",        TYPEWRITER_FAMILY, inh_series,
766                           inh_shape, Color_math},
767         {"mathit",        inh_family, inh_series,
768                           ITALIC_SHAPE, Color_math},
769         {"mathscr",       RSFS_FAMILY, inh_series,
770                           inh_shape, Color_math},
771         {"cmex",          CMEX_FAMILY, inh_series,
772                           inh_shape, Color_math},
773         {"cmm",           CMM_FAMILY, inh_series,
774                           inh_shape, Color_math},
775         {"cmr",           CMR_FAMILY, inh_series,
776                           inh_shape, Color_math},
777         {"cmsy",          CMSY_FAMILY, inh_series,
778                           inh_shape, Color_math},
779         {"eufrak",        EUFRAK_FAMILY, inh_series,
780                           inh_shape, Color_math},
781         {"msa",           MSA_FAMILY, inh_series,
782                           inh_shape, Color_math},
783         {"msb",           MSB_FAMILY, inh_series,
784                           inh_shape, Color_math},
785         {"stmry",         STMARY_FAMILY, inh_series,
786                           inh_shape, Color_math},
787         {"wasy",          WASY_FAMILY, inh_series,
788                           inh_shape, Color_math},
789         {"esint",         ESINT_FAMILY, inh_series,
790                           inh_shape, Color_math},
791
792         // Text fonts
793         {"text",          inh_family, inh_series,
794                           inh_shape, Color_foreground},
795         {"textbf",        inh_family, BOLD_SERIES,
796                           inh_shape, Color_foreground},
797         {"textit",        inh_family, inh_series,
798                           ITALIC_SHAPE, Color_foreground},
799         {"textmd",        inh_family, MEDIUM_SERIES,
800                           inh_shape, Color_foreground},
801         {"textnormal",    inh_family, inh_series,
802                           UP_SHAPE, Color_foreground},
803         {"textrm",        ROMAN_FAMILY,
804                           inh_series, UP_SHAPE,Color_foreground},
805         {"textsc",        inh_family, inh_series,
806                           SMALLCAPS_SHAPE, Color_foreground},
807         {"textsf",        SANS_FAMILY, inh_series,
808                           inh_shape, Color_foreground},
809         {"textsl",        inh_family, inh_series,
810                           SLANTED_SHAPE, Color_foreground},
811         {"texttt",        TYPEWRITER_FAMILY, inh_series,
812                           inh_shape, Color_foreground},
813         {"textup",        inh_family, inh_series,
814                           UP_SHAPE, Color_foreground},
815
816         // TIPA support
817         {"textipa",       inh_family, inh_series,
818                           inh_shape, Color_foreground},
819
820         // mhchem support
821         {"ce",            inh_family, inh_series,
822                           inh_shape, Color_foreground},
823         {"cf",            inh_family, inh_series,
824                           inh_shape, Color_foreground},
825
826         // LyX internal usage
827         {"lyxtex",        inh_family, inh_series,
828                           UP_SHAPE, Color_latex},
829         // FIXME: The following two don't work on OS X, since the Symbol font
830         //        uses a different encoding, and is therefore disabled in
831         //        FontLoader::available().
832         {"lyxsymbol",     SYMBOL_FAMILY, inh_series,
833                           inh_shape, Color_math},
834         {"lyxboldsymbol", SYMBOL_FAMILY, BOLD_SERIES,
835                           inh_shape, Color_math},
836         {"lyxblacktext",  ROMAN_FAMILY, MEDIUM_SERIES,
837                           UP_SHAPE, Color_foreground},
838         {"lyxnochange",   inh_family, inh_series,
839                           inh_shape, Color_foreground},
840         {"lyxfakebb",     TYPEWRITER_FAMILY, BOLD_SERIES,
841                           UP_SHAPE, Color_math},
842         {"lyxfakecal",    SANS_FAMILY, MEDIUM_SERIES,
843                           ITALIC_SHAPE, Color_math},
844         {"lyxfakefrak",   ROMAN_FAMILY, BOLD_SERIES,
845                           ITALIC_SHAPE, Color_math}
846 };
847
848
849 fontinfo * lookupFont(string const & name)
850 {
851         //lyxerr << "searching font '" << name << "'" << endl;
852         int const n = sizeof(fontinfos) / sizeof(fontinfo);
853         for (int i = 0; i < n; ++i)
854                 if (fontinfos[i].cmd_ == name) {
855                         //lyxerr << "found '" << i << "'" << endl;
856                         return fontinfos + i;
857                 }
858         return 0;
859 }
860
861
862 fontinfo * searchFont(string const & name)
863 {
864         fontinfo * f = lookupFont(name);
865         return f ? f : fontinfos;
866         // this should be mathnormal
867         //return searchFont("mathnormal");
868 }
869
870
871 bool isFontName(string const & name)
872 {
873         return lookupFont(name);
874 }
875
876
877 bool isMathFont(string const & name)
878 {
879         fontinfo * f = lookupFont(name);
880         return f && f->color_ == Color_math;
881 }
882
883
884 bool isTextFont(string const & name)
885 {
886         fontinfo * f = lookupFont(name);
887         return f && f->color_ == Color_foreground;
888 }
889
890
891 FontInfo getFont(string const & name)
892 {
893         FontInfo font;
894         augmentFont(font, name);
895         return font;
896 }
897
898
899 void fakeFont(string const & orig, string const & fake)
900 {
901         fontinfo * forig = searchFont(orig);
902         fontinfo * ffake = searchFont(fake);
903         if (forig && ffake) {
904                 forig->family_ = ffake->family_;
905                 forig->series_ = ffake->series_;
906                 forig->shape_  = ffake->shape_;
907                 forig->color_  = ffake->color_;
908         } else {
909                 lyxerr << "Can't fake font '" << orig << "' with '"
910                        << fake << "'" << endl;
911         }
912 }
913
914
915 void augmentFont(FontInfo & font, string const & name)
916 {
917         static bool initialized = false;
918         if (!initialized) {
919                 initialized = true;
920                 // fake fonts if necessary
921                 if (!theFontLoader().available(getFont("mathfrak")))
922                         fakeFont("mathfrak", "lyxfakefrak");
923                 if (!theFontLoader().available(getFont("mathcal")))
924                         fakeFont("mathcal", "lyxfakecal");
925         }
926         fontinfo * info = searchFont(name);
927         if (info->family_ != inh_family)
928                 font.setFamily(info->family_);
929         if (info->series_ != inh_series)
930                 font.setSeries(info->series_);
931         if (info->shape_ != inh_shape)
932                 font.setShape(info->shape_);
933         if (info->color_ != Color_none)
934                 font.setColor(info->color_);
935 }
936
937
938 bool isAlphaSymbol(MathAtom const & at)
939 {
940         if (at->asCharInset() ||
941             (at->asSymbolInset() &&
942              at->asSymbolInset()->isOrdAlpha()))
943                 return true;
944
945         if (at->asFontInset()) {
946                 MathData const & ar = at->asFontInset()->cell(0);
947                 for (size_t i = 0; i < ar.size(); ++i) {
948                         if (!(ar[i]->asCharInset() ||
949                               (ar[i]->asSymbolInset() &&
950                                ar[i]->asSymbolInset()->isOrdAlpha())))
951                                 return false;
952                 }
953                 return true;
954         }
955         return false;
956 }
957
958
959 docstring asString(MathData const & ar)
960 {
961         odocstringstream os;
962         otexrowstream ots(os);
963         WriteStream ws(ots);
964         ws << ar;
965         return os.str();
966 }
967
968
969 void asArray(docstring const & str, MathData & ar, Parse::flags pf)
970 {
971         bool quiet = pf & Parse::QUIET;
972         if ((str.size() == 1 && quiet) || (!mathed_parse_cell(ar, str, pf) && quiet))
973                 mathed_parse_cell(ar, str, pf | Parse::VERBATIM);
974 }
975
976
977 docstring asString(InsetMath const & inset)
978 {
979         odocstringstream os;
980         otexrowstream ots(os);
981         WriteStream ws(ots);
982         inset.write(ws);
983         return os.str();
984 }
985
986
987 docstring asString(MathAtom const & at)
988 {
989         odocstringstream os;
990         otexrowstream ots(os);
991         WriteStream ws(ots);
992         at->write(ws);
993         return os.str();
994 }
995
996
997 } // namespace lyx