]> git.lyx.org Git - lyx.git/blob - src/mathed/math_support.C
support for color in math
[lyx.git] / src / mathed / math_support.C
1 /**
2  * \file math_support.C
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 "math_support.h"
15
16 #include "math_data.h"
17 #include "math_mathmlstream.h"
18 #include "math_parser.h"
19
20 #include "debug.h"
21 #include "LColor.h"
22
23 #include "frontends/Painter.h"
24 #include "frontends/font_metrics.h"
25 #include "frontends/lyx_gui.h"
26
27 #include "support/std_sstream.h"
28
29 #include <map>
30
31 using std::max;
32 using std::endl;
33
34
35 ///
36 class Matrix {
37 public:
38         ///
39         Matrix(int, double, double);
40         ///
41         void transform(double &, double &);
42 private:
43         ///
44         double m_[2][2];
45 };
46
47
48 Matrix::Matrix(int code, double x, double y)
49 {
50         double const cs = (code & 1) ? 0 : (1 - code);
51         double const sn = (code & 1) ? (2 - code) : 0;
52         m_[0][0] =  cs * x;
53         m_[0][1] =  sn * x;
54         m_[1][0] = -sn * y;
55         m_[1][1] =  cs * y;
56 }
57
58
59 void Matrix::transform(double & x, double & y)
60 {
61         double xx = m_[0][0] * x + m_[0][1] * y;
62         double yy = m_[1][0] * x + m_[1][1] * y;
63         x = xx;
64         y = yy;
65 }
66
67
68
69 namespace {
70
71 /*
72  * Internal struct of a drawing: code n x1 y1 ... xn yn, where code is:
73  * 0 = end, 1 = line, 2 = polyline, 3 = square line, 4 = square polyline
74  */
75
76
77 double const parenthHigh[] = {
78         2, 13,
79         0.9840, 0.0014, 0.7143, 0.0323, 0.4603, 0.0772,
80         0.2540, 0.1278, 0.1746, 0.1966, 0.0952, 0.3300,
81         0.0950, 0.5000, 0.0952, 0.6700, 0.1746, 0.8034,
82         0.2540, 0.8722, 0.4603, 0.9228, 0.7143, 0.9677,
83         0.9840, 0.9986,
84         0
85 };
86
87
88 double const parenth[] = {
89         2, 13,
90         0.9930, 0.0071, 0.7324, 0.0578, 0.5141, 0.1126,
91         0.3380, 0.1714, 0.2183, 0.2333, 0.0634, 0.3621,
92         0.0141, 0.5000, 0.0563, 0.6369, 0.2113, 0.7647,
93         0.3310, 0.8276, 0.5070, 0.8864, 0.7254, 0.9412,
94         0.9930, 0.9919,
95         0
96 };
97
98
99 double const brace[] = {
100         2, 21,
101         0.9492, 0.0020, 0.9379, 0.0020, 0.7458, 0.0243,
102         0.5819, 0.0527, 0.4859, 0.0892, 0.4463, 0.1278,
103         0.4463, 0.3732, 0.4011, 0.4199, 0.2712, 0.4615,
104         0.0734, 0.4919, 0.0113, 0.5000, 0.0734, 0.5081,
105         0.2712, 0.5385, 0.4011, 0.5801, 0.4463, 0.6268,
106         0.4463, 0.8722, 0.4859, 0.9108, 0.5819, 0.9473,
107         0.7458, 0.9757, 0.9379, 0.9980, 0.9492, 0.9980,
108         0
109 };
110
111
112 double const arrow[] = {
113         4, 7,
114         0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
115         0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
116         0.9500, 0.7500,
117         3, 0.5000, 0.1500, 0.5000, 0.9500,
118         0
119 };
120
121
122 double const Arrow[] = {
123         4, 7,
124         0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
125         0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
126         0.9500, 0.7500,
127         3, 0.3500, 0.5000, 0.3500, 0.9500,
128         3, 0.6500, 0.5000, 0.6500, 0.9500,
129         0
130 };
131
132
133 double const udarrow[] = {
134         2, 3,
135         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
136         2, 3,
137         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,
138         1, 0.5, 0.2,  0.5, 0.8,
139         0
140 };
141
142
143 double const Udarrow[] = {
144         2, 3,
145         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
146         2, 3,
147         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,
148         1, 0.35, 0.2, 0.35, 0.8,
149         1, 0.65, 0.2, 0.65, 0.8,
150         0
151 };
152
153
154 double const brack[] = {
155         2, 4,
156         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,  0.95, 0.95,
157         0
158 };
159
160
161 double const corner[] = {
162         2, 3,
163         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,
164         0
165 };
166
167
168 double const angle[] = {
169         2, 3,
170         1, 0,  0.05, 0.5,  1, 1,
171         0
172 };
173
174
175 double const slash[] = {
176         1, 0.95, 0.05, 0.05, 0.95,
177         0
178 };
179
180
181 double const hline[] = {
182         1, 0.00, 0.5, 1.0, 0.5,
183         0
184 };
185
186
187 double const ddot[] = {
188         1, 0.2, 0.5,  0.3, 0.5,
189         1, 0.7, 0.5,  0.8, 0.5,
190         0
191 };
192
193
194 double const dddot[] = {
195         1, 0.1, 0.5,  0.2, 0.5,
196         1, 0.45, 0.5, 0.55, 0.5,
197         1, 0.8, 0.5,  0.9, 0.5,
198         0
199 };
200
201
202 double const hline3[] = {
203         1, 0.1,   0,  0.15,  0,
204         1, 0.475, 0,  0.525, 0,
205         1, 0.85,  0,  0.9,   0,
206         0
207 };
208
209
210 double const dline3[] = {
211         1, 0.1,   0.1,   0.15,  0.15,
212         1, 0.475, 0.475, 0.525, 0.525,
213         1, 0.85,  0.85,  0.9,   0.9,
214         0
215 };
216
217
218 double const hlinesmall[] = {
219         1, 0.4, 0.5, 0.6, 0.5,
220         0
221 };
222
223
224 double const ring[] = {
225         2, 5,
226         0.5, 0.8,  0.8, 0.5,  0.5, 0.2,  0.2, 0.5,  0.5, 0.8,
227         0
228 };
229
230
231 double const vert[] = {
232         1, 0.5, 0.05,  0.5, 0.95,
233         0
234 };
235
236
237 double const  Vert[] = {
238         1, 0.3, 0.05,  0.3, 0.95,
239         1, 0.7, 0.05,  0.7, 0.95,
240         0
241 };
242
243
244 double const tilde[] = {
245         2, 4,
246         0.00, 0.8,  0.25, 0.2,  0.75, 0.8,  1.00, 0.2,
247         0
248 };
249
250
251 struct deco_struct {
252         double const * data;
253         int angle;
254 };
255
256 struct named_deco_struct {
257         char const * name;
258         double const * data;
259         int angle;
260 };
261
262 named_deco_struct deco_table[] = {
263         // Decorations
264         {"widehat",             angle,    3 },
265         {"widetilde",           tilde,    0 },
266         {"underbar",            hline,    0 },
267         {"underline",           hline,    0 },
268         {"overline",            hline,    0 },
269         {"underbrace",          brace,    1 },
270         {"overbrace",           brace,    3 },
271         {"overleftarrow",       arrow,    1 },
272         {"overrightarrow",      arrow,    3 },
273         {"overleftrightarrow",  udarrow,  1 },
274         {"xleftarrow",          arrow,    1 },
275         {"xrightarrow",         arrow,    3 },
276         {"underleftarrow",      arrow,    1 },
277         {"underrightarrow",     arrow,    3 },
278         {"underleftrightarrow", udarrow,  1 },
279
280         // Delimiters
281         {"(",              parenth,    0 },
282         {")",              parenth,    2 },
283         {"{",              brace,      0 },
284         {"}",              brace,      2 },
285         {"[",              brack,      0 },
286         {"]",              brack,      2 },
287         {"|",              vert,       0 },
288         {"/",              slash,      0 },
289         {"vert",           vert,       0 },
290         {"Vert",           Vert,       0 },
291         {"'",              slash,      1 },
292         {"backslash",      slash,      1 },
293         {"langle",         angle,      0 },
294         {"lceil",          corner,     0 },
295         {"lfloor",         corner,     1 },
296         {"rangle",         angle,      2 },
297         {"rceil",          corner,     3 },
298         {"rfloor",         corner,     2 },
299         {"downarrow",      arrow,      2 },
300         {"Downarrow",      Arrow,      2 },
301         {"uparrow",        arrow,      0 },
302         {"Uparrow",        Arrow,      0 },
303         {"updownarrow",    udarrow,    0 },
304         {"Updownarrow",    Udarrow,    0 },
305
306         // Accents
307         {"ddot",           ddot,       0 },
308         {"dddot",          dddot,      0 },
309         {"hat",            angle,      3 },
310         {"grave",          slash,      1 },
311         {"acute",          slash,      0 },
312         {"tilde",          tilde,      0 },
313         {"bar",            hline,      0 },
314         {"dot",            hlinesmall, 0 },
315         {"check",          angle,      1 },
316         {"breve",          parenth,    1 },
317         {"vec",            arrow,      3 },
318         {"mathring",       ring,       0 },
319
320         // Dots
321         {"dots",           hline3,     0 },
322         {"ldots",          hline3,     0 },
323         {"cdots",          hline3,     0 },
324         {"vdots",          hline3,     1 },
325         {"ddots",          dline3,     0 },
326         {"dotsb",          hline3,     0 },
327         {"dotsc",          hline3,     0 },
328         {"dotsi",          hline3,     0 },
329         {"dotsm",          hline3,     0 },
330         {"dotso",          hline3,     0 }
331 };
332
333
334 std::map<string, deco_struct> deco_list;
335
336 // sort the table on startup
337 struct init_deco_table {
338         init_deco_table() {
339                 unsigned const n = sizeof(deco_table) / sizeof(deco_table[0]);
340                 for (named_deco_struct * p = deco_table; p != deco_table + n; ++p) {
341                         deco_struct d;
342                         d.data  = p->data;
343                         d.angle = p->angle;
344                         deco_list[p->name]= d;
345                 }
346         }
347 };
348
349 static init_deco_table dummy;
350
351
352 deco_struct const * search_deco(string const & name)
353 {
354         std::map<string, deco_struct>::const_iterator p = deco_list.find(name);
355         return (p == deco_list.end()) ? 0 : &(p->second);
356 }
357
358
359 } // namespace anon
360
361
362 void mathed_char_dim(LyXFont const & font, unsigned char c, Dimension & dim)
363 {
364         dim.des = font_metrics::descent(c, font);
365         dim.asc = font_metrics::ascent(c, font);
366         dim.wid = mathed_char_width(font, c);
367 }
368
369
370 int mathed_char_ascent(LyXFont const & font, unsigned char c)
371 {
372         return font_metrics::ascent(c, font);
373 }
374
375
376 int mathed_char_descent(LyXFont const & font, unsigned char c)
377 {
378         return font_metrics::descent(c, font);
379 }
380
381
382 int mathed_char_width(LyXFont const & font, unsigned char c)
383 {
384         return font_metrics::width(c, font);
385 }
386
387
388 void mathed_string_dim(LyXFont const & font, string const & s, Dimension & dim)
389 {
390 #if 1
391         dim.asc = 0;
392         dim.des = 0;
393         for (string::const_iterator it = s.begin(); it != s.end(); ++it) {
394                 dim.asc = max(dim.asc, font_metrics::ascent(*it, font));
395                 dim.des = max(dim.des, font_metrics::descent(*it, font));
396         }
397 #else
398         dim.asc = font_metrics::maxAscent(font);
399         dim.des = font_metrics::maxDescent(font);
400 #endif
401         dim.wid = font_metrics::width(s, font);
402 }
403
404
405 int mathed_string_width(LyXFont const & font, string const & s)
406 {
407         return font_metrics::width(s, font);
408 }
409
410
411 void mathed_draw_deco(PainterInfo & pi, int x, int y, int w, int h,
412         string const & name)
413 {
414         if (name == ".") {
415                 pi.pain.line(x + w/2, y, x + w/2, y + h,
416                           LColor::cursor, Painter::line_onoffdash);
417                 return;
418         }
419
420         deco_struct const * mds = search_deco(name);
421         if (!mds) {
422                 lyxerr << "Deco was not found. Programming error?" << endl;
423                 lyxerr << "name: '" << name << "'" << endl;
424                 return;
425         }
426
427         int const n = (w < h) ? w : h;
428         int const r = mds->angle;
429         double const * d = mds->data;
430
431         if (h > 70 && (name == "(" || name == ")"))
432                 d = parenthHigh;
433
434         Matrix mt(r, w, h);
435         Matrix sqmt(r, n, n);
436
437         if (r > 0 && r < 3)
438                 y += h;
439
440         if (r >= 2)
441                 x += w;
442
443         for (int i = 0; d[i]; ) {
444                 int code = int(d[i++]);
445                 if (code & 1) {  // code == 1 || code == 3
446                         double xx = d[i++];
447                         double yy = d[i++];
448                         double x2 = d[i++];
449                         double y2 = d[i++];
450                         if (code == 3)
451                                 sqmt.transform(xx, yy);
452                         else
453                                 mt.transform(xx, yy);
454                         mt.transform(x2, y2);
455                         pi.pain.line(
456                                 int(x + xx + 0.5), int(y + yy + 0.5),
457                                 int(x + x2 + 0.5), int(y + y2 + 0.5),
458                                 LColor::math);
459                 }       else {
460                         int xp[32];
461                         int yp[32];
462                         int const n = int(d[i++]);
463                         for (int j = 0; j < n; ++j) {
464                                 double xx = d[i++];
465                                 double yy = d[i++];
466 //           lyxerr << ' ' << xx << ' ' << yy << ' ';
467                                 if (code == 4)
468                                         sqmt.transform(xx, yy);
469                                 else
470                                         mt.transform(xx, yy);
471                                 xp[j] = int(x + xx + 0.5);
472                                 yp[j] = int(y + yy + 0.5);
473                                 //  lyxerr << "P[" << j ' ' << xx << ' ' << yy << ' ' << x << ' ' << y << ']';
474                         }
475                         pi.pain.lines(xp, yp, n, LColor::math);
476                 }
477         }
478 }
479
480
481 // In the future maybe we use a better fonts renderer
482 void drawStr(PainterInfo & pi, LyXFont const & font,
483         int x, int y, string const & str)
484 {
485         pi.pain.text(x, y, str, font);
486 }
487
488
489 void drawStrRed(PainterInfo & pi, int x, int y, string const & str)
490 {
491         LyXFont f = pi.base.font;
492         f.setColor(LColor::latex);
493         pi.pain.text(x, y, str, f);
494 }
495
496
497 void drawStrBlack(PainterInfo & pi, int x, int y, string const & str)
498 {
499         LyXFont f = pi.base.font;
500         f.setColor(LColor::foreground);
501         pi.pain.text(x, y, str, f);
502 }
503
504
505 void drawChar(PainterInfo & pi, LyXFont const & font, int x, int y, char c)
506 {
507         pi.pain.text(x, y, c, font);
508 }
509
510
511 void math_font_max_dim(LyXFont const & font, int & asc, int & des)
512 {
513         asc = font_metrics::maxAscent(font);
514         des = font_metrics::maxDescent(font);
515 }
516
517
518 struct fontinfo {
519         string cmd_;
520         LyXFont::FONT_FAMILY family_;
521         LyXFont::FONT_SERIES series_;
522         LyXFont::FONT_SHAPE  shape_;
523         LColor::color        color_;
524 };
525
526
527 LyXFont::FONT_FAMILY const inh_family = LyXFont::INHERIT_FAMILY;
528 LyXFont::FONT_SERIES const inh_series = LyXFont::INHERIT_SERIES;
529 LyXFont::FONT_SHAPE  const inh_shape  = LyXFont::INHERIT_SHAPE;
530
531
532 // mathnormal should be the first, otherwise the fallback further down
533 // does not work
534 fontinfo fontinfos[] = {
535         // math fonts
536         {"mathnormal",    inh_family, LyXFont::MEDIUM_SERIES,
537                           LyXFont::ITALIC_SHAPE, LColor::math},
538         {"mathbf",        inh_family, LyXFont::BOLD_SERIES,
539                           inh_shape, LColor::math},
540         {"mathcal",       LyXFont::CMSY_FAMILY, inh_series,
541                           inh_shape, LColor::math},
542         {"mathfrak",      LyXFont::EUFRAK_FAMILY, inh_series,
543                           inh_shape, LColor::math},
544         {"mathrm",        LyXFont::ROMAN_FAMILY, inh_series,
545                           inh_shape, LColor::math},
546         {"mathsf",        LyXFont::SANS_FAMILY, inh_series,
547                           inh_shape, LColor::math},
548         {"mathbb",        LyXFont::MSB_FAMILY, inh_series,
549                           inh_shape, LColor::math},
550         {"mathtt",        LyXFont::TYPEWRITER_FAMILY, inh_series,
551                           inh_shape, LColor::math},
552         {"mathit",        inh_family, inh_series,
553                           LyXFont::ITALIC_SHAPE, LColor::math},
554         {"cmex",          LyXFont::CMEX_FAMILY, inh_series,
555                           inh_shape, LColor::none},
556         {"cmm",           LyXFont::CMM_FAMILY, inh_series,
557                           inh_shape, LColor::none},
558         {"cmr",           LyXFont::CMR_FAMILY, inh_series,
559                           inh_shape, LColor::none},
560         {"cmsy",          LyXFont::CMSY_FAMILY, inh_series,
561                           inh_shape, LColor::none},
562         {"eufrak",        LyXFont::EUFRAK_FAMILY, inh_series,
563                           inh_shape, LColor::none},
564         {"msa",           LyXFont::MSA_FAMILY, inh_series,
565                           inh_shape, LColor::none},
566         {"msb",           LyXFont::MSB_FAMILY, inh_series,
567                           inh_shape, LColor::none},
568         {"wasy",          LyXFont::WASY_FAMILY, inh_series,
569                           inh_shape, LColor::none},
570
571         // Text fonts
572         {"text",          inh_family, inh_series,
573                           inh_shape, LColor::foreground},
574         {"textbf",        inh_family, LyXFont::BOLD_SERIES,
575                           inh_shape, LColor::foreground},
576         {"textit",        inh_family, inh_series,
577                           LyXFont::ITALIC_SHAPE, LColor::foreground},
578         {"textmd",        inh_family, LyXFont::MEDIUM_SERIES,
579                           inh_shape, LColor::foreground},
580         {"textnormal",    inh_family, inh_series,
581                           LyXFont::UP_SHAPE, LColor::foreground},
582         {"textrm",        LyXFont::ROMAN_FAMILY,
583                           inh_series,LyXFont::UP_SHAPE,LColor::foreground},
584         {"textsc",        inh_family, inh_series,
585                           LyXFont::SMALLCAPS_SHAPE, LColor::foreground},
586         {"textsf",        LyXFont::SANS_FAMILY, inh_series,
587                           inh_shape, LColor::foreground},
588         {"textsl",        inh_family, inh_series,
589                           LyXFont::SLANTED_SHAPE, LColor::foreground},
590         {"texttt",        LyXFont::TYPEWRITER_FAMILY, inh_series,
591                           inh_shape, LColor::foreground},
592         {"textup",        inh_family, inh_series,
593                           LyXFont::UP_SHAPE, LColor::foreground},
594
595         // TIPA support
596         {"textipa",       inh_family, inh_series,
597                           inh_shape, LColor::foreground},
598
599         // LyX internal usage
600         {"lyxtex",        inh_family, inh_series,
601                           inh_shape, LColor::latex},
602         {"lyxert",        LyXFont::TYPEWRITER_FAMILY, inh_series,
603                           inh_shape, LColor::latex},
604         {"lyxsymbol",     LyXFont::SYMBOL_FAMILY, inh_series,
605                           inh_shape, LColor::math},
606         {"lyxboldsymbol", LyXFont::SYMBOL_FAMILY, LyXFont::BOLD_SERIES,
607                           inh_shape, LColor::math},
608         {"lyxitsymbol",   LyXFont::SYMBOL_FAMILY, inh_series,
609                     LyXFont::ITALIC_SHAPE, LColor::math},
610         {"lyxblacktext",  LyXFont::ROMAN_FAMILY, LyXFont::MEDIUM_SERIES,
611                     LyXFont::UP_SHAPE, LColor::foreground},
612         {"lyxnochange",   inh_family, inh_series,
613                     inh_shape, LColor::foreground},
614         {"lyxfakebb",     LyXFont::TYPEWRITER_FAMILY, LyXFont::BOLD_SERIES,
615                           LyXFont::UP_SHAPE, LColor::math},
616         {"lyxfakecal",    LyXFont::SANS_FAMILY, LyXFont::MEDIUM_SERIES,
617                           LyXFont::ITALIC_SHAPE, LColor::math},
618         {"lyxfakefrak",   LyXFont::ROMAN_FAMILY, LyXFont::BOLD_SERIES,
619                           LyXFont::ITALIC_SHAPE, LColor::math}
620 };
621
622
623 fontinfo * lookupFont(string const & name)
624 {
625         //lyxerr << "searching font '" << name << "'" << endl;
626         int const n = sizeof(fontinfos) / sizeof(fontinfo);
627         for (int i = 0; i < n; ++i)
628                 if (fontinfos[i].cmd_ == name) {
629                         //lyxerr << "found '" << i << "'" << endl;
630                         return fontinfos + i;
631                 }
632         return 0;
633 }
634
635
636 fontinfo * searchFont(string const & name)
637 {
638         fontinfo * f = lookupFont(name);
639         return f ? f : fontinfos;
640         // this should be mathnormal
641         //return searchFont("mathnormal");
642 }
643
644
645 bool isFontName(string const & name)
646 {
647         return lookupFont(name);
648 }
649
650
651 LyXFont getFont(string const & name)
652 {
653         LyXFont font;
654         augmentFont(font, name);
655         return font;
656 }
657
658
659 void fakeFont(string const & orig, string const & fake)
660 {
661         fontinfo * forig = searchFont(orig);
662         fontinfo * ffake = searchFont(fake);
663         if (forig && ffake) {
664                 forig->family_ = ffake->family_;
665                 forig->series_ = ffake->series_;
666                 forig->shape_  = ffake->shape_;
667                 forig->color_  = ffake->color_;
668         } else {
669                 lyxerr << "Can't fake font '" << orig << "' with '"
670                        << fake << "'" << endl;
671         }
672 }
673
674
675 void augmentFont(LyXFont & font, string const & name)
676 {
677         static bool initialized = false;
678         if (!initialized) {
679                 initialized = true;
680                 // fake fonts if necessary
681                 if (!lyx_gui::font_available(getFont("mathfrak")))
682                         fakeFont("mathfrak", "lyxfakefrak");
683                 if (!lyx_gui::font_available(getFont("mathcal")))
684                         fakeFont("mathcal", "lyxfakecal");
685         }
686         fontinfo * info = searchFont(name);
687         if (info->family_ != inh_family)
688                 font.setFamily(info->family_);
689         if (info->series_ != inh_series)
690                 font.setSeries(info->series_);
691         if (info->shape_ != inh_shape)
692                 font.setShape(info->shape_);
693         if (info->color_ != LColor::none)
694                 font.setColor(info->color_);
695 }
696
697
698 string asString(MathArray const & ar)
699 {
700         std::ostringstream os;
701         WriteStream ws(os);
702         ws << ar;
703         return os.str();
704 }
705
706
707 void asArray(string const & str, MathArray & ar)
708 {
709         mathed_parse_cell(ar, str);
710 }