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