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