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