]> git.lyx.org Git - features.git/blob - src/mathed/support.C
write \mathrm{x}\mathrm{y} as \mathrm{xy} again
[features.git] / src / mathed / support.C
1 #include <config.h>
2
3 #include <map>
4
5 #include "mathed/support.h"
6 #include "lyxfont.h"
7 #include "FontLoader.h"
8 #include "font.h"
9 #include "math_defs.h"
10 #include "math_inset.h"
11 #include "math_parser.h"
12 #include "Painter.h"
13 #include "debug.h"
14
15 using std::map;
16 using std::endl;
17 using std::max;
18
19
20 bool isBinaryOp(char c, MathTextCodes type)
21 {
22         return type < LM_TC_SYMB && strchr("+-<>=/*", c);
23 }
24
25
26 ///
27 class Matrix {
28 public:
29         ///
30         Matrix(int, double, double);
31         ///
32         void transform(double &, double &);
33 private:
34         ///
35         double m_[2][2];
36 };
37
38
39 Matrix::Matrix(int code, double x, double y)
40 {
41         double const cs = (code & 1) ? 0 : (1 - code);
42         double const sn = (code & 1) ? (2 - code) : 0;
43         m_[0][0] =  cs * x;
44         m_[0][1] =  sn * x;
45         m_[1][0] = -sn * y;
46         m_[1][1] =  cs * y;
47 }
48
49
50 void Matrix::transform(double & x, double & y)
51 {
52         double xx = m_[0][0] * x + m_[0][1] * y;
53         double yy = m_[1][0] * x + m_[1][1] * y;
54         x = xx;
55         y = yy;
56 }
57
58
59 namespace {
60
61 LyXFont * MathFonts = 0;
62 bool font_available[LM_FONT_END];
63 bool font_available_initialized[LM_FONT_END];
64
65 enum MathFont {
66         FONT_IT,
67         FONT_SYMBOL,
68         FONT_SYMBOLI,
69         FONT_BF,
70         FONT_BB,
71         FONT_CAL,
72         FONT_TT,
73         FONT_RM,
74         FONT_SF,
75         FONT_CMR,
76         FONT_CMSY,
77         FONT_CMM,
78         FONT_CMEX,
79         FONT_MSA,
80         FONT_MSB,
81         FONT_NUM
82 };
83
84 void mathed_init_fonts()
85 {
86         MathFonts = new LyXFont[FONT_NUM]; //DEC cxx cannot initialize all fonts
87         //at once (JMarc) rc
88
89         for (int i = 0 ; i < FONT_NUM ; ++i) {
90                 MathFonts[i] = LyXFont(LyXFont::ALL_SANE);
91         }
92
93         MathFonts[FONT_IT].setShape(LyXFont::ITALIC_SHAPE);
94
95         MathFonts[FONT_SYMBOL].setFamily(LyXFont::SYMBOL_FAMILY);
96
97         MathFonts[FONT_SYMBOLI].setFamily(LyXFont::SYMBOL_FAMILY);
98         MathFonts[FONT_SYMBOLI].setShape(LyXFont::ITALIC_SHAPE);
99
100         MathFonts[FONT_BF].setSeries(LyXFont::BOLD_SERIES);
101
102         MathFonts[FONT_BB].setSeries(LyXFont::BOLD_SERIES);
103         MathFonts[FONT_BB].setFamily(LyXFont::TYPEWRITER_FAMILY);
104
105         MathFonts[FONT_CAL].setFamily(LyXFont::SANS_FAMILY);
106         MathFonts[FONT_CAL].setShape(LyXFont::ITALIC_SHAPE);
107
108         MathFonts[FONT_TT].setFamily(LyXFont::TYPEWRITER_FAMILY);
109
110         MathFonts[FONT_RM].setFamily(LyXFont::ROMAN_FAMILY);
111
112         MathFonts[FONT_SF].setFamily(LyXFont::SANS_FAMILY);
113
114         MathFonts[FONT_CMR].setFamily(LyXFont::CMR_FAMILY);
115         MathFonts[FONT_CMSY].setFamily(LyXFont::CMSY_FAMILY);
116         MathFonts[FONT_CMM].setFamily(LyXFont::CMM_FAMILY);
117         MathFonts[FONT_CMEX].setFamily(LyXFont::CMEX_FAMILY);
118         MathFonts[FONT_MSA].setFamily(LyXFont::MSA_FAMILY);
119         MathFonts[FONT_MSB].setFamily(LyXFont::MSB_FAMILY);
120
121         for (int i = 0; i < LM_FONT_END; ++i)
122                 font_available_initialized[i] = false;
123 }
124
125
126 LyXFont const & whichFontBase(MathTextCodes type)
127 {
128         if (!MathFonts)
129                 mathed_init_fonts();
130
131         switch (type) {
132         case LM_TC_SYMB:        
133         case LM_TC_BOLDSYMB:    
134                 return MathFonts[FONT_SYMBOLI];
135
136         case LM_TC_VAR:
137         case LM_TC_IT:
138                 return MathFonts[FONT_IT];
139
140         case LM_TC_BF:
141                 return MathFonts[FONT_BF];
142
143         case LM_TC_BB:
144                 if (math_font_available(LM_TC_MSB))
145                         return MathFonts[FONT_MSB];
146                 else
147                         return MathFonts[FONT_BB];
148
149         case LM_TC_CAL:
150                 if (math_font_available(LM_TC_CMSY))
151                         return MathFonts[FONT_CMSY];
152                 else
153                         return MathFonts[FONT_CAL];
154
155         case LM_TC_TT:
156                 return MathFonts[FONT_TT];
157
158         case LM_TC_TEXTRM:
159         case LM_TC_CONST:
160         case LM_TC_TEX:
161         case LM_TC_RM:
162                 return MathFonts[FONT_RM];
163
164         case LM_TC_SF:
165                 return MathFonts[FONT_SF];
166
167         case LM_TC_CMR:
168                 return MathFonts[FONT_CMR];
169
170         case LM_TC_CMSY:
171                 return MathFonts[FONT_CMSY];
172
173         case LM_TC_CMM:
174                 return MathFonts[FONT_CMM];
175
176         case LM_TC_CMEX:
177                 return MathFonts[FONT_CMEX];
178
179         case LM_TC_MSA:
180                 return MathFonts[FONT_MSA];
181
182         case LM_TC_MSB:
183                 return MathFonts[FONT_MSB];
184
185         default:
186                 break;
187         }
188         return MathFonts[1];
189 }
190
191
192 LyXFont whichFont(MathTextCodes type, MathMetricsInfo const & size)
193 {
194         LyXFont f = whichFontBase(type);
195         // use actual size
196         f.setSize(size.font.size());
197
198         switch (size.style) {
199         case LM_ST_DISPLAY:
200                 if (type == LM_TC_BOLDSYMB || type == LM_TC_CMEX) {
201                         f.incSize();
202                         f.incSize();
203                 }
204                 break;
205
206         case LM_ST_TEXT:
207                 break;
208
209         case LM_ST_SCRIPT:
210                 f.decSize();
211                 f.decSize();
212                 break;
213
214         case LM_ST_SCRIPTSCRIPT:
215                 f.decSize();
216                 f.decSize();
217                 f.decSize();
218                 break;
219
220         default:
221                 lyxerr << "Math Error: wrong font size: " << size.style << endl;
222                 break;
223         }
224
225         if (type != LM_TC_TEXTRM)
226                 f.setColor(LColor::math);
227
228         if (type == LM_TC_TEX)
229                 f.setColor(LColor::latex);
230
231         return f;
232 }
233
234 } // namespace
235
236
237 bool math_font_available(MathTextCodes type)
238 {
239         if (!font_available_initialized[type]) {
240                 font_available_initialized[type] = true;
241                 font_available[type] = fontloader.available(whichFontBase(type));
242         }
243         return font_available[type];
244 }
245
246
247 namespace {
248
249 /*
250  * Internal struct of a drawing: code n x1 y1 ... xn yn, where code is:
251  * 0 = end, 1 = line, 2 = polyline, 3 = square line, 4= square polyline
252  */
253
254
255 double const parenthHigh[] = {
256         2, 13,
257         0.9840, 0.0014, 0.7143, 0.0323, 0.4603, 0.0772,
258         0.2540, 0.1278, 0.1746, 0.1966, 0.0952, 0.3300,
259         0.0950, 0.5000, 0.0952, 0.6700, 0.1746, 0.8034,
260         0.2540, 0.8722, 0.4603, 0.9228, 0.7143, 0.9677,
261         0.9840, 0.9986,
262         0
263 };
264
265
266 double const parenth[] = {
267         2, 13,
268         0.9930, 0.0071, 0.7324, 0.0578, 0.5141, 0.1126,
269         0.3380, 0.1714, 0.2183, 0.2333, 0.0634, 0.3621,
270         0.0141, 0.5000, 0.0563, 0.6369, 0.2113, 0.7647,
271         0.3310, 0.8276, 0.5070, 0.8864, 0.7254, 0.9412,
272         0.9930, 0.9919,
273         0
274 };
275
276
277 double const brace[] = {
278         2, 21,
279         0.9492, 0.0020, 0.9379, 0.0020, 0.7458, 0.0243,
280         0.5819, 0.0527, 0.4859, 0.0892, 0.4463, 0.1278,
281         0.4463, 0.3732, 0.4011, 0.4199, 0.2712, 0.4615,
282         0.0734, 0.4919, 0.0113, 0.5000, 0.0734, 0.5081,
283         0.2712, 0.5385, 0.4011, 0.5801, 0.4463, 0.6268,
284         0.4463, 0.8722, 0.4859, 0.9108, 0.5819, 0.9473,
285         0.7458, 0.9757, 0.9379, 0.9980, 0.9492, 0.9980,
286         0
287 };
288
289
290 double const arrow[] = {
291         4, 7,
292         0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
293         0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
294         0.9500, 0.7500,
295         3, 0.5000, 0.1500, 0.5000, 0.9500,
296         0
297 };
298
299
300 double const Arrow[] = {
301         4, 7,
302         0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
303         0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
304         0.9500, 0.7500,
305         3, 0.3500, 0.5000, 0.3500, 0.9500,
306         3, 0.6500, 0.5000, 0.6500, 0.9500,
307         0
308 };
309
310
311 double const udarrow[] = {
312         2, 3,
313         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
314         2, 3,
315         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,
316         1, 0.5, 0.2,  0.5, 0.8,
317         0
318 };
319
320
321 double const Udarrow[] = {
322         2, 3,
323         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
324         2, 3,
325         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,
326         1, 0.35, 0.2, 0.35, 0.8,
327         1, 0.65, 0.2, 0.65, 0.8,
328         0
329 };
330
331
332 double const brack[] = {
333         2, 4,
334         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,  0.95, 0.95,
335         0
336 };
337
338
339 double const corner[] = {
340         2, 3,
341         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,
342         0
343 };
344
345
346 double const angle[] = {
347         2, 3,
348         1, 0,  0.05, 0.5,  1, 1,
349         0
350 };
351
352
353 double const slash[] = {
354         1, 0.95, 0.05, 0.05, 0.95,
355         0
356 };
357
358
359 double const hline[] = {
360         1, 0.00, 0.5, 1.0, 0.5,
361         0
362 };
363
364
365 double const hline2[] = {
366         1, 0.1, 0.5,  0.3, 0.5,
367         1, 0.7, 0.5,  0.9, 0.5,
368         0
369 };
370
371
372 double const hline3[] = {
373         1, 0.1,   0,  0.15,  0,
374         1, 0.475, 0,  0.525, 0,
375         1, 0.85,  0,  0.9,   0,
376         0
377 };
378
379
380 double const dline3[] = {
381         1, 0.1,   0.1,   0.15,  0.15,
382         1, 0.475, 0.475, 0.525, 0.525,
383         1, 0.85,  0.85,  0.9,   0.9,
384         0
385 };
386
387
388 double const hlinesmall[] = {
389         1, 0.4, 0.5, 0.6, 0.5,
390         0
391 };
392
393
394 double const vert[] = {
395         1, 0.5, 0.05,  0.5, 0.95,
396         0
397 };
398
399
400 double const  Vert[] = {
401         1, 0.3, 0.05,  0.3, 0.95,
402         1, 0.7, 0.05,  0.7, 0.95,
403         0
404 };
405
406
407 double const tilde[] = {
408         2, 4,
409         0.05, 0.8,  0.25, 0.2,  0.75, 0.8,  0.95, 0.2,
410         0
411 };
412
413
414 struct deco_struct {
415         double const * data;
416         int angle;
417 };
418
419 struct named_deco_struct {
420         char const * name;
421         double const * data;
422         int angle;
423 };
424
425 named_deco_struct deco_table[] = {
426         // Decorations
427         {"widehat",        angle,      3 },
428         {"widetilde",      tilde,      0 },
429         {"underline",      hline,      0 },
430         {"overline",       hline,      0 },
431         {"underbrace",     brace,      1 },
432         {"overbrace",      brace,      3 },
433         {"overleftarrow",  arrow,      1 },
434         {"overrightarrow", arrow,      3 },
435         
436         // Delimiters
437         {"(",              parenth,    0 },
438         {")",              parenth,    2 },
439         {"{",              brace,      0 },
440         {"}",              brace,      2 },
441         {"[",              brack,      0 },
442         {"]",              brack,      2 },
443         {"|",              vert,       0 },
444         {"/",              slash,      0 },
445         {"Vert",           Vert,       0 },
446         {"'",              slash,      1 },
447         {"backslash",      slash,      1 },
448         {"langle",         angle,      0 },
449         {"lceil",          corner,     0 },
450         {"lfloor",         corner,     1 },
451         {"rangle",         angle,      2 },
452         {"rceil",          corner,     3 },
453         {"rfloor",         corner,     2 },
454         {"downarrow",      arrow,      2 },
455         {"Downarrow",      Arrow,      2 },
456         {"uparrow",        arrow,      0 },
457         {"Uparrow",        Arrow,      0 },
458         {"updownarrow",    udarrow,    0 },
459         {"Updownarrow",    Udarrow,    0 },     
460         
461         // Accents
462         {"ddot",           hline2,     0 },
463         {"hat",            angle,      3 },
464         {"grave",          slash,      1 },
465         {"acute",          slash,      0 },
466         {"tilde",          tilde,      0 },
467         {"bar",            hline,      0 },
468         {"dot",            hlinesmall, 0 },
469         {"check",          angle,      1 },
470         {"breve",          parenth,    1 },
471         {"vec",            arrow,      3 },
472         {"not",            slash,      0 },
473         
474         // Dots
475         {"ldots",          hline3,     0 },
476         {"cdots",          hline3,     0 },
477         {"vdots",          hline3,     1 },
478         {"ddots",          dline3,     0 }
479 };
480
481
482 map<string, deco_struct> deco_list;
483
484 // sort the table on startup
485 struct init_deco_table {
486         init_deco_table() {
487                 unsigned const n = sizeof(deco_table) / sizeof(deco_table[0]);
488                 for (named_deco_struct * p = deco_table; p != deco_table + n; ++p) {
489                         deco_struct d;
490                         d.data  = p->data;
491                         d.angle = p->angle;
492                         deco_list[p->name]= d;
493                 }
494         }
495 };
496
497 static init_deco_table dummy;
498
499
500 deco_struct const * search_deco(string const & name)
501 {
502         map<string, deco_struct>::const_iterator p = deco_list.find(name);
503         return (p == deco_list.end()) ? 0 : &(p->second);
504 }
505
506
507 } // namespace anon
508
509
510 void mathed_char_dim(MathTextCodes type, MathMetricsInfo const & size,
511         unsigned char c, int & asc, int & des, int & wid)
512 {
513         LyXFont const font = whichFont(type, size);
514         des = lyxfont::descent(c, font);
515         asc = lyxfont::ascent(c, font);
516         wid = mathed_char_width(type, size, c);
517 }
518
519
520 int mathed_char_height(MathTextCodes type, MathMetricsInfo const & size,
521         unsigned char c, int & asc, int & des)
522 {
523         LyXFont const font = whichFont(type, size);
524         des = lyxfont::descent(c, font);
525         asc = lyxfont::ascent(c, font);
526         return asc + des;
527 }
528
529
530 int mathed_char_height(MathTextCodes type, MathMetricsInfo const & size,
531         unsigned char c)
532 {
533         int asc;
534         int des;
535         return mathed_char_height(type, size, c, asc, des);
536 }
537
538
539 int mathed_char_ascent(MathTextCodes type, MathMetricsInfo const & size,
540         unsigned char c)
541 {
542         LyXFont const font = whichFont(type, size);
543         return lyxfont::ascent(c, font);
544 }
545
546
547 int mathed_char_descent(MathTextCodes type, MathMetricsInfo const & size,
548         unsigned char c)
549 {
550         LyXFont const font = whichFont(type, size);
551         return lyxfont::descent(c, font);
552 }
553
554
555 int mathed_char_width(MathTextCodes type, MathMetricsInfo const & size,
556         unsigned char c)
557 {
558         LyXFont const font = whichFont(type, size);
559         if (isBinaryOp(c, type))
560                 return lyxfont::width(c, font) + 2 * lyxfont::width(' ', font);
561         else
562                 return lyxfont::width(c, font);
563 }
564
565
566 void mathed_string_dim(MathTextCodes type, MathMetricsInfo const & size,
567         string const & s, int & asc, int & des, int & wid)
568 {
569         mathed_string_height(type, size, s, asc, des);
570         wid = mathed_string_width(type, size, s);
571 }
572
573
574 int mathed_string_height(MathTextCodes type, MathMetricsInfo const & size,
575         string const & s, int & asc, int & des)
576 {
577         LyXFont const font = whichFont(type, size);
578         asc = des = 0;
579         for (string::const_iterator it = s.begin(); it != s.end(); ++it) {
580                 des = max(des, lyxfont::descent(*it, font));
581                 asc = max(asc, lyxfont::ascent(*it, font));
582         }
583         return asc + des;
584 }
585
586
587 int mathed_string_width(MathTextCodes type, MathMetricsInfo const & size,
588         string const & s)
589 {
590         return lyxfont::width(s, whichFont(type, size));
591 }
592
593
594 int mathed_string_ascent(MathTextCodes type, MathMetricsInfo const & size,
595         string const & s)
596 {
597         LyXFont const font = whichFont(type, size);
598         int asc = 0;
599         for (string::const_iterator it = s.begin(); it != s.end(); ++it)
600                 asc = max(asc, lyxfont::ascent(*it, font));
601         return asc;
602 }
603
604
605 int mathed_string_descent(MathTextCodes type, MathMetricsInfo const & size,
606         string const & s)
607 {
608         LyXFont const font = whichFont(type, size);
609         int des = 0;
610         for (string::const_iterator it = s.begin(); it != s.end(); ++it)
611                 des = max(des, lyxfont::descent(*it, font));
612         return des;
613 }
614
615
616
617 void mathed_draw_deco(Painter & pain, int x, int y, int w, int h,
618         const string & name)
619 {
620         if (name == ".") {
621                 pain.line(x + w/2, y, x + w/2, y + h,
622                           LColor::mathcursor, Painter::line_onoffdash);
623                 return;
624         }       
625         
626         deco_struct const * mds = search_deco(name);
627         if (!mds) {
628                 lyxerr << "Deco was not found. Programming error?\n";
629                 lyxerr << "name: '" << name << "'\n";
630                 return;
631         }
632         
633         int const n = (w < h) ? w : h;
634         int const r = mds->angle;
635         double const * d = mds->data;
636         
637         if (h > 70 && (name == "(" || name == ")"))
638                 d = parenthHigh;
639         
640         Matrix mt(r, w, h);
641         Matrix sqmt(r, n, n);
642
643         if (r > 0 && r < 3)
644                 y += h;
645
646         if (r >= 2)
647                 x += w;
648
649         for (int i = 0; d[i]; ) {
650                 int code = int(d[i++]);
651                 if (code & 1) {  // code == 1 || code == 3
652                         double xx = d[i++];
653                         double yy = d[i++];
654                         double x2 = d[i++];
655                         double y2 = d[i++];
656                         if (code == 3)
657                                 sqmt.transform(xx, yy);
658                         else
659                                 mt.transform(xx, yy);
660                         mt.transform(x2, y2);
661                         pain.line(x + int(xx), y + int(yy), x + int(x2), y + int(y2),
662                                         LColor::mathline);
663                 }       else {
664                         int xp[32];
665                         int yp[32];
666                         int const n = int(d[i++]);
667                         for (int j = 0; j < n; ++j) {
668                                 double xx = d[i++];
669                                 double yy = d[i++];
670 //           lyxerr << " " << xx << " " << yy << " ";
671                                 if (code == 4)
672                                         sqmt.transform(xx, yy);
673                                 else
674                                         mt.transform(xx, yy);
675                                 xp[j] = x + int(xx);
676                                 yp[j] = y + int(yy);
677                                 //  lyxerr << "P[" << j " " << xx << " " << yy << " " << x << " " << y << "]";
678                         }
679                         pain.lines(xp, yp, n, LColor::mathline);
680                 }
681         }
682 }
683
684
685 // In the future maybe we use a better fonts renderer
686 void drawStr(Painter & pain, MathTextCodes type, MathMetricsInfo const & siz,
687         int x, int y, string const & s)
688 {
689         pain.text(x, y, s, whichFont(type, siz));
690 }
691
692
693 void drawChar(Painter & pain, MathTextCodes type, MathMetricsInfo const & siz,
694         int x, int y, char c)
695 {
696         string s;
697         if (isBinaryOp(c, type))
698                 s += ' ';
699         s += c;
700         if (isBinaryOp(c, type))
701                 s += ' ';
702         drawStr(pain, type, siz, x, y, s);
703 }
704
705
706 // decrease math size for super- and subscripts
707 void smallerStyleScript(MathMetricsInfo & st)
708 {
709         switch (st.style) {
710                 case LM_ST_DISPLAY:
711                 case LM_ST_TEXT:    st.style = LM_ST_SCRIPT; break;
712                 default:            st.style = LM_ST_SCRIPTSCRIPT;
713         }
714 }
715
716
717 // decrease math size for fractions
718 void smallerStyleFrac(MathMetricsInfo & st)
719 {
720         switch (st.style) {
721                 case LM_ST_DISPLAY: st.style = LM_ST_TEXT; break;
722                 case LM_ST_TEXT:    st.style = LM_ST_SCRIPT; break;
723                 default:            st.style = LM_ST_SCRIPTSCRIPT;
724         }
725 }
726
727
728 void math_font_max_dim(MathTextCodes code, MathMetricsInfo const & siz,
729         int & asc, int & des)
730 {
731         LyXFont font = whichFont(code, siz);
732         asc = lyxfont::maxAscent(font);
733         des = lyxfont::maxDescent(font);
734 }
735
736
737 char const * latex_mathspace[] = {
738         "!", ",", ":", ";", "quad", "qquad"
739 };
740
741
742 char const * math_font_name(MathTextCodes code)
743 {
744         static char const * theFontNames[] = {
745                 "mathrm",
746                 "mathcal",
747                 "mathbf",
748                 "mathbb",
749                 "mathsf",
750                 "mathtt",
751                 "mathit",
752                 "textrm"
753         };
754
755         if (code >= LM_TC_RM && code <= LM_TC_TEXTRM) 
756                 return theFontNames[code - LM_TC_RM];
757         return 0;
758 }