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