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