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_font_em(FontInfo const & font)
506 return theFontMetrics(font).em();
510 int mathed_char_width(FontInfo const & font, char_type c)
512 return theFontMetrics(font).width(c);
516 int mathed_char_kerning(FontInfo const & font, char_type c)
518 frontend::FontMetrics const & fm = theFontMetrics(font);
519 return fm.rbearing(c) - fm.width(c);
523 void mathed_string_dim(FontInfo const & font,
527 frontend::FontMetrics const & fm = theFontMetrics(font);
530 for (docstring::const_iterator it = s.begin();
533 dim.asc = max(dim.asc, fm.ascent(*it));
534 dim.des = max(dim.des, fm.descent(*it));
536 dim.wid = fm.width(s);
540 int mathed_string_width(FontInfo const & font, docstring const & s)
542 return theFontMetrics(font).width(s);
546 void mathed_draw_deco(PainterInfo & pi, int x, int y, int w, int h,
547 docstring const & name)
550 pi.pain.line(x + w/2, y, x + w/2, y + h,
551 Color_cursor, Painter::line_onoffdash);
555 deco_struct const * mds = search_deco(name);
557 lyxerr << "Deco was not found. Programming error?" << endl;
558 lyxerr << "name: '" << to_utf8(name) << "'" << endl;
562 int const n = (w < h) ? w : h;
563 int const r = mds->angle;
564 double const * d = mds->data;
566 if (h > 70 && (name == "(" || name == ")"))
570 Matrix sqmt(r, n, n);
578 for (int i = 0; d[i]; ) {
579 int code = int(d[i++]);
580 if (code & 1) { // code == 1 || code == 3
586 sqmt.transform(xx, yy);
588 mt.transform(xx, yy);
589 mt.transform(x2, y2);
591 int(x + xx + 0.5), int(y + yy + 0.5),
592 int(x + x2 + 0.5), int(y + y2 + 0.5),
593 pi.base.font.color());
597 int const n = int(d[i++]);
598 for (int j = 0; j < n; ++j) {
601 // lyxerr << ' ' << xx << ' ' << yy << ' ';
603 sqmt.transform(xx, yy);
605 mt.transform(xx, yy);
606 xp[j] = int(x + xx + 0.5);
607 yp[j] = int(y + yy + 0.5);
608 // lyxerr << "P[" << j ' ' << xx << ' ' << yy << ' ' << x << ' ' << y << ']';
610 pi.pain.lines(xp, yp, n, pi.base.font.color());
616 void metricsStrRedBlack(MetricsInfo & mi, Dimension & dim, docstring const & str)
618 FontInfo font = mi.base.font;
619 augmentFont(font, from_ascii("mathnormal"));
620 mathed_string_dim(font, str, dim);
624 void drawStrRed(PainterInfo & pi, int x, int y, docstring const & str)
626 FontInfo f = pi.base.font;
627 augmentFont(f, from_ascii("mathnormal"));
628 f.setColor(Color_latex);
629 pi.pain.text(x, y, str, f);
633 void drawStrBlack(PainterInfo & pi, int x, int y, docstring const & str)
635 FontInfo f = pi.base.font;
636 augmentFont(f, from_ascii("mathnormal"));
637 f.setColor(Color_foreground);
638 pi.pain.text(x, y, str, f);
642 void math_font_max_dim(FontInfo const & font, int & asc, int & des)
644 frontend::FontMetrics const & fm = theFontMetrics(font);
645 asc = fm.maxAscent();
646 des = fm.maxDescent();
659 FontFamily const inh_family = INHERIT_FAMILY;
660 FontSeries const inh_series = INHERIT_SERIES;
661 FontShape const inh_shape = INHERIT_SHAPE;
664 // mathnormal should be the first, otherwise the fallback further down
666 fontinfo fontinfos[] = {
668 {"mathnormal", ROMAN_FAMILY, MEDIUM_SERIES,
669 ITALIC_SHAPE, Color_math},
670 {"mathbf", inh_family, BOLD_SERIES,
671 inh_shape, Color_math},
672 {"mathcal", CMSY_FAMILY, inh_series,
673 inh_shape, Color_math},
674 {"mathfrak", EUFRAK_FAMILY, inh_series,
675 inh_shape, Color_math},
676 {"mathrm", ROMAN_FAMILY, inh_series,
677 UP_SHAPE, Color_math},
678 {"mathsf", SANS_FAMILY, inh_series,
679 inh_shape, Color_math},
680 {"mathbb", MSB_FAMILY, inh_series,
681 inh_shape, Color_math},
682 {"mathtt", TYPEWRITER_FAMILY, inh_series,
683 inh_shape, Color_math},
684 {"mathit", inh_family, inh_series,
685 ITALIC_SHAPE, Color_math},
686 {"mathscr", RSFS_FAMILY, inh_series,
687 inh_shape, Color_math},
688 {"cmex", CMEX_FAMILY, inh_series,
689 inh_shape, Color_math},
690 {"cmm", CMM_FAMILY, inh_series,
691 inh_shape, Color_math},
692 {"cmr", CMR_FAMILY, inh_series,
693 inh_shape, Color_math},
694 {"cmsy", CMSY_FAMILY, inh_series,
695 inh_shape, Color_math},
696 {"eufrak", EUFRAK_FAMILY, inh_series,
697 inh_shape, Color_math},
698 {"msa", MSA_FAMILY, inh_series,
699 inh_shape, Color_math},
700 {"msb", MSB_FAMILY, inh_series,
701 inh_shape, Color_math},
702 {"stmry", STMARY_FAMILY, inh_series,
703 inh_shape, Color_math},
704 {"wasy", WASY_FAMILY, inh_series,
705 inh_shape, Color_math},
706 {"esint", ESINT_FAMILY, inh_series,
707 inh_shape, Color_math},
710 {"text", inh_family, inh_series,
711 inh_shape, Color_foreground},
712 {"textbf", inh_family, BOLD_SERIES,
713 inh_shape, Color_foreground},
714 {"textit", inh_family, inh_series,
715 ITALIC_SHAPE, Color_foreground},
716 {"textmd", inh_family, MEDIUM_SERIES,
717 inh_shape, Color_foreground},
718 {"textnormal", inh_family, inh_series,
719 UP_SHAPE, Color_foreground},
720 {"textrm", ROMAN_FAMILY,
721 inh_series, UP_SHAPE,Color_foreground},
722 {"textsc", inh_family, inh_series,
723 SMALLCAPS_SHAPE, Color_foreground},
724 {"textsf", SANS_FAMILY, inh_series,
725 inh_shape, Color_foreground},
726 {"textsl", inh_family, inh_series,
727 SLANTED_SHAPE, Color_foreground},
728 {"texttt", TYPEWRITER_FAMILY, inh_series,
729 inh_shape, Color_foreground},
730 {"textup", inh_family, inh_series,
731 UP_SHAPE, Color_foreground},
734 {"textipa", inh_family, inh_series,
735 inh_shape, Color_foreground},
738 {"ce", inh_family, inh_series,
739 inh_shape, Color_foreground},
740 {"cf", inh_family, inh_series,
741 inh_shape, Color_foreground},
743 // LyX internal usage
744 {"lyxtex", inh_family, inh_series,
745 UP_SHAPE, Color_latex},
746 // FIXME: The following two don't work on OS X, since the Symbol font
747 // uses a different encoding, and is therefore disabled in
748 // FontLoader::available().
749 {"lyxsymbol", SYMBOL_FAMILY, inh_series,
750 inh_shape, Color_math},
751 {"lyxboldsymbol", SYMBOL_FAMILY, BOLD_SERIES,
752 inh_shape, Color_math},
753 {"lyxblacktext", ROMAN_FAMILY, MEDIUM_SERIES,
754 UP_SHAPE, Color_foreground},
755 {"lyxnochange", inh_family, inh_series,
756 inh_shape, Color_foreground},
757 {"lyxfakebb", TYPEWRITER_FAMILY, BOLD_SERIES,
758 UP_SHAPE, Color_math},
759 {"lyxfakecal", SANS_FAMILY, MEDIUM_SERIES,
760 ITALIC_SHAPE, Color_math},
761 {"lyxfakefrak", ROMAN_FAMILY, BOLD_SERIES,
762 ITALIC_SHAPE, Color_math}
766 fontinfo * lookupFont(docstring const & name0)
768 //lyxerr << "searching font '" << name << "'" << endl;
769 int const n = sizeof(fontinfos) / sizeof(fontinfo);
770 string name = to_utf8(name0);
771 for (int i = 0; i < n; ++i)
772 if (fontinfos[i].cmd_ == name) {
773 //lyxerr << "found '" << i << "'" << endl;
774 return fontinfos + i;
780 fontinfo * searchFont(docstring const & name)
782 fontinfo * f = lookupFont(name);
783 return f ? f : fontinfos;
784 // this should be mathnormal
785 //return searchFont("mathnormal");
789 bool isFontName(docstring const & name)
791 return lookupFont(name);
795 bool isMathFont(docstring const & name)
797 fontinfo * f = lookupFont(name);
798 return f && f->color_ == Color_math;
802 bool isTextFont(docstring const & name)
804 fontinfo * f = lookupFont(name);
805 return f && f->color_ == Color_foreground;
809 FontInfo getFont(docstring const & name)
812 augmentFont(font, name);
817 void fakeFont(docstring const & orig, docstring const & fake)
819 fontinfo * forig = searchFont(orig);
820 fontinfo * ffake = searchFont(fake);
821 if (forig && ffake) {
822 forig->family_ = ffake->family_;
823 forig->series_ = ffake->series_;
824 forig->shape_ = ffake->shape_;
825 forig->color_ = ffake->color_;
827 lyxerr << "Can't fake font '" << to_utf8(orig) << "' with '"
828 << to_utf8(fake) << "'" << endl;
833 void augmentFont(FontInfo & font, docstring const & name)
835 static bool initialized = false;
838 // fake fonts if necessary
839 if (!theFontLoader().available(getFont(from_ascii("mathfrak"))))
840 fakeFont(from_ascii("mathfrak"), from_ascii("lyxfakefrak"));
841 if (!theFontLoader().available(getFont(from_ascii("mathcal"))))
842 fakeFont(from_ascii("mathcal"), from_ascii("lyxfakecal"));
844 fontinfo * info = searchFont(name);
845 if (info->family_ != inh_family)
846 font.setFamily(info->family_);
847 if (info->series_ != inh_series)
848 font.setSeries(info->series_);
849 if (info->shape_ != inh_shape)
850 font.setShape(info->shape_);
851 if (info->color_ != Color_none)
852 font.setColor(info->color_);
856 bool isAlphaSymbol(MathAtom const & at)
858 if (at->asCharInset() ||
859 (at->asSymbolInset() &&
860 at->asSymbolInset()->isOrdAlpha()))
863 if (at->asFontInset()) {
864 MathData const & ar = at->asFontInset()->cell(0);
865 for (size_t i = 0; i < ar.size(); ++i) {
866 if (!(ar[i]->asCharInset() ||
867 (ar[i]->asSymbolInset() &&
868 ar[i]->asSymbolInset()->isOrdAlpha())))
877 docstring asString(MathData const & ar)
880 TexRow texrow(false);
881 otexrowstream ots(os,texrow);
888 void asArray(docstring const & str, MathData & ar, Parse::flags pf)
890 bool quiet = pf & Parse::QUIET;
891 if ((str.size() == 1 && quiet) || (!mathed_parse_cell(ar, str, pf) && quiet))
892 mathed_parse_cell(ar, str, pf | Parse::VERBATIM);
896 docstring asString(InsetMath const & inset)
899 TexRow texrow(false);
900 otexrowstream ots(os,texrow);
907 docstring asString(MathAtom const & at)
910 TexRow texrow(false);
911 otexrowstream ots(os,texrow);