]> git.lyx.org Git - features.git/blob - src/mathed/MathSupport.cpp
Improve appearance of dots at higher zoom levels
[features.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                                 double const xa = x + xx + 0.5;
648                                 double const xb = x + x2 + 0.5;
649                                 double const ya = y + yy + 0.5;
650                                 double const yb = y + y2 + 0.5;
651                                 pi.pain.line(int(xa + 1), int(ya - 1),
652                                              int(xb - 1), int(yb - 1),
653                                              pi.base.font.color(),
654                                              Painter::line_solid, lw);
655                                 pi.pain.line(int(xa + 1), int(ya + 1),
656                                              int(xb - 1), int(yb + 1),
657                                              pi.base.font.color(),
658                                              Painter::line_solid, lw);
659                                 if (xa + 2 <= xb - 2) {
660                                         pi.pain.line(int(xa + 2), int(ya - 2),
661                                                      int(xb - 2), int(yb - 2),
662                                                      pi.base.font.color(),
663                                                      Painter::line_solid, lw);
664                                         pi.pain.line(int(xa + 2), int(ya + 2),
665                                                      int(xb - 2), int(yb + 2),
666                                                      pi.base.font.color(),
667                                                      Painter::line_solid, lw);
668                                 }
669                                 if (xa + 3 <= xb - 3) {
670                                         pi.pain.line(int(xa + 3), int(ya - 3),
671                                                      int(xb - 3), int(yb - 3),
672                                                      pi.base.font.color(),
673                                                      Painter::line_solid, lw);
674                                         pi.pain.line(int(xa + 3), int(ya + 3),
675                                                      int(xb - 3), int(yb + 3),
676                                                      pi.base.font.color(),
677                                                      Painter::line_solid, lw);
678                                 }
679                                 if (xa + 4 <= xb - 4) {
680                                         pi.pain.line(int(xa + 4), int(ya - 4),
681                                                      int(xb - 4), int(yb - 4),
682                                                      pi.base.font.color(),
683                                                      Painter::line_solid, lw);
684                                         pi.pain.line(int(xa + 4), int(ya + 4),
685                                                      int(xb - 4), int(yb + 4),
686                                                      pi.base.font.color(),
687                                                      Painter::line_solid, lw);
688                                 }
689                         }
690                 } else {
691                         int xp[32];
692                         int yp[32];
693                         int const n2 = int(d[i++]);
694                         for (int j = 0; j < n2; ++j) {
695                                 double xx = d[i++];
696                                 double yy = d[i++];
697 //           lyxerr << ' ' << xx << ' ' << yy << ' ';
698                                 if (code == 4)
699                                         sqmt.transform(xx, yy);
700                                 else
701                                         mt.transform(xx, yy);
702                                 xp[j] = int(x + xx + 0.5);
703                                 yp[j] = int(y + yy + 0.5);
704                                 //  lyxerr << "P[" << j ' ' << xx << ' ' << yy << ' ' << x << ' ' << y << ']';
705                         }
706                         pi.pain.lines(xp, yp, n2, pi.base.font.color(),
707                                 Painter::fill_none, Painter::line_solid, lw);
708                 }
709         }
710 }
711
712
713 docstring const &  mathedSymbol(MetricsBase & mb, latexkeys const * sym)
714 {
715         return (mb.font.style() == DISPLAY_STYLE && !sym->dsp_draw.empty()) ?
716                 sym->dsp_draw : sym->draw;
717 }
718
719
720 int mathedSymbolDim(MetricsBase & mb, Dimension & dim, latexkeys const * sym)
721 {
722         LASSERT((bool)sym, return 0);
723         //lyxerr << "metrics: symbol: '" << sym->name
724         //      << "' in font: '" << sym->inset
725         //      << "' drawn as: '" << sym->draw
726         //      << "'" << endl;
727
728         bool const italic_upcase_greek = sym->inset == "cmr" &&
729                 sym->extra == "mathalpha" &&
730                 mb.fontname == "mathit";
731         std::string const font = italic_upcase_greek ? "cmm" : sym->inset;
732         bool const change_font = font != "cmr" ||
733                                 (mb.fontname != "mathbb" &&
734                                  mb.fontname != "mathds" &&
735                                  mb.fontname != "mathfrak" &&
736                                  mb.fontname != "mathcal" &&
737                                  mb.fontname != "mathscr");
738         Changer dummy = change_font ? mb.changeFontSet(font) : noChange();
739         mathed_string_dim(mb.font, mathedSymbol(mb, sym), dim);
740         return mathed_char_kerning(mb.font, mathedSymbol(mb, sym).back());
741 }
742
743
744 void mathedSymbolDraw(PainterInfo & pi, int x, int y, latexkeys const * sym)
745 {
746         LASSERT((bool)sym, return);
747         //lyxerr << "drawing: symbol: '" << sym->name
748         //      << "' in font: '" << sym->inset
749         //      << "' drawn as: '" << sym->draw
750         //      << "'" << endl;
751
752         bool const italic_upcase_greek = sym->inset == "cmr" &&
753                 sym->extra == "mathalpha" &&
754                 pi.base.fontname == "mathit";
755         std::string const font = italic_upcase_greek ? "cmm" : sym->inset;
756         bool const change_font = font != "cmr" ||
757                                 (pi.base.fontname != "mathbb" &&
758                                  pi.base.fontname != "mathds" &&
759                                  pi.base.fontname != "mathfrak" &&
760                                  pi.base.fontname != "mathcal" &&
761                                  pi.base.fontname != "mathscr");
762         Changer dummy = change_font ? pi.base.changeFontSet(font) : noChange();
763         pi.draw(x, y, mathedSymbol(pi.base, sym));
764 }
765
766
767 void metricsStrRedBlack(MetricsInfo & mi, Dimension & dim, docstring const & str)
768 {
769         FontInfo font = mi.base.font;
770         augmentFont(font, "mathnormal");
771         mathed_string_dim(font, str, dim);
772 }
773
774
775 void drawStrRed(PainterInfo & pi, int x, int y, docstring const & str)
776 {
777         FontInfo f = pi.base.font;
778         augmentFont(f, "mathnormal");
779         f.setColor(Color_latex);
780         pi.pain.text(x, y, str, f);
781 }
782
783
784 void drawStrBlack(PainterInfo & pi, int x, int y, docstring const & str)
785 {
786         FontInfo f = pi.base.font;
787         augmentFont(f, "mathnormal");
788         f.setColor(Color_foreground);
789         pi.pain.text(x, y, str, f);
790 }
791
792
793 void math_font_max_dim(FontInfo const & font, int & asc, int & des)
794 {
795         frontend::FontMetrics const & fm = theFontMetrics(font);
796         asc = fm.maxAscent();
797         des = fm.maxDescent();
798 }
799
800
801 struct fontinfo {
802         string cmd_;
803         FontFamily family_;
804         FontSeries series_;
805         FontShape  shape_;
806         ColorCode        color_;
807 };
808
809
810 FontFamily const inh_family = INHERIT_FAMILY;
811 FontSeries const inh_series = INHERIT_SERIES;
812 FontShape  const inh_shape  = INHERIT_SHAPE;
813
814
815 // mathnormal should be the first, otherwise the fallback further down
816 // does not work
817 fontinfo fontinfos[] = {
818         // math fonts
819         // Color_math determines which fonts are math (see isMathFont)
820         {"mathnormal",    ROMAN_FAMILY, MEDIUM_SERIES,
821                           ITALIC_SHAPE, Color_math},
822         {"mathbf",        inh_family, BOLD_SERIES,
823                           inh_shape, Color_math},
824         {"mathcal",       CMSY_FAMILY, inh_series,
825                           inh_shape, Color_math},
826         {"mathfrak",      EUFRAK_FAMILY, inh_series,
827                           inh_shape, Color_math},
828         {"mathrm",        ROMAN_FAMILY, inh_series,
829                           UP_SHAPE, Color_math},
830         {"mathsf",        SANS_FAMILY, inh_series,
831                           inh_shape, Color_math},
832         {"mathbb",        MSB_FAMILY, inh_series,
833                           inh_shape, Color_math},
834         {"mathds",        DS_FAMILY, inh_series,
835                           inh_shape, Color_math},
836         {"mathtt",        TYPEWRITER_FAMILY, inh_series,
837                           inh_shape, Color_math},
838         {"mathit",        inh_family, inh_series,
839                           ITALIC_SHAPE, Color_math},
840         {"mathscr",       RSFS_FAMILY, inh_series,
841                           inh_shape, Color_math},
842         {"cmex",          CMEX_FAMILY, inh_series,
843                           inh_shape, Color_math},
844         {"cmm",           CMM_FAMILY, inh_series,
845                           inh_shape, Color_math},
846         {"cmr",           CMR_FAMILY, inh_series,
847                           inh_shape, Color_math},
848         {"cmsy",          CMSY_FAMILY, inh_series,
849                           inh_shape, Color_math},
850         {"eufrak",        EUFRAK_FAMILY, inh_series,
851                           inh_shape, Color_math},
852         {"msa",           MSA_FAMILY, inh_series,
853                           inh_shape, Color_math},
854         {"msb",           MSB_FAMILY, inh_series,
855                           inh_shape, Color_math},
856         {"stmry",         STMARY_FAMILY, inh_series,
857                           inh_shape, Color_math},
858         {"wasy",          WASY_FAMILY, inh_series,
859                           inh_shape, Color_math},
860         {"esint",         ESINT_FAMILY, inh_series,
861                           inh_shape, Color_math},
862
863         // Text fonts
864         {"text",          inh_family, inh_series,
865                           inh_shape, Color_foreground},
866         {"textbf",        inh_family, BOLD_SERIES,
867                           inh_shape, Color_foreground},
868         {"textit",        inh_family, inh_series,
869                           ITALIC_SHAPE, Color_foreground},
870         {"textmd",        inh_family, MEDIUM_SERIES,
871                           inh_shape, Color_foreground},
872         {"textnormal",    inh_family, inh_series,
873                           UP_SHAPE, Color_foreground},
874         {"textrm",        ROMAN_FAMILY,
875                           inh_series, UP_SHAPE,Color_foreground},
876         {"textsc",        inh_family, inh_series,
877                           SMALLCAPS_SHAPE, Color_foreground},
878         {"textsf",        SANS_FAMILY, inh_series,
879                           inh_shape, Color_foreground},
880         {"textsl",        inh_family, inh_series,
881                           SLANTED_SHAPE, Color_foreground},
882         {"texttt",        TYPEWRITER_FAMILY, inh_series,
883                           inh_shape, Color_foreground},
884         {"textup",        inh_family, inh_series,
885                           UP_SHAPE, Color_foreground},
886
887         // TIPA support
888         {"textipa",       inh_family, inh_series,
889                           inh_shape, Color_foreground},
890
891         // mhchem support
892         {"ce",            inh_family, inh_series,
893                           inh_shape, Color_foreground},
894         {"cf",            inh_family, inh_series,
895                           inh_shape, Color_foreground},
896
897         // LyX internal usage
898         {"lyxtex",        inh_family, inh_series,
899                           UP_SHAPE, Color_latex},
900         // FIXME: The following two don't work on OS X, since the Symbol font
901         //        uses a different encoding, and is therefore disabled in
902         //        FontLoader::available().
903         {"lyxsymbol",     SYMBOL_FAMILY, inh_series,
904                           inh_shape, Color_math},
905         {"lyxboldsymbol", SYMBOL_FAMILY, BOLD_SERIES,
906                           inh_shape, Color_math},
907         {"lyxblacktext",  ROMAN_FAMILY, MEDIUM_SERIES,
908                           UP_SHAPE, Color_foreground},
909         {"lyxnochange",   inh_family, inh_series,
910                           inh_shape, Color_foreground},
911         {"lyxfakebb",     TYPEWRITER_FAMILY, BOLD_SERIES,
912                           UP_SHAPE, Color_math},
913         {"lyxfakecal",    SANS_FAMILY, MEDIUM_SERIES,
914                           ITALIC_SHAPE, Color_math},
915         {"lyxfakefrak",   ROMAN_FAMILY, BOLD_SERIES,
916                           ITALIC_SHAPE, Color_math}
917 };
918
919
920 fontinfo * lookupFont(string const & name)
921 {
922         //lyxerr << "searching font '" << name << "'" << endl;
923         int const n = sizeof(fontinfos) / sizeof(fontinfo);
924         for (int i = 0; i < n; ++i)
925                 if (fontinfos[i].cmd_ == name) {
926                         //lyxerr << "found '" << i << "'" << endl;
927                         return fontinfos + i;
928                 }
929         return 0;
930 }
931
932
933 fontinfo * searchFont(string const & name)
934 {
935         fontinfo * f = lookupFont(name);
936         return f ? f : fontinfos;
937         // this should be mathnormal
938         //return searchFont("mathnormal");
939 }
940
941
942 bool isFontName(string const & name)
943 {
944         return lookupFont(name);
945 }
946
947
948 bool isMathFont(string const & name)
949 {
950         fontinfo * f = lookupFont(name);
951         return f && f->color_ == Color_math;
952 }
953
954
955 bool isTextFont(string const & name)
956 {
957         fontinfo * f = lookupFont(name);
958         return f && f->color_ == Color_foreground;
959 }
960
961
962 FontInfo getFont(string const & name)
963 {
964         FontInfo font;
965         augmentFont(font, name);
966         return font;
967 }
968
969
970 void fakeFont(string const & orig, string const & fake)
971 {
972         fontinfo * forig = searchFont(orig);
973         fontinfo * ffake = searchFont(fake);
974         if (forig && ffake) {
975                 forig->family_ = ffake->family_;
976                 forig->series_ = ffake->series_;
977                 forig->shape_  = ffake->shape_;
978                 forig->color_  = ffake->color_;
979         } else {
980                 lyxerr << "Can't fake font '" << orig << "' with '"
981                        << fake << "'" << endl;
982         }
983 }
984
985
986 void augmentFont(FontInfo & font, string const & name)
987 {
988         static bool initialized = false;
989         if (!initialized) {
990                 initialized = true;
991                 // fake fonts if necessary
992                 if (!theFontLoader().available(getFont("mathfrak")))
993                         fakeFont("mathfrak", "lyxfakefrak");
994                 if (!theFontLoader().available(getFont("mathcal")))
995                         fakeFont("mathcal", "lyxfakecal");
996         }
997         fontinfo * info = searchFont(name);
998         if (info->family_ != inh_family)
999                 font.setFamily(info->family_);
1000         if (info->series_ != inh_series)
1001                 font.setSeries(info->series_);
1002         if (info->shape_ != inh_shape)
1003                 font.setShape(info->shape_);
1004         if (info->color_ != Color_none)
1005                 font.setColor(info->color_);
1006 }
1007
1008
1009 bool isAlphaSymbol(MathAtom const & at)
1010 {
1011         if (at->asCharInset() ||
1012             (at->asSymbolInset() &&
1013              at->asSymbolInset()->isOrdAlpha()))
1014                 return true;
1015
1016         if (at->asFontInset()) {
1017                 MathData const & ar = at->asFontInset()->cell(0);
1018                 for (size_t i = 0; i < ar.size(); ++i) {
1019                         if (!(ar[i]->asCharInset() ||
1020                               (ar[i]->asSymbolInset() &&
1021                                ar[i]->asSymbolInset()->isOrdAlpha())))
1022                                 return false;
1023                 }
1024                 return true;
1025         }
1026         return false;
1027 }
1028
1029
1030 docstring asString(MathData const & ar)
1031 {
1032         odocstringstream os;
1033         otexrowstream ots(os);
1034         TeXMathStream ws(ots);
1035         ws << ar;
1036         return os.str();
1037 }
1038
1039
1040 void asArray(docstring const & str, MathData & ar, Parse::flags pf)
1041 {
1042         // If the QUIET flag is set, we are going to parse for either
1043         // a paste operation or a macro definition. We try to do the
1044         // right thing in all cases.
1045
1046         bool quiet = pf & Parse::QUIET;
1047         bool macro = pf & Parse::MACRODEF;
1048         if ((str.size() == 1 && quiet) || (!mathed_parse_cell(ar, str, pf) && quiet && !macro))
1049                 mathed_parse_cell(ar, str, pf | Parse::VERBATIM);
1050 }
1051
1052
1053 docstring asString(InsetMath const & inset)
1054 {
1055         odocstringstream os;
1056         otexrowstream ots(os);
1057         TeXMathStream ws(ots);
1058         inset.write(ws);
1059         return os.str();
1060 }
1061
1062
1063 docstring asString(MathAtom const & at)
1064 {
1065         odocstringstream os;
1066         otexrowstream ots(os);
1067         TeXMathStream ws(ots);
1068         at->write(ws);
1069         return os.str();
1070 }
1071
1072
1073 int axis_height(MetricsBase & mb)
1074 {
1075         Changer dummy = mb.changeFontSet("mathnormal");
1076         return theFontMetrics(mb.font).ascent('-') - 1;
1077 }
1078
1079
1080 void validate_math_word(LaTeXFeatures & features, docstring const & word)
1081 {
1082         MathWordList const & words = mathedWordList();
1083         MathWordList::const_iterator it = words.find(word);
1084         if (it != words.end()) {
1085                 string const req = it->second.required;
1086                 if (!req.empty())
1087                         features.require(req);
1088         }
1089 }
1090
1091
1092 } // namespace lyx