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