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