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 "InsetMathFont.h"
17 #include "InsetMathSymbol.h"
19 #include "MathParser.h"
20 #include "MathStream.h"
22 #include "frontends/FontLoader.h"
23 #include "frontends/FontMetrics.h"
24 #include "frontends/Painter.h"
26 #include "support/debug.h"
27 #include "support/docstream.h"
36 using frontend::Painter;
43 Matrix(int, double, double);
45 void transform(double &, double &);
52 Matrix::Matrix(int code, double x, double y)
54 double const cs = (code & 1) ? 0 : (1 - code);
55 double const sn = (code & 1) ? (2 - code) : 0;
63 void Matrix::transform(double & x, double & y)
65 double xx = m_[0][0] * x + m_[0][1] * y;
66 double yy = m_[1][0] * x + m_[1][1] * y;
76 * Internal struct of a drawing: code n x1 y1 ... xn yn, where code is:
77 * 0 = end, 1 = line, 2 = polyline, 3 = square line, 4 = square polyline
81 double const parenthHigh[] = {
83 0.9840, 0.0014, 0.7143, 0.0323, 0.4603, 0.0772,
84 0.2540, 0.1278, 0.1746, 0.1966, 0.0952, 0.3300,
85 0.0950, 0.5000, 0.0952, 0.6700, 0.1746, 0.8034,
86 0.2540, 0.8722, 0.4603, 0.9228, 0.7143, 0.9677,
92 double const parenth[] = {
94 0.9930, 0.0071, 0.7324, 0.0578, 0.5141, 0.1126,
95 0.3380, 0.1714, 0.2183, 0.2333, 0.0634, 0.3621,
96 0.0141, 0.5000, 0.0563, 0.6369, 0.2113, 0.7647,
97 0.3310, 0.8276, 0.5070, 0.8864, 0.7254, 0.9412,
103 double const brace[] = {
105 0.9492, 0.0020, 0.9379, 0.0020, 0.7458, 0.0243,
106 0.5819, 0.0527, 0.4859, 0.0892, 0.4463, 0.1278,
107 0.4463, 0.3732, 0.4011, 0.4199, 0.2712, 0.4615,
108 0.0734, 0.4919, 0.0113, 0.5000, 0.0734, 0.5081,
109 0.2712, 0.5385, 0.4011, 0.5801, 0.4463, 0.6268,
110 0.4463, 0.8722, 0.4859, 0.9108, 0.5819, 0.9473,
111 0.7458, 0.9757, 0.9379, 0.9980, 0.9492, 0.9980,
116 double const mapsto[] = {
118 0.75, 0.015, 0.95, 0.5, 0.75, 0.985,
119 1, 0.015, 0.475, 0.945, 0.475,
120 1, 0.015, 0.015, 0.015, 0.985,
125 double const lhook[] = {
127 0.25, 0.015, 0.05, 0.5, 0.25, 0.985,
128 1, 0.015, 0.475, 0.7, 0.475,
130 0.7, 0.015, 0.825, 0.15, 0.985, 0.25,
131 0.825, 0.35, 0.7, 0.475,
136 double const rhook[] = {
138 0.75, 0.015, 0.95, 0.5, 0.75, 0.985,
139 1, 0.3, 0.475, 0.985, 0.475,
141 0.3, 0.015, 0.175, 0.15, 0.05, 0.25,
142 0.175, 0.35, 0.3, 0.475,
147 double const LRArrow[] = {
149 0.25, 0.015, 0.05, 0.5, 0.25, 0.985,
151 0.75, 0.015, 0.95, 0.5, 0.75, 0.985,
152 1, 0.2, 0.8, 0.8, 0.8,
153 1, 0.2, 0.2, 0.8, 0.2,
158 double const LArrow[] = {
160 0.25, 0.015, 0.05, 0.5, 0.25, 0.985,
161 1, 0.2, 0.8, 0.985, 0.8,
162 1, 0.2, 0.2, 0.985, 0.2,
167 double const lharpoondown[] = {
169 0.015, 0.5, 0.25, 0.985,
170 1, 0.02, 0.475, 0.985, 0.475,
175 double const lharpoonup[] = {
177 0.25, 0.015, 0.015, 0.5,
178 1, 0.02, 0.525, 0.985, 0.525,
183 double const lrharpoons[] = {
185 0.25, 0.015, 0.015, 0.225,
186 1, 0.02, 0.23, 0.985, 0.23,
188 0.75, 0.985, 0.985, 0.775,
189 1, 0.02, 0.7, 0.980, 0.7,
194 double const rlharpoons[] = {
196 0.75, 0.015, 0.985, 0.225,
197 1, 0.02, 0.23, 0.985, 0.23,
199 0.25, 0.985, 0.015, 0.775,
200 1, 0.02, 0.7, 0.980, 0.7,
205 double const arrow[] = {
207 0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
208 0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
210 3, 0.5000, 0.1500, 0.5000, 0.9500,
215 double const Arrow[] = {
217 0.0150, 0.7500, 0.2000, 0.6000, 0.3500, 0.3500,
218 0.5000, 0.0500, 0.6500, 0.3500, 0.8000, 0.6000,
220 3, 0.3500, 0.5000, 0.3500, 0.9500,
221 3, 0.6500, 0.5000, 0.6500, 0.9500,
226 double const udarrow[] = {
228 0.015, 0.25, 0.5, 0.05, 0.95, 0.25,
230 0.015, 0.75, 0.5, 0.95, 0.95, 0.75,
231 1, 0.5, 0.1, 0.5, 0.9,
236 double const Udarrow[] = {
238 0.015, 0.25, 0.5, 0.05, 0.95, 0.25,
240 0.015, 0.75, 0.5, 0.95, 0.95, 0.75,
241 1, 0.35, 0.2, 0.35, 0.8,
242 1, 0.65, 0.2, 0.65, 0.8,
247 double const brack[] = {
249 0.95, 0.05, 0.05, 0.05, 0.05, 0.95, 0.95, 0.95,
254 double const dbrack[] = {
256 0.95, 0.05, 0.05, 0.05, 0.05, 0.95, 0.95, 0.95,
258 0.50, 0.05, 0.50, 0.95,
263 double const corner[] = {
265 0.95, 0.05, 0.05, 0.05, 0.05, 0.95,
270 double const angle[] = {
272 1, 0, 0.05, 0.5, 1, 1,
277 double const slash[] = {
278 1, 0.95, 0.05, 0.05, 0.95,
283 double const hline[] = {
284 1, 0.00, 0.5, 1.0, 0.5,
289 double const ddot[] = {
290 1, 0.2, 0.5, 0.3, 0.5,
291 1, 0.7, 0.5, 0.8, 0.5,
296 double const dddot[] = {
297 1, 0.1, 0.5, 0.2, 0.5,
298 1, 0.45, 0.5, 0.55, 0.5,
299 1, 0.8, 0.5, 0.9, 0.5,
304 double const ddddot[] = {
305 1, 0.1, 0.5, 0.2, 0.5,
306 1, 0.45, 0.5, 0.55, 0.5,
307 1, 0.8, 0.5, 0.9, 0.5,
308 1, 1.15, 0.5, 1.25, 0.5,
313 double const hline3[] = {
315 1, 0.475, 0, 0.525, 0,
321 double const dline3[] = {
322 1, 0.1, 0.1, 0.15, 0.15,
323 1, 0.475, 0.475, 0.525, 0.525,
324 1, 0.85, 0.85, 0.9, 0.9,
329 double const hlinesmall[] = {
330 1, 0.4, 0.5, 0.6, 0.5,
335 double const ring[] = {
337 0.5, 0.8, 0.8, 0.5, 0.5, 0.2, 0.2, 0.5, 0.5, 0.8,
342 double const vert[] = {
343 1, 0.5, 0.05, 0.5, 0.95,
348 double const Vert[] = {
349 1, 0.3, 0.05, 0.3, 0.95,
350 1, 0.7, 0.05, 0.7, 0.95,
355 double const tilde[] = {
357 0.00, 0.8, 0.25, 0.2, 0.75, 0.8, 1.00, 0.2,
367 struct named_deco_struct {
373 named_deco_struct deco_table[] = {
375 {"widehat", angle, 3 },
376 {"widetilde", tilde, 0 },
377 {"underbar", hline, 0 },
378 {"underline", hline, 0 },
379 {"overline", hline, 0 },
380 {"underbrace", brace, 1 },
381 {"overbrace", brace, 3 },
382 {"overleftarrow", arrow, 1 },
383 {"overrightarrow", arrow, 3 },
384 {"overleftrightarrow", udarrow, 1 },
385 {"xhookleftarrow", lhook, 0 },
386 {"xhookrightarrow", rhook, 0 },
387 {"xleftarrow", arrow, 1 },
388 {"xLeftarrow", LArrow, 0 },
389 {"xleftharpoondown", lharpoondown, 0 },
390 {"xleftharpoonup", lharpoonup, 0 },
391 {"xleftrightharpoons", lrharpoons, 0 },
392 {"xleftrightarrow", udarrow, 1 },
393 {"xLeftrightarrow", LRArrow, 0 },
394 {"xmapsto", mapsto, 0 },
395 {"xrightarrow", arrow, 3 },
396 {"xRightarrow", LArrow, 2 },
397 {"xrightharpoondown", lharpoonup, 2 },
398 {"xrightharpoonup", lharpoondown, 2 },
399 {"xrightleftharpoons", rlharpoons, 0 },
400 {"underleftarrow", arrow, 1 },
401 {"underrightarrow", arrow, 3 },
402 {"underleftrightarrow", udarrow, 1 },
403 {"undertilde", tilde, 0 },
404 {"utilde", tilde, 0 },
411 {"lbrace", brace, 0 },
412 {"rbrace", brace, 2 },
415 {"llbracket", dbrack, 0 },
416 {"rrbracket", dbrack, 2 },
419 {"slash", slash, 0 },
430 {"backslash", slash, 1 },
431 {"langle", angle, 0 },
432 {"lceil", corner, 0 },
433 {"lfloor", corner, 1 },
434 {"rangle", angle, 2 },
435 {"rceil", corner, 3 },
436 {"rfloor", corner, 2 },
437 {"downarrow", arrow, 2 },
438 {"Downarrow", Arrow, 2 },
439 {"uparrow", arrow, 0 },
440 {"Uparrow", Arrow, 0 },
441 {"updownarrow", udarrow, 0 },
442 {"Updownarrow", Udarrow, 0 },
446 {"dddot", dddot, 0 },
447 {"ddddot", ddddot, 0 },
449 {"grave", slash, 1 },
450 {"acute", slash, 0 },
451 {"tilde", tilde, 0 },
453 {"dot", hlinesmall, 0 },
454 {"check", angle, 1 },
455 {"breve", parenth, 1 },
457 {"mathring", ring, 0 },
460 {"dots", hline3, 0 },
461 {"ldots", hline3, 0 },
462 {"cdots", hline3, 0 },
463 {"vdots", hline3, 1 },
464 {"ddots", dline3, 0 },
465 {"adots", dline3, 1 },
466 {"iddots", dline3, 1 },
467 {"dotsb", hline3, 0 },
468 {"dotsc", hline3, 0 },
469 {"dotsi", hline3, 0 },
470 {"dotsm", hline3, 0 },
471 {"dotso", hline3, 0 }
475 map<docstring, deco_struct> deco_list;
477 // sort the table on startup
478 class init_deco_table {
481 unsigned const n = sizeof(deco_table) / sizeof(deco_table[0]);
482 for (named_deco_struct * p = deco_table; p != deco_table + n; ++p) {
486 deco_list[from_ascii(p->name)] = d;
491 static init_deco_table dummy;
494 deco_struct const * search_deco(docstring const & name)
496 map<docstring, deco_struct>::const_iterator p = deco_list.find(name);
497 return p == deco_list.end() ? 0 : &(p->second);
504 int mathed_char_width(FontInfo const & font, char_type c)
506 return theFontMetrics(font).width(c);
510 int mathed_char_kerning(FontInfo const & font, char_type c)
512 frontend::FontMetrics const & fm = theFontMetrics(font);
513 return fm.rbearing(c) - fm.width(c);
517 void mathed_string_dim(FontInfo const & font,
521 frontend::FontMetrics const & fm = theFontMetrics(font);
524 for (docstring::const_iterator it = s.begin();
527 dim.asc = max(dim.asc, fm.ascent(*it));
528 dim.des = max(dim.des, fm.descent(*it));
530 dim.wid = fm.width(s);
534 int mathed_string_width(FontInfo const & font, docstring const & s)
536 return theFontMetrics(font).width(s);
540 void mathed_draw_deco(PainterInfo & pi, int x, int y, int w, int h,
541 docstring const & name)
544 pi.pain.line(x + w/2, y, x + w/2, y + h,
545 Color_cursor, Painter::line_onoffdash);
549 deco_struct const * mds = search_deco(name);
551 lyxerr << "Deco was not found. Programming error?" << endl;
552 lyxerr << "name: '" << to_utf8(name) << "'" << endl;
556 int const n = (w < h) ? w : h;
557 int const r = mds->angle;
558 double const * d = mds->data;
560 if (h > 70 && (name == "(" || name == ")"))
564 Matrix sqmt(r, n, n);
572 for (int i = 0; d[i]; ) {
573 int code = int(d[i++]);
574 if (code & 1) { // code == 1 || code == 3
580 sqmt.transform(xx, yy);
582 mt.transform(xx, yy);
583 mt.transform(x2, y2);
585 int(x + xx + 0.5), int(y + yy + 0.5),
586 int(x + x2 + 0.5), int(y + y2 + 0.5),
587 pi.base.font.color());
591 int const n = int(d[i++]);
592 for (int j = 0; j < n; ++j) {
595 // lyxerr << ' ' << xx << ' ' << yy << ' ';
597 sqmt.transform(xx, yy);
599 mt.transform(xx, yy);
600 xp[j] = int(x + xx + 0.5);
601 yp[j] = int(y + yy + 0.5);
602 // lyxerr << "P[" << j ' ' << xx << ' ' << yy << ' ' << x << ' ' << y << ']';
604 pi.pain.lines(xp, yp, n, pi.base.font.color());
610 void metricsStrRedBlack(MetricsInfo & mi, Dimension & dim, docstring const & str)
612 FontInfo font = mi.base.font;
613 augmentFont(font, from_ascii("mathnormal"));
614 mathed_string_dim(font, str, dim);
618 void drawStrRed(PainterInfo & pi, int x, int y, docstring const & str)
620 FontInfo f = pi.base.font;
621 augmentFont(f, from_ascii("mathnormal"));
622 f.setColor(Color_latex);
623 pi.pain.text(x, y, str, f);
627 void drawStrBlack(PainterInfo & pi, int x, int y, docstring const & str)
629 FontInfo f = pi.base.font;
630 augmentFont(f, from_ascii("mathnormal"));
631 f.setColor(Color_foreground);
632 pi.pain.text(x, y, str, f);
636 void math_font_max_dim(FontInfo const & font, int & asc, int & des)
638 frontend::FontMetrics const & fm = theFontMetrics(font);
639 asc = fm.maxAscent();
640 des = fm.maxDescent();
653 FontFamily const inh_family = INHERIT_FAMILY;
654 FontSeries const inh_series = INHERIT_SERIES;
655 FontShape const inh_shape = INHERIT_SHAPE;
658 // mathnormal should be the first, otherwise the fallback further down
660 fontinfo fontinfos[] = {
662 {"mathnormal", ROMAN_FAMILY, MEDIUM_SERIES,
663 ITALIC_SHAPE, Color_math},
664 {"mathbf", inh_family, BOLD_SERIES,
665 inh_shape, Color_math},
666 {"mathcal", CMSY_FAMILY, inh_series,
667 inh_shape, Color_math},
668 {"mathfrak", EUFRAK_FAMILY, inh_series,
669 inh_shape, Color_math},
670 {"mathrm", ROMAN_FAMILY, inh_series,
671 UP_SHAPE, Color_math},
672 {"mathsf", SANS_FAMILY, inh_series,
673 inh_shape, Color_math},
674 {"mathbb", MSB_FAMILY, inh_series,
675 inh_shape, Color_math},
676 {"mathtt", TYPEWRITER_FAMILY, inh_series,
677 inh_shape, Color_math},
678 {"mathit", inh_family, inh_series,
679 ITALIC_SHAPE, Color_math},
680 {"mathscr", RSFS_FAMILY, inh_series,
681 inh_shape, Color_math},
682 {"cmex", CMEX_FAMILY, inh_series,
683 inh_shape, Color_math},
684 {"cmm", CMM_FAMILY, inh_series,
685 inh_shape, Color_math},
686 {"cmr", CMR_FAMILY, inh_series,
687 inh_shape, Color_math},
688 {"cmsy", CMSY_FAMILY, inh_series,
689 inh_shape, Color_math},
690 {"eufrak", EUFRAK_FAMILY, inh_series,
691 inh_shape, Color_math},
692 {"msa", MSA_FAMILY, inh_series,
693 inh_shape, Color_math},
694 {"msb", MSB_FAMILY, inh_series,
695 inh_shape, Color_math},
696 {"stmry", STMARY_FAMILY, inh_series,
697 inh_shape, Color_math},
698 {"wasy", WASY_FAMILY, inh_series,
699 inh_shape, Color_math},
700 {"esint", ESINT_FAMILY, inh_series,
701 inh_shape, Color_math},
704 {"text", inh_family, inh_series,
705 inh_shape, Color_foreground},
706 {"textbf", inh_family, BOLD_SERIES,
707 inh_shape, Color_foreground},
708 {"textit", inh_family, inh_series,
709 ITALIC_SHAPE, Color_foreground},
710 {"textmd", inh_family, MEDIUM_SERIES,
711 inh_shape, Color_foreground},
712 {"textnormal", inh_family, inh_series,
713 UP_SHAPE, Color_foreground},
714 {"textrm", ROMAN_FAMILY,
715 inh_series, UP_SHAPE,Color_foreground},
716 {"textsc", inh_family, inh_series,
717 SMALLCAPS_SHAPE, Color_foreground},
718 {"textsf", SANS_FAMILY, inh_series,
719 inh_shape, Color_foreground},
720 {"textsl", inh_family, inh_series,
721 SLANTED_SHAPE, Color_foreground},
722 {"texttt", TYPEWRITER_FAMILY, inh_series,
723 inh_shape, Color_foreground},
724 {"textup", inh_family, inh_series,
725 UP_SHAPE, Color_foreground},
728 {"textipa", inh_family, inh_series,
729 inh_shape, Color_foreground},
732 {"ce", inh_family, inh_series,
733 inh_shape, Color_foreground},
734 {"cf", inh_family, inh_series,
735 inh_shape, Color_foreground},
737 // LyX internal usage
738 {"lyxtex", inh_family, inh_series,
739 UP_SHAPE, Color_latex},
740 // FIXME: The following two don't work on OS X, since the Symbol font
741 // uses a different encoding, and is therefore disabled in
742 // FontLoader::available().
743 {"lyxsymbol", SYMBOL_FAMILY, inh_series,
744 inh_shape, Color_math},
745 {"lyxboldsymbol", SYMBOL_FAMILY, BOLD_SERIES,
746 inh_shape, Color_math},
747 {"lyxblacktext", ROMAN_FAMILY, MEDIUM_SERIES,
748 UP_SHAPE, Color_foreground},
749 {"lyxnochange", inh_family, inh_series,
750 inh_shape, Color_foreground},
751 {"lyxfakebb", TYPEWRITER_FAMILY, BOLD_SERIES,
752 UP_SHAPE, Color_math},
753 {"lyxfakecal", SANS_FAMILY, MEDIUM_SERIES,
754 ITALIC_SHAPE, Color_math},
755 {"lyxfakefrak", ROMAN_FAMILY, BOLD_SERIES,
756 ITALIC_SHAPE, Color_math}
760 fontinfo * lookupFont(docstring const & name0)
762 //lyxerr << "searching font '" << name << "'" << endl;
763 int const n = sizeof(fontinfos) / sizeof(fontinfo);
764 string name = to_utf8(name0);
765 for (int i = 0; i < n; ++i)
766 if (fontinfos[i].cmd_ == name) {
767 //lyxerr << "found '" << i << "'" << endl;
768 return fontinfos + i;
774 fontinfo * searchFont(docstring const & name)
776 fontinfo * f = lookupFont(name);
777 return f ? f : fontinfos;
778 // this should be mathnormal
779 //return searchFont("mathnormal");
783 bool isFontName(docstring const & name)
785 return lookupFont(name);
789 bool isMathFont(docstring const & name)
791 fontinfo * f = lookupFont(name);
792 return f && f->color_ == Color_math;
796 bool isTextFont(docstring const & name)
798 fontinfo * f = lookupFont(name);
799 return f && f->color_ == Color_foreground;
803 FontInfo getFont(docstring const & name)
806 augmentFont(font, name);
811 void fakeFont(docstring const & orig, docstring const & fake)
813 fontinfo * forig = searchFont(orig);
814 fontinfo * ffake = searchFont(fake);
815 if (forig && ffake) {
816 forig->family_ = ffake->family_;
817 forig->series_ = ffake->series_;
818 forig->shape_ = ffake->shape_;
819 forig->color_ = ffake->color_;
821 lyxerr << "Can't fake font '" << to_utf8(orig) << "' with '"
822 << to_utf8(fake) << "'" << endl;
827 void augmentFont(FontInfo & font, docstring const & name)
829 static bool initialized = false;
832 // fake fonts if necessary
833 if (!theFontLoader().available(getFont(from_ascii("mathfrak"))))
834 fakeFont(from_ascii("mathfrak"), from_ascii("lyxfakefrak"));
835 if (!theFontLoader().available(getFont(from_ascii("mathcal"))))
836 fakeFont(from_ascii("mathcal"), from_ascii("lyxfakecal"));
838 fontinfo * info = searchFont(name);
839 if (info->family_ != inh_family)
840 font.setFamily(info->family_);
841 if (info->series_ != inh_series)
842 font.setSeries(info->series_);
843 if (info->shape_ != inh_shape)
844 font.setShape(info->shape_);
845 if (info->color_ != Color_none)
846 font.setColor(info->color_);
850 bool isAlphaSymbol(MathAtom const & at)
852 if (at->asCharInset() ||
853 (at->asSymbolInset() &&
854 at->asSymbolInset()->isOrdAlpha()))
857 if (at->asFontInset()) {
858 MathData const & ar = at->asFontInset()->cell(0);
859 for (size_t i = 0; i < ar.size(); ++i) {
860 if (!(ar[i]->asCharInset() ||
861 (ar[i]->asSymbolInset() &&
862 ar[i]->asSymbolInset()->isOrdAlpha())))
871 docstring asString(MathData const & ar)
880 void asArray(docstring const & str, MathData & ar, Parse::flags pf)
882 bool quiet = pf & Parse::QUIET;
883 if ((str.size() == 1 && quiet) || (!mathed_parse_cell(ar, str, pf) && quiet))
884 mathed_parse_cell(ar, str, pf | Parse::VERBATIM);
888 docstring asString(InsetMath const & inset)
897 docstring asString(MathAtom const & at)