]> git.lyx.org Git - lyx.git/blob - src/mathed/MathSupport.cpp
- fix requirements of \dddot
[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         {"\\",             slash,      1 },
307         {"backslash",      slash,      1 },
308         {"langle",         angle,      0 },
309         {"lceil",          corner,     0 },
310         {"lfloor",         corner,     1 },
311         {"rangle",         angle,      2 },
312         {"rceil",          corner,     3 },
313         {"rfloor",         corner,     2 },
314         {"downarrow",      arrow,      2 },
315         {"Downarrow",      Arrow,      2 },
316         {"uparrow",        arrow,      0 },
317         {"Uparrow",        Arrow,      0 },
318         {"updownarrow",    udarrow,    0 },
319         {"Updownarrow",    Udarrow,    0 },
320
321         // Accents
322         {"ddot",           ddot,       0 },
323         {"dddot",          dddot,      0 },
324         {"ddddot",         ddddot,     0 },
325         {"hat",            angle,      3 },
326         {"grave",          slash,      1 },
327         {"acute",          slash,      0 },
328         {"tilde",          tilde,      0 },
329         {"bar",            hline,      0 },
330         {"dot",            hlinesmall, 0 },
331         {"check",          angle,      1 },
332         {"breve",          parenth,    1 },
333         {"vec",            arrow,      3 },
334         {"mathring",       ring,       0 },
335
336         // Dots
337         {"dots",           hline3,     0 },
338         {"ldots",          hline3,     0 },
339         {"cdots",          hline3,     0 },
340         {"vdots",          hline3,     1 },
341         {"ddots",          dline3,     0 },
342         {"adots",          dline3,     1 },
343         {"iddots",         dline3,     1 },
344         {"dotsb",          hline3,     0 },
345         {"dotsc",          hline3,     0 },
346         {"dotsi",          hline3,     0 },
347         {"dotsm",          hline3,     0 },
348         {"dotso",          hline3,     0 }
349 };
350
351
352 map<docstring, deco_struct> deco_list;
353
354 // sort the table on startup
355 class init_deco_table {
356 public:
357         init_deco_table() {
358                 unsigned const n = sizeof(deco_table) / sizeof(deco_table[0]);
359                 for (named_deco_struct * p = deco_table; p != deco_table + n; ++p) {
360                         deco_struct d;
361                         d.data  = p->data;
362                         d.angle = p->angle;
363                         deco_list[from_ascii(p->name)] = d;
364                 }
365         }
366 };
367
368 static init_deco_table dummy;
369
370
371 deco_struct const * search_deco(docstring const & name)
372 {
373         map<docstring, deco_struct>::const_iterator p = deco_list.find(name);
374         return p == deco_list.end() ? 0 : &(p->second);
375 }
376
377
378 } // namespace anon
379
380
381 int mathed_char_width(FontInfo const & font, char_type c)
382 {
383         return theFontMetrics(font).width(c);
384 }
385
386
387 int mathed_char_kerning(FontInfo const & font, char_type c)
388 {
389         frontend::FontMetrics const & fm = theFontMetrics(font);
390         return fm.rbearing(c) - fm.width(c);
391 }
392
393
394 void mathed_string_dim(FontInfo const & font,
395                        docstring const & s,
396                        Dimension & dim)
397 {
398         frontend::FontMetrics const & fm = theFontMetrics(font);
399         dim.asc = 0;
400         dim.des = 0;
401         for (docstring::const_iterator it = s.begin();
402              it != s.end();
403              ++it) {
404                 dim.asc = max(dim.asc, fm.ascent(*it));
405                 dim.des = max(dim.des, fm.descent(*it));
406         }
407         dim.wid = fm.width(s);
408 }
409
410
411 int mathed_string_width(FontInfo const & font, docstring const & s)
412 {
413         return theFontMetrics(font).width(s);
414 }
415
416
417 void mathed_draw_deco(PainterInfo & pi, int x, int y, int w, int h,
418         docstring const & name)
419 {
420         if (name == ".") {
421                 pi.pain.line(x + w/2, y, x + w/2, y + h,
422                           Color_cursor, Painter::line_onoffdash);
423                 return;
424         }
425
426         deco_struct const * mds = search_deco(name);
427         if (!mds) {
428                 lyxerr << "Deco was not found. Programming error?" << endl;
429                 lyxerr << "name: '" << to_utf8(name) << "'" << endl;
430                 return;
431         }
432
433         int const n = (w < h) ? w : h;
434         int const r = mds->angle;
435         double const * d = mds->data;
436
437         if (h > 70 && (name == "(" || name == ")"))
438                 d = parenthHigh;
439
440         Matrix mt(r, w, h);
441         Matrix sqmt(r, n, n);
442
443         if (r > 0 && r < 3)
444                 y += h;
445
446         if (r >= 2)
447                 x += w;
448
449         for (int i = 0; d[i]; ) {
450                 int code = int(d[i++]);
451                 if (code & 1) {  // code == 1 || code == 3
452                         double xx = d[i++];
453                         double yy = d[i++];
454                         double x2 = d[i++];
455                         double y2 = d[i++];
456                         if (code == 3)
457                                 sqmt.transform(xx, yy);
458                         else
459                                 mt.transform(xx, yy);
460                         mt.transform(x2, y2);
461                         pi.pain.line(
462                                 int(x + xx + 0.5), int(y + yy + 0.5),
463                                 int(x + x2 + 0.5), int(y + y2 + 0.5),
464                                 Color_math);
465                 } else {
466                         int xp[32];
467                         int yp[32];
468                         int const n = int(d[i++]);
469                         for (int j = 0; j < n; ++j) {
470                                 double xx = d[i++];
471                                 double yy = d[i++];
472 //           lyxerr << ' ' << xx << ' ' << yy << ' ';
473                                 if (code == 4)
474                                         sqmt.transform(xx, yy);
475                                 else
476                                         mt.transform(xx, yy);
477                                 xp[j] = int(x + xx + 0.5);
478                                 yp[j] = int(y + yy + 0.5);
479                                 //  lyxerr << "P[" << j ' ' << xx << ' ' << yy << ' ' << x << ' ' << y << ']';
480                         }
481                         pi.pain.lines(xp, yp, n, Color_math);
482                 }
483         }
484 }
485
486
487 void drawStrRed(PainterInfo & pi, int x, int y, docstring const & str)
488 {
489         FontInfo f = pi.base.font;
490         f.setColor(Color_latex);
491         pi.pain.text(x, y, str, f);
492 }
493
494
495 void drawStrBlack(PainterInfo & pi, int x, int y, docstring const & str)
496 {
497         FontInfo f = pi.base.font;
498         f.setColor(Color_foreground);
499         pi.pain.text(x, y, str, f);
500 }
501
502
503 void math_font_max_dim(FontInfo const & font, int & asc, int & des)
504 {
505         frontend::FontMetrics const & fm = theFontMetrics(font);
506         asc = fm.maxAscent();
507         des = fm.maxDescent();
508 }
509
510
511 struct fontinfo {
512         string cmd_;
513         FontFamily family_;
514         FontSeries series_;
515         FontShape  shape_;
516         ColorCode        color_;
517 };
518
519
520 FontFamily const inh_family = INHERIT_FAMILY;
521 FontSeries const inh_series = INHERIT_SERIES;
522 FontShape  const inh_shape  = INHERIT_SHAPE;
523
524
525 // mathnormal should be the first, otherwise the fallback further down
526 // does not work
527 fontinfo fontinfos[] = {
528         // math fonts
529         {"mathnormal",    ROMAN_FAMILY, MEDIUM_SERIES,
530                           ITALIC_SHAPE, Color_math},
531         {"mathbf",        inh_family, BOLD_SERIES,
532                           inh_shape, Color_math},
533         {"mathcal",       CMSY_FAMILY, inh_series,
534                           inh_shape, Color_math},
535         {"mathfrak",      EUFRAK_FAMILY, inh_series,
536                           inh_shape, Color_math},
537         {"mathrm",        ROMAN_FAMILY, inh_series,
538                           UP_SHAPE, Color_math},
539         {"mathsf",        SANS_FAMILY, inh_series,
540                           inh_shape, Color_math},
541         {"mathbb",        MSB_FAMILY, inh_series,
542                           inh_shape, Color_math},
543         {"mathtt",        TYPEWRITER_FAMILY, inh_series,
544                           inh_shape, Color_math},
545         {"mathit",        inh_family, inh_series,
546                           ITALIC_SHAPE, Color_math},
547         {"cmex",          CMEX_FAMILY, inh_series,
548                           inh_shape, Color_math},
549         {"cmm",           CMM_FAMILY, inh_series,
550                           inh_shape, Color_math},
551         {"cmr",           CMR_FAMILY, inh_series,
552                           inh_shape, Color_math},
553         {"cmsy",          CMSY_FAMILY, inh_series,
554                           inh_shape, Color_math},
555         {"eufrak",        EUFRAK_FAMILY, inh_series,
556                           inh_shape, Color_math},
557         {"msa",           MSA_FAMILY, inh_series,
558                           inh_shape, Color_math},
559         {"msb",           MSB_FAMILY, inh_series,
560                           inh_shape, Color_math},
561         {"wasy",          WASY_FAMILY, inh_series,
562                           inh_shape, Color_math},
563         {"esint",         ESINT_FAMILY, inh_series,
564                           inh_shape, Color_math},
565
566         // Text fonts
567         {"text",          inh_family, inh_series,
568                           inh_shape, Color_foreground},
569         {"textbf",        inh_family, BOLD_SERIES,
570                           inh_shape, Color_foreground},
571         {"textit",        inh_family, inh_series,
572                           ITALIC_SHAPE, Color_foreground},
573         {"textmd",        inh_family, MEDIUM_SERIES,
574                           inh_shape, Color_foreground},
575         {"textnormal",    inh_family, inh_series,
576                           UP_SHAPE, Color_foreground},
577         {"textrm",        ROMAN_FAMILY,
578                           inh_series, UP_SHAPE,Color_foreground},
579         {"textsc",        inh_family, inh_series,
580                           SMALLCAPS_SHAPE, Color_foreground},
581         {"textsf",        SANS_FAMILY, inh_series,
582                           inh_shape, Color_foreground},
583         {"textsl",        inh_family, inh_series,
584                           SLANTED_SHAPE, Color_foreground},
585         {"texttt",        TYPEWRITER_FAMILY, inh_series,
586                           inh_shape, Color_foreground},
587         {"textup",        inh_family, inh_series,
588                           UP_SHAPE, Color_foreground},
589
590         // TIPA support
591         {"textipa",       inh_family, inh_series,
592                           inh_shape, Color_foreground},
593
594         // LyX internal usage
595         {"lyxtex",        inh_family, inh_series,
596                           UP_SHAPE, Color_latex},
597         {"lyxsymbol",     SYMBOL_FAMILY, inh_series,
598                           inh_shape, Color_math},
599         {"lyxboldsymbol", SYMBOL_FAMILY, BOLD_SERIES,
600                           inh_shape, Color_math},
601         {"lyxblacktext",  ROMAN_FAMILY, MEDIUM_SERIES,
602                           UP_SHAPE, Color_foreground},
603         {"lyxnochange",   inh_family, inh_series,
604                           inh_shape, Color_foreground},
605         {"lyxfakebb",     TYPEWRITER_FAMILY, BOLD_SERIES,
606                           UP_SHAPE, Color_math},
607         {"lyxfakecal",    SANS_FAMILY, MEDIUM_SERIES,
608                           ITALIC_SHAPE, Color_math},
609         {"lyxfakefrak",   ROMAN_FAMILY, BOLD_SERIES,
610                           ITALIC_SHAPE, Color_math}
611 };
612
613
614 fontinfo * lookupFont(docstring const & name0)
615 {
616         //lyxerr << "searching font '" << name << "'" << endl;
617         int const n = sizeof(fontinfos) / sizeof(fontinfo);
618         string name = to_utf8(name0);
619         for (int i = 0; i < n; ++i)
620                 if (fontinfos[i].cmd_ == name) {
621                         //lyxerr << "found '" << i << "'" << endl;
622                         return fontinfos + i;
623                 }
624         return 0;
625 }
626
627
628 fontinfo * searchFont(docstring const & name)
629 {
630         fontinfo * f = lookupFont(name);
631         return f ? f : fontinfos;
632         // this should be mathnormal
633         //return searchFont("mathnormal");
634 }
635
636
637 bool isFontName(docstring const & name)
638 {
639         return lookupFont(name);
640 }
641
642
643 FontInfo getFont(docstring const & name)
644 {
645         FontInfo font;
646         augmentFont(font, name);
647         return font;
648 }
649
650
651 void fakeFont(docstring const & orig, docstring const & fake)
652 {
653         fontinfo * forig = searchFont(orig);
654         fontinfo * ffake = searchFont(fake);
655         if (forig && ffake) {
656                 forig->family_ = ffake->family_;
657                 forig->series_ = ffake->series_;
658                 forig->shape_  = ffake->shape_;
659                 forig->color_  = ffake->color_;
660         } else {
661                 lyxerr << "Can't fake font '" << to_utf8(orig) << "' with '"
662                        << to_utf8(fake) << "'" << endl;
663         }
664 }
665
666
667 void augmentFont(FontInfo & font, docstring const & name)
668 {
669         static bool initialized = false;
670         if (!initialized) {
671                 initialized = true;
672                 // fake fonts if necessary
673                 if (!theFontLoader().available(getFont(from_ascii("mathfrak"))))
674                         fakeFont(from_ascii("mathfrak"), from_ascii("lyxfakefrak"));
675                 if (!theFontLoader().available(getFont(from_ascii("mathcal"))))
676                         fakeFont(from_ascii("mathcal"), from_ascii("lyxfakecal"));
677         }
678         fontinfo * info = searchFont(name);
679         if (info->family_ != inh_family)
680                 font.setFamily(info->family_);
681         if (info->series_ != inh_series)
682                 font.setSeries(info->series_);
683         if (info->shape_ != inh_shape)
684                 font.setShape(info->shape_);
685         if (info->color_ != Color_none)
686                 font.setColor(info->color_);
687 }
688
689
690 docstring asString(MathData const & ar)
691 {
692         odocstringstream os;
693         WriteStream ws(os);
694         ws << ar;
695         return os.str();
696 }
697
698
699 void asArray(docstring const & str, MathData & ar, Parse::flags pf)
700 {
701         bool quiet = pf & Parse::QUIET;
702         if ((str.size() == 1 && quiet) || (!mathed_parse_cell(ar, str, pf) && quiet))
703                 mathed_parse_cell(ar, str, pf | Parse::VERBATIM);
704 }
705
706
707 docstring asString(InsetMath const & inset)
708 {
709         odocstringstream os;
710         WriteStream ws(os);
711         inset.write(ws);
712         return os.str();
713 }
714
715
716 docstring asString(MathAtom const & at)
717 {
718         odocstringstream os;
719         WriteStream ws(os);
720         at->write(ws);
721         return os.str();
722 }
723
724
725 } // namespace lyx