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