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 corner[] = {
165 0.95, 0.05, 0.05, 0.05, 0.05, 0.95,
170 double const angle[] = {
172 1, 0, 0.05, 0.5, 1, 1,
177 double const slash[] = {
178 1, 0.95, 0.05, 0.05, 0.95,
183 double const hline[] = {
184 1, 0.00, 0.5, 1.0, 0.5,
189 double const ddot[] = {
190 1, 0.2, 0.5, 0.3, 0.5,
191 1, 0.7, 0.5, 0.8, 0.5,
196 double const dddot[] = {
197 1, 0.1, 0.5, 0.2, 0.5,
198 1, 0.45, 0.5, 0.55, 0.5,
199 1, 0.8, 0.5, 0.9, 0.5,
204 double const ddddot[] = {
205 1, 0.1, 0.5, 0.2, 0.5,
206 1, 0.45, 0.5, 0.55, 0.5,
207 1, 0.8, 0.5, 0.9, 0.5,
208 1, 1.15, 0.5, 1.25, 0.5,
213 double const hline3[] = {
215 1, 0.475, 0, 0.525, 0,
221 double const dline3[] = {
222 1, 0.1, 0.1, 0.15, 0.15,
223 1, 0.475, 0.475, 0.525, 0.525,
224 1, 0.85, 0.85, 0.9, 0.9,
229 double const hlinesmall[] = {
230 1, 0.4, 0.5, 0.6, 0.5,
235 double const ring[] = {
237 0.5, 0.8, 0.8, 0.5, 0.5, 0.2, 0.2, 0.5, 0.5, 0.8,
242 double const vert[] = {
243 1, 0.5, 0.05, 0.5, 0.95,
248 double const Vert[] = {
249 1, 0.3, 0.05, 0.3, 0.95,
250 1, 0.7, 0.05, 0.7, 0.95,
255 double const tilde[] = {
257 0.00, 0.8, 0.25, 0.2, 0.75, 0.8, 1.00, 0.2,
267 struct named_deco_struct {
273 named_deco_struct deco_table[] = {
275 {"widehat", angle, 3 },
276 {"widetilde", tilde, 0 },
277 {"underbar", hline, 0 },
278 {"underline", hline, 0 },
279 {"overline", hline, 0 },
280 {"underbrace", brace, 1 },
281 {"overbrace", brace, 3 },
282 {"overleftarrow", arrow, 1 },
283 {"overrightarrow", arrow, 3 },
284 {"overleftrightarrow", udarrow, 1 },
285 {"xleftarrow", arrow, 1 },
286 {"xrightarrow", arrow, 3 },
287 {"underleftarrow", arrow, 1 },
288 {"underrightarrow", arrow, 3 },
289 {"underleftrightarrow", udarrow, 1 },
290 {"undertilde", tilde, 0 },
291 {"utilde", tilde, 0 },
298 {"lbrace", brace, 0 },
299 {"rbrace", brace, 2 },
304 {"slash", slash, 0 },
311 {"backslash", slash, 1 },
312 {"langle", angle, 0 },
313 {"lceil", corner, 0 },
314 {"lfloor", corner, 1 },
315 {"rangle", angle, 2 },
316 {"rceil", corner, 3 },
317 {"rfloor", corner, 2 },
318 {"downarrow", arrow, 2 },
319 {"Downarrow", Arrow, 2 },
320 {"uparrow", arrow, 0 },
321 {"Uparrow", Arrow, 0 },
322 {"updownarrow", udarrow, 0 },
323 {"Updownarrow", Udarrow, 0 },
327 {"dddot", dddot, 0 },
328 {"ddddot", ddddot, 0 },
330 {"grave", slash, 1 },
331 {"acute", slash, 0 },
332 {"tilde", tilde, 0 },
334 {"dot", hlinesmall, 0 },
335 {"check", angle, 1 },
336 {"breve", parenth, 1 },
338 {"mathring", ring, 0 },
341 {"dots", hline3, 0 },
342 {"ldots", hline3, 0 },
343 {"cdots", hline3, 0 },
344 {"vdots", hline3, 1 },
345 {"ddots", dline3, 0 },
346 {"adots", dline3, 1 },
347 {"iddots", dline3, 1 },
348 {"dotsb", hline3, 0 },
349 {"dotsc", hline3, 0 },
350 {"dotsi", hline3, 0 },
351 {"dotsm", hline3, 0 },
352 {"dotso", hline3, 0 }
356 map<docstring, deco_struct> deco_list;
358 // sort the table on startup
359 class init_deco_table {
362 unsigned const n = sizeof(deco_table) / sizeof(deco_table[0]);
363 for (named_deco_struct * p = deco_table; p != deco_table + n; ++p) {
367 deco_list[from_ascii(p->name)] = d;
372 static init_deco_table dummy;
375 deco_struct const * search_deco(docstring const & name)
377 map<docstring, deco_struct>::const_iterator p = deco_list.find(name);
378 return p == deco_list.end() ? 0 : &(p->second);
385 int mathed_char_width(FontInfo const & font, char_type c)
387 return theFontMetrics(font).width(c);
391 int mathed_char_kerning(FontInfo const & font, char_type c)
393 frontend::FontMetrics const & fm = theFontMetrics(font);
394 return fm.rbearing(c) - fm.width(c);
398 void mathed_string_dim(FontInfo const & font,
402 frontend::FontMetrics const & fm = theFontMetrics(font);
405 for (docstring::const_iterator it = s.begin();
408 dim.asc = max(dim.asc, fm.ascent(*it));
409 dim.des = max(dim.des, fm.descent(*it));
411 dim.wid = fm.width(s);
415 int mathed_string_width(FontInfo const & font, docstring const & s)
417 return theFontMetrics(font).width(s);
421 void mathed_draw_deco(PainterInfo & pi, int x, int y, int w, int h,
422 docstring const & name)
425 pi.pain.line(x + w/2, y, x + w/2, y + h,
426 Color_cursor, Painter::line_onoffdash);
430 deco_struct const * mds = search_deco(name);
432 lyxerr << "Deco was not found. Programming error?" << endl;
433 lyxerr << "name: '" << to_utf8(name) << "'" << endl;
437 int const n = (w < h) ? w : h;
438 int const r = mds->angle;
439 double const * d = mds->data;
441 if (h > 70 && (name == "(" || name == ")"))
445 Matrix sqmt(r, n, n);
453 for (int i = 0; d[i]; ) {
454 int code = int(d[i++]);
455 if (code & 1) { // code == 1 || code == 3
461 sqmt.transform(xx, yy);
463 mt.transform(xx, yy);
464 mt.transform(x2, y2);
466 int(x + xx + 0.5), int(y + yy + 0.5),
467 int(x + x2 + 0.5), int(y + y2 + 0.5),
468 pi.base.font.color());
472 int const n = int(d[i++]);
473 for (int j = 0; j < n; ++j) {
476 // lyxerr << ' ' << xx << ' ' << yy << ' ';
478 sqmt.transform(xx, yy);
480 mt.transform(xx, yy);
481 xp[j] = int(x + xx + 0.5);
482 yp[j] = int(y + yy + 0.5);
483 // lyxerr << "P[" << j ' ' << xx << ' ' << yy << ' ' << x << ' ' << y << ']';
485 pi.pain.lines(xp, yp, n, pi.base.font.color());
491 void drawStrRed(PainterInfo & pi, int x, int y, docstring const & str)
493 FontInfo f = pi.base.font;
494 f.setColor(Color_latex);
495 pi.pain.text(x, y, str, f);
499 void drawStrBlack(PainterInfo & pi, int x, int y, docstring const & str)
501 FontInfo f = pi.base.font;
502 f.setColor(Color_foreground);
503 pi.pain.text(x, y, str, f);
507 void math_font_max_dim(FontInfo const & font, int & asc, int & des)
509 frontend::FontMetrics const & fm = theFontMetrics(font);
510 asc = fm.maxAscent();
511 des = fm.maxDescent();
524 FontFamily const inh_family = INHERIT_FAMILY;
525 FontSeries const inh_series = INHERIT_SERIES;
526 FontShape const inh_shape = INHERIT_SHAPE;
529 // mathnormal should be the first, otherwise the fallback further down
531 fontinfo fontinfos[] = {
533 {"mathnormal", ROMAN_FAMILY, MEDIUM_SERIES,
534 ITALIC_SHAPE, Color_math},
535 {"mathbf", inh_family, BOLD_SERIES,
536 inh_shape, Color_math},
537 {"mathcal", CMSY_FAMILY, inh_series,
538 inh_shape, Color_math},
539 {"mathfrak", EUFRAK_FAMILY, inh_series,
540 inh_shape, Color_math},
541 {"mathrm", ROMAN_FAMILY, inh_series,
542 UP_SHAPE, Color_math},
543 {"mathsf", SANS_FAMILY, inh_series,
544 inh_shape, Color_math},
545 {"mathbb", MSB_FAMILY, inh_series,
546 inh_shape, Color_math},
547 {"mathtt", TYPEWRITER_FAMILY, inh_series,
548 inh_shape, Color_math},
549 {"mathit", inh_family, inh_series,
550 ITALIC_SHAPE, Color_math},
551 {"mathscr", RSFS_FAMILY, inh_series,
552 inh_shape, Color_math},
553 {"cmex", CMEX_FAMILY, inh_series,
554 inh_shape, Color_math},
555 {"cmm", CMM_FAMILY, inh_series,
556 inh_shape, Color_math},
557 {"cmr", CMR_FAMILY, inh_series,
558 inh_shape, Color_math},
559 {"cmsy", CMSY_FAMILY, inh_series,
560 inh_shape, Color_math},
561 {"eufrak", EUFRAK_FAMILY, inh_series,
562 inh_shape, Color_math},
563 {"msa", MSA_FAMILY, inh_series,
564 inh_shape, Color_math},
565 {"msb", MSB_FAMILY, inh_series,
566 inh_shape, Color_math},
567 {"wasy", WASY_FAMILY, inh_series,
568 inh_shape, Color_math},
569 {"esint", ESINT_FAMILY, inh_series,
570 inh_shape, Color_math},
573 {"text", inh_family, inh_series,
574 inh_shape, Color_foreground},
575 {"textbf", inh_family, BOLD_SERIES,
576 inh_shape, Color_foreground},
577 {"textit", inh_family, inh_series,
578 ITALIC_SHAPE, Color_foreground},
579 {"textmd", inh_family, MEDIUM_SERIES,
580 inh_shape, Color_foreground},
581 {"textnormal", inh_family, inh_series,
582 UP_SHAPE, Color_foreground},
583 {"textrm", ROMAN_FAMILY,
584 inh_series, UP_SHAPE,Color_foreground},
585 {"textsc", inh_family, inh_series,
586 SMALLCAPS_SHAPE, Color_foreground},
587 {"textsf", SANS_FAMILY, inh_series,
588 inh_shape, Color_foreground},
589 {"textsl", inh_family, inh_series,
590 SLANTED_SHAPE, Color_foreground},
591 {"texttt", TYPEWRITER_FAMILY, inh_series,
592 inh_shape, Color_foreground},
593 {"textup", inh_family, inh_series,
594 UP_SHAPE, Color_foreground},
597 {"textipa", inh_family, inh_series,
598 inh_shape, Color_foreground},
601 {"ce", inh_family, inh_series,
602 inh_shape, Color_foreground},
603 {"cf", inh_family, inh_series,
604 inh_shape, Color_foreground},
606 // LyX internal usage
607 {"lyxtex", inh_family, inh_series,
608 UP_SHAPE, Color_latex},
609 {"lyxsymbol", SYMBOL_FAMILY, inh_series,
610 inh_shape, Color_math},
611 {"lyxboldsymbol", SYMBOL_FAMILY, BOLD_SERIES,
612 inh_shape, Color_math},
613 {"lyxblacktext", ROMAN_FAMILY, MEDIUM_SERIES,
614 UP_SHAPE, Color_foreground},
615 {"lyxnochange", inh_family, inh_series,
616 inh_shape, Color_foreground},
617 {"lyxfakebb", TYPEWRITER_FAMILY, BOLD_SERIES,
618 UP_SHAPE, Color_math},
619 {"lyxfakecal", SANS_FAMILY, MEDIUM_SERIES,
620 ITALIC_SHAPE, Color_math},
621 {"lyxfakefrak", ROMAN_FAMILY, BOLD_SERIES,
622 ITALIC_SHAPE, Color_math}
626 fontinfo * lookupFont(docstring const & name0)
628 //lyxerr << "searching font '" << name << "'" << endl;
629 int const n = sizeof(fontinfos) / sizeof(fontinfo);
630 string name = to_utf8(name0);
631 for (int i = 0; i < n; ++i)
632 if (fontinfos[i].cmd_ == name) {
633 //lyxerr << "found '" << i << "'" << endl;
634 return fontinfos + i;
640 fontinfo * searchFont(docstring const & name)
642 fontinfo * f = lookupFont(name);
643 return f ? f : fontinfos;
644 // this should be mathnormal
645 //return searchFont("mathnormal");
649 bool isFontName(docstring const & name)
651 return lookupFont(name);
655 bool isMathFont(docstring const & name)
657 fontinfo * f = lookupFont(name);
658 return f && f->color_ == Color_math;
662 bool isTextFont(docstring const & name)
664 fontinfo * f = lookupFont(name);
665 return f && f->color_ == Color_foreground;
669 FontInfo getFont(docstring const & name)
672 augmentFont(font, name);
677 void fakeFont(docstring const & orig, docstring const & fake)
679 fontinfo * forig = searchFont(orig);
680 fontinfo * ffake = searchFont(fake);
681 if (forig && ffake) {
682 forig->family_ = ffake->family_;
683 forig->series_ = ffake->series_;
684 forig->shape_ = ffake->shape_;
685 forig->color_ = ffake->color_;
687 lyxerr << "Can't fake font '" << to_utf8(orig) << "' with '"
688 << to_utf8(fake) << "'" << endl;
693 void augmentFont(FontInfo & font, docstring const & name)
695 static bool initialized = false;
698 // fake fonts if necessary
699 if (!theFontLoader().available(getFont(from_ascii("mathfrak"))))
700 fakeFont(from_ascii("mathfrak"), from_ascii("lyxfakefrak"));
701 if (!theFontLoader().available(getFont(from_ascii("mathcal"))))
702 fakeFont(from_ascii("mathcal"), from_ascii("lyxfakecal"));
704 fontinfo * info = searchFont(name);
705 if (info->family_ != inh_family)
706 font.setFamily(info->family_);
707 if (info->series_ != inh_series)
708 font.setSeries(info->series_);
709 if (info->shape_ != inh_shape)
710 font.setShape(info->shape_);
711 if (info->color_ != Color_none)
712 font.setColor(info->color_);
716 docstring asString(MathData const & ar)
725 void asArray(docstring const & str, MathData & ar, Parse::flags pf)
727 bool quiet = pf & Parse::QUIET;
728 if ((str.size() == 1 && quiet) || (!mathed_parse_cell(ar, str, pf) && quiet))
729 mathed_parse_cell(ar, str, pf | Parse::VERBATIM);
733 docstring asString(InsetMath const & inset)
742 docstring asString(MathAtom const & at)