]> git.lyx.org Git - lyx.git/blob - src/mathed/MathSupport.cpp
Revert "XHTML: remove DOCTYPE, as the document is then understood as HTML4/XHTML1...
[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/debug.h"
31 #include "support/docstream.h"
32 #include "support/lassert.h"
33 #include "support/Length.h"
34 #include "support/lyxlib.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.4, 0.6, 0.4,
302         0
303 };
304
305
306 double const ddot[] = {
307         5, 0.0, 0.4, 0.3, 0.4,
308         5, 0.6, 0.4, 1.0, 0.4,
309         0
310 };
311
312
313 double const dddot[] = {
314         1, 0.1,  0.5, 0.2,  0.5,
315         1, 0.45, 0.5, 0.55, 0.5,
316         1, 0.8,  0.5, 0.9,  0.5,
317         0
318 };
319
320
321 double const ddddot[] = {
322         1, 0.1,  0.5, 0.2,  0.5,
323         1, 0.45, 0.5, 0.55, 0.5,
324         1, 0.8,  0.5, 0.9,  0.5,
325         1, 1.15, 0.5, 1.25, 0.5,
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, 5,
348         0.5, 0.8,  0.8, 0.5,  0.5, 0.2,  0.2, 0.5,  0.5, 0.8,
349         0
350 };
351
352
353 double const vert[] = {
354         1, 0.5, 0.05,  0.5, 0.95,
355         0
356 };
357
358
359 double const  Vert[] = {
360         1, 0.3, 0.05,  0.3, 0.95,
361         1, 0.7, 0.05,  0.7, 0.95,
362         0
363 };
364
365
366 double const tilde[] = {
367         2, 4,
368         0.00, 0.8,  0.25, 0.2,  0.75, 0.8,  1.00, 0.2,
369         0
370 };
371
372
373 struct deco_struct {
374         double const * data;
375         int angle;
376 };
377
378 struct named_deco_struct {
379         char const * name;
380         double const * data;
381         int angle;
382 };
383
384 named_deco_struct deco_table[] = {
385         // Decorations
386         {"widehat",             angle,        3 },
387         {"widetilde",           tilde,        0 },
388         {"underbar",            hline,        0 },
389         {"underline",           hline,        0 },
390         {"overline",            hline,        0 },
391         {"underbrace",          brace,        1 },
392         {"overbrace",           brace,        3 },
393         {"overleftarrow",       arrow,        1 },
394         {"overrightarrow",      arrow,        3 },
395         {"overleftrightarrow",  udarrow,      1 },
396         {"xhookleftarrow",      lhook,        0 },
397         {"xhookrightarrow",     rhook,        0 },
398         {"xleftarrow",          arrow,        1 },
399         {"xLeftarrow",          LArrow,       0 },
400         {"xleftharpoondown",    lharpoondown, 0 },
401         {"xleftharpoonup",      lharpoonup,   0 },
402         {"xleftrightharpoons",  lrharpoons,   0 },
403         {"xleftrightarrow",     udarrow,      1 },
404         {"xLeftrightarrow",     LRArrow,      0 },
405         {"xmapsto",             mapsto,       0 },
406         {"xrightarrow",         arrow,        3 },
407         {"xRightarrow",         LArrow,       2 },
408         {"xrightharpoondown",   lharpoonup,   2 },
409         {"xrightharpoonup",     lharpoondown, 2 },
410         {"xrightleftharpoons",  rlharpoons,   0 },
411         {"underleftarrow",      arrow,        1 },
412         {"underrightarrow",     arrow,        3 },
413         {"underleftrightarrow", udarrow,      1 },
414         {"undertilde",          tilde,        0 },
415         {"utilde",              tilde,        0 },
416
417         // Delimiters
418         {"(",              parenth,    0 },
419         {")",              parenth,    2 },
420         {"{",              brace,      0 },
421         {"}",              brace,      2 },
422         {"lbrace",         brace,      0 },
423         {"rbrace",         brace,      2 },
424         {"[",              brack,      0 },
425         {"]",              brack,      2 },
426         {"llbracket",      dbrack,     0 },
427         {"rrbracket",      dbrack,     2 },
428         {"|",              vert,       0 },
429         {"/",              slash,      0 },
430         {"slash",          slash,      0 },
431         {"vert",           vert,       0 },
432         {"lvert",          vert,       0 },
433         {"rvert",          vert,       0 },
434         {"Vert",           Vert,       0 },
435         {"lVert",          Vert,       0 },
436         {"rVert",          Vert,       0 },
437         {"'",              slash,      1 },
438         {"<",              angle,      0 },
439         {">",              angle,      2 },
440         {"\\",             slash,      1 },
441         {"backslash",      slash,      1 },
442         {"langle",         angle,      0 },
443         {"lceil",          corner,     0 },
444         {"lfloor",         corner,     1 },
445         {"rangle",         angle,      2 },
446         {"rceil",          corner,     3 },
447         {"rfloor",         corner,     2 },
448         {"downarrow",      arrow,      2 },
449         {"Downarrow",      Arrow,      2 },
450         {"uparrow",        arrow,      0 },
451         {"Uparrow",        Arrow,      0 },
452         {"updownarrow",    udarrow,    0 },
453         {"Updownarrow",    Udarrow,    0 },
454
455         // Accents
456         {"ddot",           ddot,       0 },
457         {"dddot",          dddot,      0 },
458         {"ddddot",         ddddot,     0 },
459         {"hat",            angle,      3 },
460         {"grave",          slash,      1 },
461         {"acute",          slash,      0 },
462         {"tilde",          tilde,      0 },
463         {"bar",            hline,      0 },
464         {"dot",            dot,        0 },
465         {"check",          angle,      1 },
466         {"breve",          parenth,    1 },
467         {"vec",            arrow,      3 },
468         {"mathring",       ring,       0 },
469
470         // Dots
471         {"dots",           hline3,     0 },
472         {"ldots",          hline3,     0 },
473         {"cdots",          hline3,     0 },
474         {"vdots",          hline3,     1 },
475         {"ddots",          dline3,     0 },
476         {"adots",          dline3,     1 },
477         {"iddots",         dline3,     1 },
478         {"dotsb",          hline3,     0 },
479         {"dotsc",          hline3,     0 },
480         {"dotsi",          hline3,     0 },
481         {"dotsm",          hline3,     0 },
482         {"dotso",          hline3,     0 }
483 };
484
485
486 map<docstring, deco_struct> deco_list;
487
488 // sort the table on startup
489 class init_deco_table {
490 public:
491         init_deco_table() {
492                 unsigned const n = sizeof(deco_table) / sizeof(deco_table[0]);
493                 for (named_deco_struct * p = deco_table; p != deco_table + n; ++p) {
494                         deco_struct d;
495                         d.data  = p->data;
496                         d.angle = p->angle;
497                         deco_list[from_ascii(p->name)] = d;
498                 }
499         }
500 };
501
502 static init_deco_table dummy_deco_table;
503
504
505 deco_struct const * search_deco(docstring const & name)
506 {
507         map<docstring, deco_struct>::const_iterator p = deco_list.find(name);
508         return p == deco_list.end() ? 0 : &(p->second);
509 }
510
511
512 } // namespace
513
514
515 int mathed_font_em(FontInfo const & font)
516 {
517         return theFontMetrics(font).em();
518 }
519
520
521 int mathed_font_x_height(FontInfo const & font)
522 {
523         return theFontMetrics(font).xHeight();
524 }
525
526 /* The math units. Quoting TeX by Topic, p.205:
527  *
528  * Spacing around mathematical objects is measured in mu units. A mu
529  * is 1/18th part of \fontdimen6 of the font in family 2 in the
530  * current style, the ‘quad’ value of the symbol font.
531  *
532  * A \thickmuskip (default value in plain TeX: 5mu plus 5mu) is
533  * inserted around (binary) relations, except where these are preceded
534  * or followed by other relations or punctuation, and except if they
535  * follow an open, or precede a close symbol.
536  *
537  * A \medmuskip (default value in plain TeX: 4mu plus 2mu minus 4mu)
538  * is put around binary operators.
539  *
540  * A \thinmuskip (default value in plain TeX: 3mu) follows after
541  * punctuation, and is put around inner objects, except where these
542  * are followed by a close or preceded by an open symbol, and except
543  * if the other object is a large operator or a binary relation.
544  *
545  * See the file MathClass.cpp for a formal implementation of the rules
546  * above.
547  */
548
549 int mathed_mu(FontInfo const & font, double mu)
550 {
551         MetricsBase mb(nullptr, font);
552         return mb.inPixels(Length(mu, Length::MU));
553 }
554
555 int mathed_thinmuskip(FontInfo const & font) { return mathed_mu(font, 3.0); }
556 int mathed_medmuskip(FontInfo const & font) { return mathed_mu(font, 4.0); }
557 int mathed_thickmuskip(FontInfo const & font) { return mathed_mu(font, 5.0); }
558
559
560 int mathed_char_width(FontInfo const & font, char_type c)
561 {
562         return theFontMetrics(font).width(c);
563 }
564
565
566 int mathed_char_kerning(FontInfo const & font, char_type c)
567 {
568         frontend::FontMetrics const & fm = theFontMetrics(font);
569         return max(0, fm.rbearing(c) - fm.width(c));
570 }
571
572
573 void mathed_string_dim(FontInfo const & font,
574                        docstring const & s,
575                        Dimension & dim)
576 {
577         frontend::FontMetrics const & fm = theFontMetrics(font);
578         dim.asc = 0;
579         dim.des = 0;
580         for (docstring::const_iterator it = s.begin();
581              it != s.end();
582              ++it) {
583                 dim.asc = max(dim.asc, fm.ascent(*it));
584                 dim.des = max(dim.des, fm.descent(*it));
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         if (name == ".") {
600                 pi.pain.line(x + w/2, y, x + w/2, y + h,
601                           Color_cursor, Painter::line_onoffdash);
602                 return;
603         }
604
605         deco_struct const * mds = search_deco(name);
606         if (!mds) {
607                 lyxerr << "Deco was not found. Programming error?" << endl;
608                 lyxerr << "name: '" << to_utf8(name) << "'" << endl;
609                 return;
610         }
611
612         int const n = (w < h) ? w : h;
613         int const r = mds->angle;
614         double const * d = mds->data;
615
616         if (h > 70 && (name == "(" || name == ")"))
617                 d = parenthHigh;
618
619         Matrix mt(r, w, h);
620         Matrix sqmt(r, n, n);
621
622         if (r > 0 && r < 3)
623                 y += h;
624
625         if (r >= 2)
626                 x += w;
627
628         for (int i = 0; d[i]; ) {
629                 int code = int(d[i++]);
630                 if (code & 1) {  // code == 1 || code == 3 || code == 5
631                         double xx = d[i++];
632                         double yy = d[i++];
633                         double x2 = d[i++];
634                         double y2 = d[i++];
635                         if (code == 3)
636                                 sqmt.transform(xx, yy);
637                         else
638                                 mt.transform(xx, yy);
639                         mt.transform(x2, y2);
640                         pi.pain.line(
641                                 int(x + xx + 0.5), int(y + yy + 0.5),
642                                 int(x + x2 + 0.5), int(y + y2 + 0.5),
643                                 pi.base.font.color());
644                         if (code == 5) {  // thicker, but rounded
645                                 pi.pain.line(
646                                         int(x + xx + 0.5+1), int(y + yy + 0.5-1),
647                                         int(x + x2 + 0.5-1), int(y + y2 + 0.5-1),
648                                 pi.base.font.color());
649                                 pi.pain.line(
650                                         int(x + xx + 0.5+1), int(y + yy + 0.5+1),
651                                         int(x + x2 + 0.5-1), int(y + y2 + 0.5+1),
652                                 pi.base.font.color());
653                         }
654                 } else {
655                         int xp[32];
656                         int yp[32];
657                         int const n2 = int(d[i++]);
658                         for (int j = 0; j < n2; ++j) {
659                                 double xx = d[i++];
660                                 double yy = d[i++];
661 //           lyxerr << ' ' << xx << ' ' << yy << ' ';
662                                 if (code == 4)
663                                         sqmt.transform(xx, yy);
664                                 else
665                                         mt.transform(xx, yy);
666                                 xp[j] = int(x + xx + 0.5);
667                                 yp[j] = int(y + yy + 0.5);
668                                 //  lyxerr << "P[" << j ' ' << xx << ' ' << yy << ' ' << x << ' ' << y << ']';
669                         }
670                         pi.pain.lines(xp, yp, n2, pi.base.font.color());
671                 }
672         }
673 }
674
675
676 docstring const &  mathedSymbol(MetricsBase & mb, latexkeys const * sym)
677 {
678         return (mb.font.style() == DISPLAY_STYLE && !sym->dsp_draw.empty()) ?
679                 sym->dsp_draw : sym->draw;
680 }
681
682
683 int mathedSymbolDim(MetricsBase & mb, Dimension & dim, latexkeys const * sym)
684 {
685         LASSERT((bool)sym, return 0);
686         //lyxerr << "metrics: symbol: '" << sym->name
687         //      << "' in font: '" << sym->inset
688         //      << "' drawn as: '" << sym->draw
689         //      << "'" << endl;
690
691         bool const italic_upcase_greek = sym->inset == "cmr" &&
692                 sym->extra == "mathalpha" &&
693                 mb.fontname == "mathit";
694         std::string const font = italic_upcase_greek ? "cmm" : sym->inset;
695         bool const change_font = font != "cmr" ||
696                                 (mb.fontname != "mathbb" &&
697                                  mb.fontname != "mathds" &&
698                                  mb.fontname != "mathfrak" &&
699                                  mb.fontname != "mathcal" &&
700                                  mb.fontname != "mathscr");
701         Changer dummy = change_font ? mb.changeFontSet(font) : Changer();
702         mathed_string_dim(mb.font, mathedSymbol(mb, sym), dim);
703         return mathed_char_kerning(mb.font, mathedSymbol(mb, sym).back());
704 }
705
706
707 void mathedSymbolDraw(PainterInfo & pi, int x, int y, latexkeys const * sym)
708 {
709         LASSERT((bool)sym, return);
710         //lyxerr << "drawing: symbol: '" << sym->name
711         //      << "' in font: '" << sym->inset
712         //      << "' drawn as: '" << sym->draw
713         //      << "'" << endl;
714
715         bool const italic_upcase_greek = sym->inset == "cmr" &&
716                 sym->extra == "mathalpha" &&
717                 pi.base.fontname == "mathit";
718         std::string const font = italic_upcase_greek ? "cmm" : sym->inset;
719         bool const change_font = font != "cmr" ||
720                                 (pi.base.fontname != "mathbb" &&
721                                  pi.base.fontname != "mathds" &&
722                                  pi.base.fontname != "mathfrak" &&
723                                  pi.base.fontname != "mathcal" &&
724                                  pi.base.fontname != "mathscr");
725         Changer dummy = change_font ? pi.base.changeFontSet(font) : Changer();
726         pi.draw(x, y, mathedSymbol(pi.base, sym));
727 }
728
729
730 void metricsStrRedBlack(MetricsInfo & mi, Dimension & dim, docstring const & str)
731 {
732         FontInfo font = mi.base.font;
733         augmentFont(font, "mathnormal");
734         mathed_string_dim(font, str, dim);
735 }
736
737
738 void drawStrRed(PainterInfo & pi, int x, int y, docstring const & str)
739 {
740         FontInfo f = pi.base.font;
741         augmentFont(f, "mathnormal");
742         f.setColor(Color_latex);
743         pi.pain.text(x, y, str, f);
744 }
745
746
747 void drawStrBlack(PainterInfo & pi, int x, int y, docstring const & str)
748 {
749         FontInfo f = pi.base.font;
750         augmentFont(f, "mathnormal");
751         f.setColor(Color_foreground);
752         pi.pain.text(x, y, str, f);
753 }
754
755
756 void math_font_max_dim(FontInfo const & font, int & asc, int & des)
757 {
758         frontend::FontMetrics const & fm = theFontMetrics(font);
759         asc = fm.maxAscent();
760         des = fm.maxDescent();
761 }
762
763
764 struct fontinfo {
765         string cmd_;
766         FontFamily family_;
767         FontSeries series_;
768         FontShape  shape_;
769         ColorCode        color_;
770 };
771
772
773 FontFamily const inh_family = INHERIT_FAMILY;
774 FontSeries const inh_series = INHERIT_SERIES;
775 FontShape  const inh_shape  = INHERIT_SHAPE;
776
777
778 // mathnormal should be the first, otherwise the fallback further down
779 // does not work
780 fontinfo fontinfos[] = {
781         // math fonts
782         // Color_math determines which fonts are math (see isMathFont)
783         {"mathnormal",    ROMAN_FAMILY, MEDIUM_SERIES,
784                           ITALIC_SHAPE, Color_math},
785         {"mathbf",        inh_family, BOLD_SERIES,
786                           inh_shape, Color_math},
787         {"mathcal",       CMSY_FAMILY, inh_series,
788                           inh_shape, Color_math},
789         {"mathfrak",      EUFRAK_FAMILY, inh_series,
790                           inh_shape, Color_math},
791         {"mathrm",        ROMAN_FAMILY, inh_series,
792                           UP_SHAPE, Color_math},
793         {"mathsf",        SANS_FAMILY, inh_series,
794                           inh_shape, Color_math},
795         {"mathbb",        MSB_FAMILY, inh_series,
796                           inh_shape, Color_math},
797         {"mathds",        DS_FAMILY, inh_series,
798                           inh_shape, Color_math},
799         {"mathtt",        TYPEWRITER_FAMILY, inh_series,
800                           inh_shape, Color_math},
801         {"mathit",        inh_family, inh_series,
802                           ITALIC_SHAPE, Color_math},
803         {"mathscr",       RSFS_FAMILY, inh_series,
804                           inh_shape, Color_math},
805         {"cmex",          CMEX_FAMILY, inh_series,
806                           inh_shape, Color_math},
807         {"cmm",           CMM_FAMILY, inh_series,
808                           inh_shape, Color_math},
809         {"cmr",           CMR_FAMILY, inh_series,
810                           inh_shape, Color_math},
811         {"cmsy",          CMSY_FAMILY, inh_series,
812                           inh_shape, Color_math},
813         {"eufrak",        EUFRAK_FAMILY, inh_series,
814                           inh_shape, Color_math},
815         {"msa",           MSA_FAMILY, inh_series,
816                           inh_shape, Color_math},
817         {"msb",           MSB_FAMILY, inh_series,
818                           inh_shape, Color_math},
819         {"stmry",         STMARY_FAMILY, inh_series,
820                           inh_shape, Color_math},
821         {"wasy",          WASY_FAMILY, inh_series,
822                           inh_shape, Color_math},
823         {"esint",         ESINT_FAMILY, inh_series,
824                           inh_shape, Color_math},
825
826         // Text fonts
827         {"text",          inh_family, inh_series,
828                           inh_shape, Color_foreground},
829         {"textbf",        inh_family, BOLD_SERIES,
830                           inh_shape, Color_foreground},
831         {"textit",        inh_family, inh_series,
832                           ITALIC_SHAPE, Color_foreground},
833         {"textmd",        inh_family, MEDIUM_SERIES,
834                           inh_shape, Color_foreground},
835         {"textnormal",    inh_family, inh_series,
836                           UP_SHAPE, Color_foreground},
837         {"textrm",        ROMAN_FAMILY,
838                           inh_series, UP_SHAPE,Color_foreground},
839         {"textsc",        inh_family, inh_series,
840                           SMALLCAPS_SHAPE, Color_foreground},
841         {"textsf",        SANS_FAMILY, inh_series,
842                           inh_shape, Color_foreground},
843         {"textsl",        inh_family, inh_series,
844                           SLANTED_SHAPE, Color_foreground},
845         {"texttt",        TYPEWRITER_FAMILY, inh_series,
846                           inh_shape, Color_foreground},
847         {"textup",        inh_family, inh_series,
848                           UP_SHAPE, Color_foreground},
849
850         // TIPA support
851         {"textipa",       inh_family, inh_series,
852                           inh_shape, Color_foreground},
853
854         // mhchem support
855         {"ce",            inh_family, inh_series,
856                           inh_shape, Color_foreground},
857         {"cf",            inh_family, inh_series,
858                           inh_shape, Color_foreground},
859
860         // LyX internal usage
861         {"lyxtex",        inh_family, inh_series,
862                           UP_SHAPE, Color_latex},
863         // FIXME: The following two don't work on OS X, since the Symbol font
864         //        uses a different encoding, and is therefore disabled in
865         //        FontLoader::available().
866         {"lyxsymbol",     SYMBOL_FAMILY, inh_series,
867                           inh_shape, Color_math},
868         {"lyxboldsymbol", SYMBOL_FAMILY, BOLD_SERIES,
869                           inh_shape, Color_math},
870         {"lyxblacktext",  ROMAN_FAMILY, MEDIUM_SERIES,
871                           UP_SHAPE, Color_foreground},
872         {"lyxnochange",   inh_family, inh_series,
873                           inh_shape, Color_foreground},
874         {"lyxfakebb",     TYPEWRITER_FAMILY, BOLD_SERIES,
875                           UP_SHAPE, Color_math},
876         {"lyxfakecal",    SANS_FAMILY, MEDIUM_SERIES,
877                           ITALIC_SHAPE, Color_math},
878         {"lyxfakefrak",   ROMAN_FAMILY, BOLD_SERIES,
879                           ITALIC_SHAPE, Color_math}
880 };
881
882
883 fontinfo * lookupFont(string const & name)
884 {
885         //lyxerr << "searching font '" << name << "'" << endl;
886         int const n = sizeof(fontinfos) / sizeof(fontinfo);
887         for (int i = 0; i < n; ++i)
888                 if (fontinfos[i].cmd_ == name) {
889                         //lyxerr << "found '" << i << "'" << endl;
890                         return fontinfos + i;
891                 }
892         return 0;
893 }
894
895
896 fontinfo * searchFont(string const & name)
897 {
898         fontinfo * f = lookupFont(name);
899         return f ? f : fontinfos;
900         // this should be mathnormal
901         //return searchFont("mathnormal");
902 }
903
904
905 bool isFontName(string const & name)
906 {
907         return lookupFont(name);
908 }
909
910
911 bool isMathFont(string const & name)
912 {
913         fontinfo * f = lookupFont(name);
914         return f && f->color_ == Color_math;
915 }
916
917
918 bool isTextFont(string const & name)
919 {
920         fontinfo * f = lookupFont(name);
921         return f && f->color_ == Color_foreground;
922 }
923
924
925 FontInfo getFont(string const & name)
926 {
927         FontInfo font;
928         augmentFont(font, name);
929         return font;
930 }
931
932
933 void fakeFont(string const & orig, string const & fake)
934 {
935         fontinfo * forig = searchFont(orig);
936         fontinfo * ffake = searchFont(fake);
937         if (forig && ffake) {
938                 forig->family_ = ffake->family_;
939                 forig->series_ = ffake->series_;
940                 forig->shape_  = ffake->shape_;
941                 forig->color_  = ffake->color_;
942         } else {
943                 lyxerr << "Can't fake font '" << orig << "' with '"
944                        << fake << "'" << endl;
945         }
946 }
947
948
949 void augmentFont(FontInfo & font, string const & name)
950 {
951         static bool initialized = false;
952         if (!initialized) {
953                 initialized = true;
954                 // fake fonts if necessary
955                 if (!theFontLoader().available(getFont("mathfrak")))
956                         fakeFont("mathfrak", "lyxfakefrak");
957                 if (!theFontLoader().available(getFont("mathcal")))
958                         fakeFont("mathcal", "lyxfakecal");
959         }
960         fontinfo * info = searchFont(name);
961         if (info->family_ != inh_family)
962                 font.setFamily(info->family_);
963         if (info->series_ != inh_series)
964                 font.setSeries(info->series_);
965         if (info->shape_ != inh_shape)
966                 font.setShape(info->shape_);
967         if (info->color_ != Color_none)
968                 font.setColor(info->color_);
969 }
970
971
972 bool isAlphaSymbol(MathAtom const & at)
973 {
974         if (at->asCharInset() ||
975             (at->asSymbolInset() &&
976              at->asSymbolInset()->isOrdAlpha()))
977                 return true;
978
979         if (at->asFontInset()) {
980                 MathData const & ar = at->asFontInset()->cell(0);
981                 for (size_t i = 0; i < ar.size(); ++i) {
982                         if (!(ar[i]->asCharInset() ||
983                               (ar[i]->asSymbolInset() &&
984                                ar[i]->asSymbolInset()->isOrdAlpha())))
985                                 return false;
986                 }
987                 return true;
988         }
989         return false;
990 }
991
992
993 docstring asString(MathData const & ar)
994 {
995         odocstringstream os;
996         otexrowstream ots(os);
997         WriteStream ws(ots);
998         ws << ar;
999         return os.str();
1000 }
1001
1002
1003 void asArray(docstring const & str, MathData & ar, Parse::flags pf)
1004 {
1005         // If the QUIET flag is set, we are going to parse for either
1006         // a paste operation or a macro definition. We try to do the
1007         // right thing in all cases.
1008
1009         bool quiet = pf & Parse::QUIET;
1010         bool macro = pf & Parse::MACRODEF;
1011         if ((str.size() == 1 && quiet) || (!mathed_parse_cell(ar, str, pf) && quiet && !macro))
1012                 mathed_parse_cell(ar, str, pf | Parse::VERBATIM);
1013 }
1014
1015
1016 docstring asString(InsetMath const & inset)
1017 {
1018         odocstringstream os;
1019         otexrowstream ots(os);
1020         WriteStream ws(ots);
1021         inset.write(ws);
1022         return os.str();
1023 }
1024
1025
1026 docstring asString(MathAtom const & at)
1027 {
1028         odocstringstream os;
1029         otexrowstream ots(os);
1030         WriteStream ws(ots);
1031         at->write(ws);
1032         return os.str();
1033 }
1034
1035
1036 int axis_height(MetricsBase & mb)
1037 {
1038         Changer dummy = mb.changeFontSet("mathnormal");
1039         return theFontMetrics(mb.font).ascent('-') - 1;
1040 }
1041
1042
1043 void validate_math_word(LaTeXFeatures & features, docstring const & word)
1044 {
1045         MathWordList const & words = mathedWordList();
1046         MathWordList::const_iterator it = words.find(word);
1047         if (it != words.end()) {
1048                 string const req = it->second.required;
1049                 if (!req.empty())
1050                         features.require(req);
1051         }
1052 }
1053
1054
1055 } // namespace lyx