2 * \file MathSupport.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Alejandro Aguilar Sierra
9 * Full author contact details are available in file CREDITS.
14 #include "MathSupport.h"
16 #include "InsetMath.h"
18 #include "MathParser.h"
19 #include "MathStream.h"
21 #include "frontends/FontLoader.h"
22 #include "frontends/FontMetrics.h"
23 #include "frontends/Painter.h"
25 #include "support/debug.h"
26 #include "support/docstream.h"
34 using frontend::Painter;
41 Matrix(int, double, double);
43 void transform(double &, double &);
50 Matrix::Matrix(int code, double x, double y)
52 double const cs = (code & 1) ? 0 : (1 - code);
53 double const sn = (code & 1) ? (2 - code) : 0;
61 void Matrix::transform(double & x, double & y)
63 double xx = m_[0][0] * x + m_[0][1] * y;
64 double yy = m_[1][0] * x + m_[1][1] * y;
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
79 double const parenthHigh[] = {
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,
90 double const parenth[] = {
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,
101 double const brace[] = {
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,
114 double const arrow[] = {
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,
119 3, 0.5000, 0.1500, 0.5000, 0.9500,
124 double const Arrow[] = {
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,
129 3, 0.3500, 0.5000, 0.3500, 0.9500,
130 3, 0.6500, 0.5000, 0.6500, 0.9500,
135 double const udarrow[] = {
137 0.015, 0.25, 0.5, 0.05, 0.95, 0.25,
139 0.015, 0.75, 0.5, 0.95, 0.95, 0.75,
140 1, 0.5, 0.2, 0.5, 0.8,
145 double const Udarrow[] = {
147 0.015, 0.25, 0.5, 0.05, 0.95, 0.25,
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,
156 double const brack[] = {
158 0.95, 0.05, 0.05, 0.05, 0.05, 0.95, 0.95, 0.95,
163 double const dbrack[] = {
165 0.95, 0.05, 0.05, 0.05, 0.05, 0.95, 0.95, 0.95,
167 0.50, 0.05, 0.50, 0.95,
172 double const corner[] = {
174 0.95, 0.05, 0.05, 0.05, 0.05, 0.95,
179 double const angle[] = {
181 1, 0, 0.05, 0.5, 1, 1,
186 double const slash[] = {
187 1, 0.95, 0.05, 0.05, 0.95,
192 double const hline[] = {
193 1, 0.00, 0.5, 1.0, 0.5,
198 double const ddot[] = {
199 1, 0.2, 0.5, 0.3, 0.5,
200 1, 0.7, 0.5, 0.8, 0.5,
205 double const dddot[] = {
206 1, 0.1, 0.5, 0.2, 0.5,
207 1, 0.45, 0.5, 0.55, 0.5,
208 1, 0.8, 0.5, 0.9, 0.5,
213 double const ddddot[] = {
214 1, 0.1, 0.5, 0.2, 0.5,
215 1, 0.45, 0.5, 0.55, 0.5,
216 1, 0.8, 0.5, 0.9, 0.5,
217 1, 1.15, 0.5, 1.25, 0.5,
222 double const hline3[] = {
224 1, 0.475, 0, 0.525, 0,
230 double const dline3[] = {
231 1, 0.1, 0.1, 0.15, 0.15,
232 1, 0.475, 0.475, 0.525, 0.525,
233 1, 0.85, 0.85, 0.9, 0.9,
238 double const hlinesmall[] = {
239 1, 0.4, 0.5, 0.6, 0.5,
244 double const ring[] = {
246 0.5, 0.8, 0.8, 0.5, 0.5, 0.2, 0.2, 0.5, 0.5, 0.8,
251 double const vert[] = {
252 1, 0.5, 0.05, 0.5, 0.95,
257 double const Vert[] = {
258 1, 0.3, 0.05, 0.3, 0.95,
259 1, 0.7, 0.05, 0.7, 0.95,
264 double const tilde[] = {
266 0.00, 0.8, 0.25, 0.2, 0.75, 0.8, 1.00, 0.2,
276 struct named_deco_struct {
282 named_deco_struct deco_table[] = {
284 {"widehat", angle, 3 },
285 {"widetilde", tilde, 0 },
286 {"underbar", hline, 0 },
287 {"underline", hline, 0 },
288 {"overline", hline, 0 },
289 {"underbrace", brace, 1 },
290 {"overbrace", brace, 3 },
291 {"overleftarrow", arrow, 1 },
292 {"overrightarrow", arrow, 3 },
293 {"overleftrightarrow", udarrow, 1 },
294 {"xleftarrow", arrow, 1 },
295 {"xrightarrow", arrow, 3 },
296 {"underleftarrow", arrow, 1 },
297 {"underrightarrow", arrow, 3 },
298 {"underleftrightarrow", udarrow, 1 },
299 {"undertilde", tilde, 0 },
300 {"utilde", tilde, 0 },
307 {"lbrace", brace, 0 },
308 {"rbrace", brace, 2 },
311 {"llbracket", dbrack, 0 },
312 {"rrbracket", dbrack, 2 },
315 {"slash", slash, 0 },
322 {"backslash", slash, 1 },
323 {"langle", angle, 0 },
324 {"lceil", corner, 0 },
325 {"lfloor", corner, 1 },
326 {"rangle", angle, 2 },
327 {"rceil", corner, 3 },
328 {"rfloor", corner, 2 },
329 {"downarrow", arrow, 2 },
330 {"Downarrow", Arrow, 2 },
331 {"uparrow", arrow, 0 },
332 {"Uparrow", Arrow, 0 },
333 {"updownarrow", udarrow, 0 },
334 {"Updownarrow", Udarrow, 0 },
338 {"dddot", dddot, 0 },
339 {"ddddot", ddddot, 0 },
341 {"grave", slash, 1 },
342 {"acute", slash, 0 },
343 {"tilde", tilde, 0 },
345 {"dot", hlinesmall, 0 },
346 {"check", angle, 1 },
347 {"breve", parenth, 1 },
349 {"mathring", ring, 0 },
352 {"dots", hline3, 0 },
353 {"ldots", hline3, 0 },
354 {"cdots", hline3, 0 },
355 {"vdots", hline3, 1 },
356 {"ddots", dline3, 0 },
357 {"adots", dline3, 1 },
358 {"iddots", dline3, 1 },
359 {"dotsb", hline3, 0 },
360 {"dotsc", hline3, 0 },
361 {"dotsi", hline3, 0 },
362 {"dotsm", hline3, 0 },
363 {"dotso", hline3, 0 }
367 map<docstring, deco_struct> deco_list;
369 // sort the table on startup
370 class init_deco_table {
373 unsigned const n = sizeof(deco_table) / sizeof(deco_table[0]);
374 for (named_deco_struct * p = deco_table; p != deco_table + n; ++p) {
378 deco_list[from_ascii(p->name)] = d;
383 static init_deco_table dummy;
386 deco_struct const * search_deco(docstring const & name)
388 map<docstring, deco_struct>::const_iterator p = deco_list.find(name);
389 return p == deco_list.end() ? 0 : &(p->second);
396 int mathed_char_width(FontInfo const & font, char_type c)
398 return theFontMetrics(font).width(c);
402 int mathed_char_kerning(FontInfo const & font, char_type c)
404 frontend::FontMetrics const & fm = theFontMetrics(font);
405 return fm.rbearing(c) - fm.width(c);
409 void mathed_string_dim(FontInfo const & font,
413 frontend::FontMetrics const & fm = theFontMetrics(font);
416 for (docstring::const_iterator it = s.begin();
419 dim.asc = max(dim.asc, fm.ascent(*it));
420 dim.des = max(dim.des, fm.descent(*it));
422 dim.wid = fm.width(s);
426 int mathed_string_width(FontInfo const & font, docstring const & s)
428 return theFontMetrics(font).width(s);
432 void mathed_draw_deco(PainterInfo & pi, int x, int y, int w, int h,
433 docstring const & name)
436 pi.pain.line(x + w/2, y, x + w/2, y + h,
437 Color_cursor, Painter::line_onoffdash);
441 deco_struct const * mds = search_deco(name);
443 lyxerr << "Deco was not found. Programming error?" << endl;
444 lyxerr << "name: '" << to_utf8(name) << "'" << endl;
448 int const n = (w < h) ? w : h;
449 int const r = mds->angle;
450 double const * d = mds->data;
452 if (h > 70 && (name == "(" || name == ")"))
456 Matrix sqmt(r, n, n);
464 for (int i = 0; d[i]; ) {
465 int code = int(d[i++]);
466 if (code & 1) { // code == 1 || code == 3
472 sqmt.transform(xx, yy);
474 mt.transform(xx, yy);
475 mt.transform(x2, y2);
477 int(x + xx + 0.5), int(y + yy + 0.5),
478 int(x + x2 + 0.5), int(y + y2 + 0.5),
479 pi.base.font.color());
483 int const n = int(d[i++]);
484 for (int j = 0; j < n; ++j) {
487 // lyxerr << ' ' << xx << ' ' << yy << ' ';
489 sqmt.transform(xx, yy);
491 mt.transform(xx, yy);
492 xp[j] = int(x + xx + 0.5);
493 yp[j] = int(y + yy + 0.5);
494 // lyxerr << "P[" << j ' ' << xx << ' ' << yy << ' ' << x << ' ' << y << ']';
496 pi.pain.lines(xp, yp, n, pi.base.font.color());
502 void metricsStrRedBlack(MetricsInfo & mi, Dimension & dim, docstring const & str)
504 FontInfo font = mi.base.font;
505 augmentFont(font, from_ascii("mathnormal"));
506 mathed_string_dim(font, str, dim);
510 void drawStrRed(PainterInfo & pi, int x, int y, docstring const & str)
512 FontInfo f = pi.base.font;
513 augmentFont(f, from_ascii("mathnormal"));
514 f.setColor(Color_latex);
515 pi.pain.text(x, y, str, f);
519 void drawStrBlack(PainterInfo & pi, int x, int y, docstring const & str)
521 FontInfo f = pi.base.font;
522 augmentFont(f, from_ascii("mathnormal"));
523 f.setColor(Color_foreground);
524 pi.pain.text(x, y, str, f);
528 void math_font_max_dim(FontInfo const & font, int & asc, int & des)
530 frontend::FontMetrics const & fm = theFontMetrics(font);
531 asc = fm.maxAscent();
532 des = fm.maxDescent();
545 FontFamily const inh_family = INHERIT_FAMILY;
546 FontSeries const inh_series = INHERIT_SERIES;
547 FontShape const inh_shape = INHERIT_SHAPE;
550 // mathnormal should be the first, otherwise the fallback further down
552 fontinfo fontinfos[] = {
554 {"mathnormal", ROMAN_FAMILY, MEDIUM_SERIES,
555 ITALIC_SHAPE, Color_math},
556 {"mathbf", inh_family, BOLD_SERIES,
557 inh_shape, Color_math},
558 {"mathcal", CMSY_FAMILY, inh_series,
559 inh_shape, Color_math},
560 {"mathfrak", EUFRAK_FAMILY, inh_series,
561 inh_shape, Color_math},
562 {"mathrm", ROMAN_FAMILY, inh_series,
563 UP_SHAPE, Color_math},
564 {"mathsf", SANS_FAMILY, inh_series,
565 inh_shape, Color_math},
566 {"mathbb", MSB_FAMILY, inh_series,
567 inh_shape, Color_math},
568 {"mathtt", TYPEWRITER_FAMILY, inh_series,
569 inh_shape, Color_math},
570 {"mathit", inh_family, inh_series,
571 ITALIC_SHAPE, Color_math},
572 {"mathscr", RSFS_FAMILY, inh_series,
573 inh_shape, Color_math},
574 {"cmex", CMEX_FAMILY, inh_series,
575 inh_shape, Color_math},
576 {"cmm", CMM_FAMILY, inh_series,
577 inh_shape, Color_math},
578 {"cmr", CMR_FAMILY, inh_series,
579 inh_shape, Color_math},
580 {"cmsy", CMSY_FAMILY, inh_series,
581 inh_shape, Color_math},
582 {"eufrak", EUFRAK_FAMILY, inh_series,
583 inh_shape, Color_math},
584 {"msa", MSA_FAMILY, inh_series,
585 inh_shape, Color_math},
586 {"msb", MSB_FAMILY, inh_series,
587 inh_shape, Color_math},
588 {"stmry", STMARY_FAMILY, inh_series,
589 inh_shape, Color_math},
590 {"wasy", WASY_FAMILY, inh_series,
591 inh_shape, Color_math},
592 {"esint", ESINT_FAMILY, inh_series,
593 inh_shape, Color_math},
596 {"text", inh_family, inh_series,
597 inh_shape, Color_foreground},
598 {"textbf", inh_family, BOLD_SERIES,
599 inh_shape, Color_foreground},
600 {"textit", inh_family, inh_series,
601 ITALIC_SHAPE, Color_foreground},
602 {"textmd", inh_family, MEDIUM_SERIES,
603 inh_shape, Color_foreground},
604 {"textnormal", inh_family, inh_series,
605 UP_SHAPE, Color_foreground},
606 {"textrm", ROMAN_FAMILY,
607 inh_series, UP_SHAPE,Color_foreground},
608 {"textsc", inh_family, inh_series,
609 SMALLCAPS_SHAPE, Color_foreground},
610 {"textsf", SANS_FAMILY, inh_series,
611 inh_shape, Color_foreground},
612 {"textsl", inh_family, inh_series,
613 SLANTED_SHAPE, Color_foreground},
614 {"texttt", TYPEWRITER_FAMILY, inh_series,
615 inh_shape, Color_foreground},
616 {"textup", inh_family, inh_series,
617 UP_SHAPE, Color_foreground},
620 {"textipa", inh_family, inh_series,
621 inh_shape, Color_foreground},
624 {"ce", inh_family, inh_series,
625 inh_shape, Color_foreground},
626 {"cf", inh_family, inh_series,
627 inh_shape, Color_foreground},
629 // LyX internal usage
630 {"lyxtex", inh_family, inh_series,
631 UP_SHAPE, Color_latex},
632 {"lyxsymbol", SYMBOL_FAMILY, inh_series,
633 inh_shape, Color_math},
634 {"lyxboldsymbol", SYMBOL_FAMILY, BOLD_SERIES,
635 inh_shape, Color_math},
636 {"lyxblacktext", ROMAN_FAMILY, MEDIUM_SERIES,
637 UP_SHAPE, Color_foreground},
638 {"lyxnochange", inh_family, inh_series,
639 inh_shape, Color_foreground},
640 {"lyxfakebb", TYPEWRITER_FAMILY, BOLD_SERIES,
641 UP_SHAPE, Color_math},
642 {"lyxfakecal", SANS_FAMILY, MEDIUM_SERIES,
643 ITALIC_SHAPE, Color_math},
644 {"lyxfakefrak", ROMAN_FAMILY, BOLD_SERIES,
645 ITALIC_SHAPE, Color_math}
649 fontinfo * lookupFont(docstring const & name0)
651 //lyxerr << "searching font '" << name << "'" << endl;
652 int const n = sizeof(fontinfos) / sizeof(fontinfo);
653 string name = to_utf8(name0);
654 for (int i = 0; i < n; ++i)
655 if (fontinfos[i].cmd_ == name) {
656 //lyxerr << "found '" << i << "'" << endl;
657 return fontinfos + i;
663 fontinfo * searchFont(docstring const & name)
665 fontinfo * f = lookupFont(name);
666 return f ? f : fontinfos;
667 // this should be mathnormal
668 //return searchFont("mathnormal");
672 bool isFontName(docstring const & name)
674 return lookupFont(name);
678 bool isMathFont(docstring const & name)
680 fontinfo * f = lookupFont(name);
681 return f && f->color_ == Color_math;
685 bool isTextFont(docstring const & name)
687 fontinfo * f = lookupFont(name);
688 return f && f->color_ == Color_foreground;
692 FontInfo getFont(docstring const & name)
695 augmentFont(font, name);
700 void fakeFont(docstring const & orig, docstring const & fake)
702 fontinfo * forig = searchFont(orig);
703 fontinfo * ffake = searchFont(fake);
704 if (forig && ffake) {
705 forig->family_ = ffake->family_;
706 forig->series_ = ffake->series_;
707 forig->shape_ = ffake->shape_;
708 forig->color_ = ffake->color_;
710 lyxerr << "Can't fake font '" << to_utf8(orig) << "' with '"
711 << to_utf8(fake) << "'" << endl;
716 void augmentFont(FontInfo & font, docstring const & name)
718 static bool initialized = false;
721 // fake fonts if necessary
722 if (!theFontLoader().available(getFont(from_ascii("mathfrak"))))
723 fakeFont(from_ascii("mathfrak"), from_ascii("lyxfakefrak"));
724 if (!theFontLoader().available(getFont(from_ascii("mathcal"))))
725 fakeFont(from_ascii("mathcal"), from_ascii("lyxfakecal"));
727 fontinfo * info = searchFont(name);
728 if (info->family_ != inh_family)
729 font.setFamily(info->family_);
730 if (info->series_ != inh_series)
731 font.setSeries(info->series_);
732 if (info->shape_ != inh_shape)
733 font.setShape(info->shape_);
734 if (info->color_ != Color_none)
735 font.setColor(info->color_);
739 docstring asString(MathData const & ar)
748 void asArray(docstring const & str, MathData & ar, Parse::flags pf)
750 bool quiet = pf & Parse::QUIET;
751 if ((str.size() == 1 && quiet) || (!mathed_parse_cell(ar, str, pf) && quiet))
752 mathed_parse_cell(ar, str, pf | Parse::VERBATIM);
756 docstring asString(InsetMath const & inset)
765 docstring asString(MathAtom const & at)