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