]> git.lyx.org Git - features.git/blob - src/mathed/MathSupport.C
Account for real width of nucleus when positioning superscripts in mathed
[features.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 "MathStream.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
31 namespace lyx {
32
33 using frontend::Painter;
34
35 using std::max;
36 using std::endl;
37 using std::vector;
38
39
40 ///
41 class Matrix {
42 public:
43         ///
44         Matrix(int, double, double);
45         ///
46         void transform(double &, double &);
47 private:
48         ///
49         double m_[2][2];
50 };
51
52
53 Matrix::Matrix(int code, double x, double y)
54 {
55         double const cs = (code & 1) ? 0 : (1 - code);
56         double const sn = (code & 1) ? (2 - code) : 0;
57         m_[0][0] =  cs * x;
58         m_[0][1] =  sn * x;
59         m_[1][0] = -sn * y;
60         m_[1][1] =  cs * y;
61 }
62
63
64 void Matrix::transform(double & x, double & y)
65 {
66         double xx = m_[0][0] * x + m_[0][1] * y;
67         double yy = m_[1][0] * x + m_[1][1] * y;
68         x = xx;
69         y = yy;
70 }
71
72
73
74 namespace {
75
76 /*
77  * Internal struct of a drawing: code n x1 y1 ... xn yn, where code is:
78  * 0 = end, 1 = line, 2 = polyline, 3 = square line, 4 = square polyline
79  */
80
81
82 double const parenthHigh[] = {
83         2, 13,
84         0.9840, 0.0014, 0.7143, 0.0323, 0.4603, 0.0772,
85         0.2540, 0.1278, 0.1746, 0.1966, 0.0952, 0.3300,
86         0.0950, 0.5000, 0.0952, 0.6700, 0.1746, 0.8034,
87         0.2540, 0.8722, 0.4603, 0.9228, 0.7143, 0.9677,
88         0.9840, 0.9986,
89         0
90 };
91
92
93 double const parenth[] = {
94         2, 13,
95         0.9930, 0.0071, 0.7324, 0.0578, 0.5141, 0.1126,
96         0.3380, 0.1714, 0.2183, 0.2333, 0.0634, 0.3621,
97         0.0141, 0.5000, 0.0563, 0.6369, 0.2113, 0.7647,
98         0.3310, 0.8276, 0.5070, 0.8864, 0.7254, 0.9412,
99         0.9930, 0.9919,
100         0
101 };
102
103
104 double const brace[] = {
105         2, 21,
106         0.9492, 0.0020, 0.9379, 0.0020, 0.7458, 0.0243,
107         0.5819, 0.0527, 0.4859, 0.0892, 0.4463, 0.1278,
108         0.4463, 0.3732, 0.4011, 0.4199, 0.2712, 0.4615,
109         0.0734, 0.4919, 0.0113, 0.5000, 0.0734, 0.5081,
110         0.2712, 0.5385, 0.4011, 0.5801, 0.4463, 0.6268,
111         0.4463, 0.8722, 0.4859, 0.9108, 0.5819, 0.9473,
112         0.7458, 0.9757, 0.9379, 0.9980, 0.9492, 0.9980,
113         0
114 };
115
116
117 double const arrow[] = {
118         4, 7,
119         0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
120         0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
121         0.9500, 0.7500,
122         3, 0.5000, 0.1500, 0.5000, 0.9500,
123         0
124 };
125
126
127 double const Arrow[] = {
128         4, 7,
129         0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
130         0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
131         0.9500, 0.7500,
132         3, 0.3500, 0.5000, 0.3500, 0.9500,
133         3, 0.6500, 0.5000, 0.6500, 0.9500,
134         0
135 };
136
137
138 double const udarrow[] = {
139         2, 3,
140         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
141         2, 3,
142         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,
143         1, 0.5, 0.2,  0.5, 0.8,
144         0
145 };
146
147
148 double const Udarrow[] = {
149         2, 3,
150         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
151         2, 3,
152         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,
153         1, 0.35, 0.2, 0.35, 0.8,
154         1, 0.65, 0.2, 0.65, 0.8,
155         0
156 };
157
158
159 double const brack[] = {
160         2, 4,
161         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,  0.95, 0.95,
162         0
163 };
164
165
166 double const corner[] = {
167         2, 3,
168         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,
169         0
170 };
171
172
173 double const angle[] = {
174         2, 3,
175         1, 0,  0.05, 0.5,  1, 1,
176         0
177 };
178
179
180 double const slash[] = {
181         1, 0.95, 0.05, 0.05, 0.95,
182         0
183 };
184
185
186 double const hline[] = {
187         1, 0.00, 0.5, 1.0, 0.5,
188         0
189 };
190
191
192 double const ddot[] = {
193         1, 0.2, 0.5,  0.3, 0.5,
194         1, 0.7, 0.5,  0.8, 0.5,
195         0
196 };
197
198
199 double const dddot[] = {
200         1, 0.1, 0.5,  0.2, 0.5,
201         1, 0.45, 0.5, 0.55, 0.5,
202         1, 0.8, 0.5,  0.9, 0.5,
203         0
204 };
205
206
207 double const hline3[] = {
208         1, 0.1,   0,  0.15,  0,
209         1, 0.475, 0,  0.525, 0,
210         1, 0.85,  0,  0.9,   0,
211         0
212 };
213
214
215 double const dline3[] = {
216         1, 0.1,   0.1,   0.15,  0.15,
217         1, 0.475, 0.475, 0.525, 0.525,
218         1, 0.85,  0.85,  0.9,   0.9,
219         0
220 };
221
222
223 double const hlinesmall[] = {
224         1, 0.4, 0.5, 0.6, 0.5,
225         0
226 };
227
228
229 double const ring[] = {
230         2, 5,
231         0.5, 0.8,  0.8, 0.5,  0.5, 0.2,  0.2, 0.5,  0.5, 0.8,
232         0
233 };
234
235
236 double const vert[] = {
237         1, 0.5, 0.05,  0.5, 0.95,
238         0
239 };
240
241
242 double const  Vert[] = {
243         1, 0.3, 0.05,  0.3, 0.95,
244         1, 0.7, 0.05,  0.7, 0.95,
245         0
246 };
247
248
249 double const tilde[] = {
250         2, 4,
251         0.00, 0.8,  0.25, 0.2,  0.75, 0.8,  1.00, 0.2,
252         0
253 };
254
255
256 struct deco_struct {
257         double const * data;
258         int angle;
259 };
260
261 struct named_deco_struct {
262         char const * name;
263         double const * data;
264         int angle;
265 };
266
267 named_deco_struct deco_table[] = {
268         // Decorations
269         {"widehat",             angle,    3 },
270         {"widetilde",           tilde,    0 },
271         {"underbar",            hline,    0 },
272         {"underline",           hline,    0 },
273         {"overline",            hline,    0 },
274         {"underbrace",          brace,    1 },
275         {"overbrace",           brace,    3 },
276         {"overleftarrow",       arrow,    1 },
277         {"overrightarrow",      arrow,    3 },
278         {"overleftrightarrow",  udarrow,  1 },
279         {"xleftarrow",          arrow,    1 },
280         {"xrightarrow",         arrow,    3 },
281         {"underleftarrow",      arrow,    1 },
282         {"underrightarrow",     arrow,    3 },
283         {"underleftrightarrow", udarrow,  1 },
284
285         // Delimiters
286         {"(",              parenth,    0 },
287         {")",              parenth,    2 },
288         {"{",              brace,      0 },
289         {"}",              brace,      2 },
290         {"lbrace",         brace,      0 },
291         {"rbrace",         brace,      2 },
292         {"[",              brack,      0 },
293         {"]",              brack,      2 },
294         {"|",              vert,       0 },
295         {"/",              slash,      0 },
296         {"vert",           vert,       0 },
297         {"Vert",           Vert,       0 },
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 std::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         std::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(LyXFont const & font, char_type c)
371 {
372         return theFontMetrics(font).width(c);
373 }
374
375
376 int mathed_char_kerning(LyXFont 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(LyXFont 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(LyXFont 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                           LColor::cursor, Painter::line_onoffdash);
412                 return;
413         }
414
415         deco_struct const * mds = search_deco(name);
416         if (!mds) {
417                 lyxerr << "Deco was not found. Programming error?" << endl;
418                 lyxerr << "name: '" << 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                                 LColor::math);
454                 } else {
455                         int xp[32];
456                         int yp[32];
457                         int const n = int(d[i++]);
458                         for (int j = 0; j < n; ++j) {
459                                 double xx = d[i++];
460                                 double yy = d[i++];
461 //           lyxerr << ' ' << xx << ' ' << yy << ' ';
462                                 if (code == 4)
463                                         sqmt.transform(xx, yy);
464                                 else
465                                         mt.transform(xx, yy);
466                                 xp[j] = int(x + xx + 0.5);
467                                 yp[j] = int(y + yy + 0.5);
468                                 //  lyxerr << "P[" << j ' ' << xx << ' ' << yy << ' ' << x << ' ' << y << ']';
469                         }
470                         pi.pain.lines(xp, yp, n, LColor::math);
471                 }
472         }
473 }
474
475
476 void drawStrRed(PainterInfo & pi, int x, int y, docstring const & str)
477 {
478         LyXFont f = pi.base.font;
479         f.setColor(LColor::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         LyXFont f = pi.base.font;
487         f.setColor(LColor::foreground);
488         pi.pain.text(x, y, str, f);
489 }
490
491
492 void math_font_max_dim(LyXFont 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         std::string cmd_;
502         LyXFont::FONT_FAMILY family_;
503         LyXFont::FONT_SERIES series_;
504         LyXFont::FONT_SHAPE  shape_;
505         LColor::color        color_;
506 };
507
508
509 LyXFont::FONT_FAMILY const inh_family = LyXFont::INHERIT_FAMILY;
510 LyXFont::FONT_SERIES const inh_series = LyXFont::INHERIT_SERIES;
511 LyXFont::FONT_SHAPE  const inh_shape  = LyXFont::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",    LyXFont::ROMAN_FAMILY, LyXFont::MEDIUM_SERIES,
519                           LyXFont::ITALIC_SHAPE, LColor::math},
520         {"mathbf",        inh_family, LyXFont::BOLD_SERIES,
521                           inh_shape, LColor::math},
522         {"mathcal",       LyXFont::CMSY_FAMILY, inh_series,
523                           inh_shape, LColor::math},
524         {"mathfrak",      LyXFont::EUFRAK_FAMILY, inh_series,
525                           inh_shape, LColor::math},
526         {"mathrm",        LyXFont::ROMAN_FAMILY, inh_series,
527                           LyXFont::UP_SHAPE, LColor::math},
528         {"mathsf",        LyXFont::SANS_FAMILY, inh_series,
529                           inh_shape, LColor::math},
530         {"mathbb",        LyXFont::MSB_FAMILY, inh_series,
531                           inh_shape, LColor::math},
532         {"mathtt",        LyXFont::TYPEWRITER_FAMILY, inh_series,
533                           inh_shape, LColor::math},
534         {"mathit",        inh_family, inh_series,
535                           LyXFont::ITALIC_SHAPE, LColor::math},
536         {"cmex",          LyXFont::CMEX_FAMILY, inh_series,
537                           inh_shape, LColor::math},
538         {"cmm",           LyXFont::CMM_FAMILY, inh_series,
539                           inh_shape, LColor::math},
540         {"cmr",           LyXFont::CMR_FAMILY, inh_series,
541                           inh_shape, LColor::math},
542         {"cmsy",          LyXFont::CMSY_FAMILY, inh_series,
543                           inh_shape, LColor::math},
544         {"eufrak",        LyXFont::EUFRAK_FAMILY, inh_series,
545                           inh_shape, LColor::math},
546         {"msa",           LyXFont::MSA_FAMILY, inh_series,
547                           inh_shape, LColor::math},
548         {"msb",           LyXFont::MSB_FAMILY, inh_series,
549                           inh_shape, LColor::math},
550         {"wasy",          LyXFont::WASY_FAMILY, inh_series,
551                           inh_shape, LColor::none},
552         {"esint",         LyXFont::ESINT_FAMILY, inh_series,
553                           inh_shape, LColor::none},
554
555         // Text fonts
556         {"text",          inh_family, inh_series,
557                           inh_shape, LColor::foreground},
558         {"textbf",        inh_family, LyXFont::BOLD_SERIES,
559                           inh_shape, LColor::foreground},
560         {"textit",        inh_family, inh_series,
561                           LyXFont::ITALIC_SHAPE, LColor::foreground},
562         {"textmd",        inh_family, LyXFont::MEDIUM_SERIES,
563                           inh_shape, LColor::foreground},
564         {"textnormal",    inh_family, inh_series,
565                           LyXFont::UP_SHAPE, LColor::foreground},
566         {"textrm",        LyXFont::ROMAN_FAMILY,
567                           inh_series, LyXFont::UP_SHAPE,LColor::foreground},
568         {"textsc",        inh_family, inh_series,
569                           LyXFont::SMALLCAPS_SHAPE, LColor::foreground},
570         {"textsf",        LyXFont::SANS_FAMILY, inh_series,
571                           inh_shape, LColor::foreground},
572         {"textsl",        inh_family, inh_series,
573                           LyXFont::SLANTED_SHAPE, LColor::foreground},
574         {"texttt",        LyXFont::TYPEWRITER_FAMILY, inh_series,
575                           inh_shape, LColor::foreground},
576         {"textup",        inh_family, inh_series,
577                           LyXFont::UP_SHAPE, LColor::foreground},
578
579         // TIPA support
580         {"textipa",       inh_family, inh_series,
581                           inh_shape, LColor::foreground},
582
583         // LyX internal usage
584         {"lyxtex",        inh_family, inh_series,
585                           LyXFont::UP_SHAPE, LColor::latex},
586         {"lyxsymbol",     LyXFont::SYMBOL_FAMILY, inh_series,
587                           inh_shape, LColor::math},
588         {"lyxboldsymbol", LyXFont::SYMBOL_FAMILY, LyXFont::BOLD_SERIES,
589                           inh_shape, LColor::math},
590         {"lyxblacktext",  LyXFont::ROMAN_FAMILY, LyXFont::MEDIUM_SERIES,
591                           LyXFont::UP_SHAPE, LColor::foreground},
592         {"lyxnochange",   inh_family, inh_series,
593                           inh_shape, LColor::foreground},
594         {"lyxfakebb",     LyXFont::TYPEWRITER_FAMILY, LyXFont::BOLD_SERIES,
595                           LyXFont::UP_SHAPE, LColor::math},
596         {"lyxfakecal",    LyXFont::SANS_FAMILY, LyXFont::MEDIUM_SERIES,
597                           LyXFont::ITALIC_SHAPE, LColor::math},
598         {"lyxfakefrak",   LyXFont::ROMAN_FAMILY, LyXFont::BOLD_SERIES,
599                           LyXFont::ITALIC_SHAPE, LColor::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         std::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 LyXFont getFont(docstring const & name)
633 {
634         LyXFont 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(LyXFont & 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_ != LColor::none)
675                 font.setColor(info->color_);
676 }
677
678
679 docstring asString(MathArray 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, MathArray & 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