]> git.lyx.org Git - lyx.git/blob - src/mathed/MathSupport.cpp
add busy.gif to resources (in line with cmake)
[lyx.git] / src / mathed / MathSupport.cpp
1 /**
2  * \file MathSupport.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author André Pönitz
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "MathSupport.h"
15
16 #include "InsetMath.h"
17 #include "MathData.h"
18 #include "MathParser.h"
19 #include "MathStream.h"
20
21 #include "frontends/FontLoader.h"
22 #include "frontends/FontMetrics.h"
23 #include "frontends/Painter.h"
24
25 #include "support/debug.h"
26 #include "support/docstream.h"
27
28 #include <map>
29
30 using namespace std;
31
32 namespace lyx {
33
34 using frontend::Painter;
35
36
37 ///
38 class Matrix {
39 public:
40         ///
41         Matrix(int, double, double);
42         ///
43         void transform(double &, double &);
44 private:
45         ///
46         double m_[2][2];
47 };
48
49
50 Matrix::Matrix(int code, double x, double y)
51 {
52         double const cs = (code & 1) ? 0 : (1 - code);
53         double const sn = (code & 1) ? (2 - code) : 0;
54         m_[0][0] =  cs * x;
55         m_[0][1] =  sn * x;
56         m_[1][0] = -sn * y;
57         m_[1][1] =  cs * y;
58 }
59
60
61 void Matrix::transform(double & x, double & y)
62 {
63         double xx = m_[0][0] * x + m_[0][1] * y;
64         double yy = m_[1][0] * x + m_[1][1] * y;
65         x = xx;
66         y = yy;
67 }
68
69
70
71 namespace {
72
73 /*
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
76  */
77
78
79 double const parenthHigh[] = {
80         2, 13,
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,
85         0.9840, 0.9986,
86         0
87 };
88
89
90 double const parenth[] = {
91         2, 13,
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,
96         0.9930, 0.9919,
97         0
98 };
99
100
101 double const brace[] = {
102         2, 21,
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,
110         0
111 };
112
113
114 double const arrow[] = {
115         4, 7,
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,
118         0.9500, 0.7500,
119         3, 0.5000, 0.1500, 0.5000, 0.9500,
120         0
121 };
122
123
124 double const Arrow[] = {
125         4, 7,
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,
128         0.9500, 0.7500,
129         3, 0.3500, 0.5000, 0.3500, 0.9500,
130         3, 0.6500, 0.5000, 0.6500, 0.9500,
131         0
132 };
133
134
135 double const udarrow[] = {
136         2, 3,
137         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
138         2, 3,
139         0.015, 0.75,  0.5, 0.95, 0.95, 0.75,
140         1, 0.5, 0.2,  0.5, 0.8,
141         0
142 };
143
144
145 double const Udarrow[] = {
146         2, 3,
147         0.015, 0.25,  0.5, 0.05, 0.95, 0.25,
148         2, 3,
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,
152         0
153 };
154
155
156 double const brack[] = {
157         2, 4,
158         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,  0.95, 0.95,
159         0
160 };
161
162
163 double const corner[] = {
164         2, 3,
165         0.95, 0.05,  0.05, 0.05,  0.05, 0.95,
166         0
167 };
168
169
170 double const angle[] = {
171         2, 3,
172         1, 0,  0.05, 0.5,  1, 1,
173         0
174 };
175
176
177 double const slash[] = {
178         1, 0.95, 0.05, 0.05, 0.95,
179         0
180 };
181
182
183 double const hline[] = {
184         1, 0.00, 0.5, 1.0, 0.5,
185         0
186 };
187
188
189 double const ddot[] = {
190         1, 0.2, 0.5, 0.3, 0.5,
191         1, 0.7, 0.5, 0.8, 0.5,
192         0
193 };
194
195
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,
200         0
201 };
202
203
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,
209         0
210 };
211
212
213 double const hline3[] = {
214         1, 0.1,   0,  0.15,  0,
215         1, 0.475, 0,  0.525, 0,
216         1, 0.85,  0,  0.9,   0,
217         0
218 };
219
220
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,
225         0
226 };
227
228
229 double const hlinesmall[] = {
230         1, 0.4, 0.5, 0.6, 0.5,
231         0
232 };
233
234
235 double const ring[] = {
236         2, 5,
237         0.5, 0.8,  0.8, 0.5,  0.5, 0.2,  0.2, 0.5,  0.5, 0.8,
238         0
239 };
240
241
242 double const vert[] = {
243         1, 0.5, 0.05,  0.5, 0.95,
244         0
245 };
246
247
248 double const  Vert[] = {
249         1, 0.3, 0.05,  0.3, 0.95,
250         1, 0.7, 0.05,  0.7, 0.95,
251         0
252 };
253
254
255 double const tilde[] = {
256         2, 4,
257         0.00, 0.8,  0.25, 0.2,  0.75, 0.8,  1.00, 0.2,
258         0
259 };
260
261
262 struct deco_struct {
263         double const * data;
264         int angle;
265 };
266
267 struct named_deco_struct {
268         char const * name;
269         double const * data;
270         int angle;
271 };
272
273 named_deco_struct deco_table[] = {
274         // Decorations
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
291         // Delimiters
292         {"(",              parenth,    0 },
293         {")",              parenth,    2 },
294         {"{",              brace,      0 },
295         {"}",              brace,      2 },
296         {"lbrace",         brace,      0 },
297         {"rbrace",         brace,      2 },
298         {"[",              brack,      0 },
299         {"]",              brack,      2 },
300         {"|",              vert,       0 },
301         {"/",              slash,      0 },
302         {"slash",          slash,      0 },
303         {"vert",           vert,       0 },
304         {"Vert",           Vert,       0 },
305         {"'",              slash,      1 },
306         {"<",              angle,      0 },
307         {">",              angle,      2 },
308         {"\\",             slash,      1 },
309         {"backslash",      slash,      1 },
310         {"langle",         angle,      0 },
311         {"lceil",          corner,     0 },
312         {"lfloor",         corner,     1 },
313         {"rangle",         angle,      2 },
314         {"rceil",          corner,     3 },
315         {"rfloor",         corner,     2 },
316         {"downarrow",      arrow,      2 },
317         {"Downarrow",      Arrow,      2 },
318         {"uparrow",        arrow,      0 },
319         {"Uparrow",        Arrow,      0 },
320         {"updownarrow",    udarrow,    0 },
321         {"Updownarrow",    Udarrow,    0 },
322
323         // Accents
324         {"ddot",           ddot,       0 },
325         {"dddot",          dddot,      0 },
326         {"ddddot",         ddddot,     0 },
327         {"hat",            angle,      3 },
328         {"grave",          slash,      1 },
329         {"acute",          slash,      0 },
330         {"tilde",          tilde,      0 },
331         {"bar",            hline,      0 },
332         {"dot",            hlinesmall, 0 },
333         {"check",          angle,      1 },
334         {"breve",          parenth,    1 },
335         {"vec",            arrow,      3 },
336         {"mathring",       ring,       0 },
337
338         // Dots
339         {"dots",           hline3,     0 },
340         {"ldots",          hline3,     0 },
341         {"cdots",          hline3,     0 },
342         {"vdots",          hline3,     1 },
343         {"ddots",          dline3,     0 },
344         {"adots",          dline3,     1 },
345         {"iddots",         dline3,     1 },
346         {"dotsb",          hline3,     0 },
347         {"dotsc",          hline3,     0 },
348         {"dotsi",          hline3,     0 },
349         {"dotsm",          hline3,     0 },
350         {"dotso",          hline3,     0 }
351 };
352
353
354 map<docstring, deco_struct> deco_list;
355
356 // sort the table on startup
357 class init_deco_table {
358 public:
359         init_deco_table() {
360                 unsigned const n = sizeof(deco_table) / sizeof(deco_table[0]);
361                 for (named_deco_struct * p = deco_table; p != deco_table + n; ++p) {
362                         deco_struct d;
363                         d.data  = p->data;
364                         d.angle = p->angle;
365                         deco_list[from_ascii(p->name)] = d;
366                 }
367         }
368 };
369
370 static init_deco_table dummy;
371
372
373 deco_struct const * search_deco(docstring const & name)
374 {
375         map<docstring, deco_struct>::const_iterator p = deco_list.find(name);
376         return p == deco_list.end() ? 0 : &(p->second);
377 }
378
379
380 } // namespace anon
381
382
383 int mathed_char_width(FontInfo const & font, char_type c)
384 {
385         return theFontMetrics(font).width(c);
386 }
387
388
389 int mathed_char_kerning(FontInfo const & font, char_type c)
390 {
391         frontend::FontMetrics const & fm = theFontMetrics(font);
392         return fm.rbearing(c) - fm.width(c);
393 }
394
395
396 void mathed_string_dim(FontInfo const & font,
397                        docstring const & s,
398                        Dimension & dim)
399 {
400         frontend::FontMetrics const & fm = theFontMetrics(font);
401         dim.asc = 0;
402         dim.des = 0;
403         for (docstring::const_iterator it = s.begin();
404              it != s.end();
405              ++it) {
406                 dim.asc = max(dim.asc, fm.ascent(*it));
407                 dim.des = max(dim.des, fm.descent(*it));
408         }
409         dim.wid = fm.width(s);
410 }
411
412
413 int mathed_string_width(FontInfo const & font, docstring const & s)
414 {
415         return theFontMetrics(font).width(s);
416 }
417
418
419 void mathed_draw_deco(PainterInfo & pi, int x, int y, int w, int h,
420         docstring const & name)
421 {
422         if (name == ".") {
423                 pi.pain.line(x + w/2, y, x + w/2, y + h,
424                           Color_cursor, Painter::line_onoffdash);
425                 return;
426         }
427
428         deco_struct const * mds = search_deco(name);
429         if (!mds) {
430                 lyxerr << "Deco was not found. Programming error?" << endl;
431                 lyxerr << "name: '" << to_utf8(name) << "'" << endl;
432                 return;
433         }
434
435         int const n = (w < h) ? w : h;
436         int const r = mds->angle;
437         double const * d = mds->data;
438
439         if (h > 70 && (name == "(" || name == ")"))
440                 d = parenthHigh;
441
442         Matrix mt(r, w, h);
443         Matrix sqmt(r, n, n);
444
445         if (r > 0 && r < 3)
446                 y += h;
447
448         if (r >= 2)
449                 x += w;
450
451         for (int i = 0; d[i]; ) {
452                 int code = int(d[i++]);
453                 if (code & 1) {  // code == 1 || code == 3
454                         double xx = d[i++];
455                         double yy = d[i++];
456                         double x2 = d[i++];
457                         double y2 = d[i++];
458                         if (code == 3)
459                                 sqmt.transform(xx, yy);
460                         else
461                                 mt.transform(xx, yy);
462                         mt.transform(x2, y2);
463                         pi.pain.line(
464                                 int(x + xx + 0.5), int(y + yy + 0.5),
465                                 int(x + x2 + 0.5), int(y + y2 + 0.5),
466                                 pi.base.font.color());
467                 } else {
468                         int xp[32];
469                         int yp[32];
470                         int const n = int(d[i++]);
471                         for (int j = 0; j < n; ++j) {
472                                 double xx = d[i++];
473                                 double yy = d[i++];
474 //           lyxerr << ' ' << xx << ' ' << yy << ' ';
475                                 if (code == 4)
476                                         sqmt.transform(xx, yy);
477                                 else
478                                         mt.transform(xx, yy);
479                                 xp[j] = int(x + xx + 0.5);
480                                 yp[j] = int(y + yy + 0.5);
481                                 //  lyxerr << "P[" << j ' ' << xx << ' ' << yy << ' ' << x << ' ' << y << ']';
482                         }
483                         pi.pain.lines(xp, yp, n, pi.base.font.color());
484                 }
485         }
486 }
487
488
489 void drawStrRed(PainterInfo & pi, int x, int y, docstring const & str)
490 {
491         FontInfo f = pi.base.font;
492         f.setColor(Color_latex);
493         pi.pain.text(x, y, str, f);
494 }
495
496
497 void drawStrBlack(PainterInfo & pi, int x, int y, docstring const & str)
498 {
499         FontInfo f = pi.base.font;
500         f.setColor(Color_foreground);
501         pi.pain.text(x, y, str, f);
502 }
503
504
505 void math_font_max_dim(FontInfo const & font, int & asc, int & des)
506 {
507         frontend::FontMetrics const & fm = theFontMetrics(font);
508         asc = fm.maxAscent();
509         des = fm.maxDescent();
510 }
511
512
513 struct fontinfo {
514         string cmd_;
515         FontFamily family_;
516         FontSeries series_;
517         FontShape  shape_;
518         ColorCode        color_;
519 };
520
521
522 FontFamily const inh_family = INHERIT_FAMILY;
523 FontSeries const inh_series = INHERIT_SERIES;
524 FontShape  const inh_shape  = INHERIT_SHAPE;
525
526
527 // mathnormal should be the first, otherwise the fallback further down
528 // does not work
529 fontinfo fontinfos[] = {
530         // math fonts
531         {"mathnormal",    ROMAN_FAMILY, MEDIUM_SERIES,
532                           ITALIC_SHAPE, Color_math},
533         {"mathbf",        inh_family, BOLD_SERIES,
534                           inh_shape, Color_math},
535         {"mathcal",       CMSY_FAMILY, inh_series,
536                           inh_shape, Color_math},
537         {"mathfrak",      EUFRAK_FAMILY, inh_series,
538                           inh_shape, Color_math},
539         {"mathrm",        ROMAN_FAMILY, inh_series,
540                           UP_SHAPE, Color_math},
541         {"mathsf",        SANS_FAMILY, inh_series,
542                           inh_shape, Color_math},
543         {"mathbb",        MSB_FAMILY, inh_series,
544                           inh_shape, Color_math},
545         {"mathtt",        TYPEWRITER_FAMILY, inh_series,
546                           inh_shape, Color_math},
547         {"mathit",        inh_family, inh_series,
548                           ITALIC_SHAPE, Color_math},
549         {"mathscr",       RSFS_FAMILY, inh_series,
550                   inh_shape, Color_math}, 
551         {"cmex",          CMEX_FAMILY, inh_series,
552                           inh_shape, Color_math},
553         {"cmm",           CMM_FAMILY, inh_series,
554                           inh_shape, Color_math},
555         {"cmr",           CMR_FAMILY, inh_series,
556                           inh_shape, Color_math},
557         {"cmsy",          CMSY_FAMILY, inh_series,
558                           inh_shape, Color_math},
559         {"eufrak",        EUFRAK_FAMILY, inh_series,
560                           inh_shape, Color_math},
561         {"msa",           MSA_FAMILY, inh_series,
562                           inh_shape, Color_math},
563         {"msb",           MSB_FAMILY, inh_series,
564                           inh_shape, Color_math},
565         {"wasy",          WASY_FAMILY, inh_series,
566                           inh_shape, Color_math},
567         {"esint",         ESINT_FAMILY, inh_series,
568                           inh_shape, Color_math},
569
570         // Text fonts
571         {"text",          inh_family, inh_series,
572                           inh_shape, Color_foreground},
573         {"textbf",        inh_family, BOLD_SERIES,
574                           inh_shape, Color_foreground},
575         {"textit",        inh_family, inh_series,
576                           ITALIC_SHAPE, Color_foreground},
577         {"textmd",        inh_family, MEDIUM_SERIES,
578                           inh_shape, Color_foreground},
579         {"textnormal",    inh_family, inh_series,
580                           UP_SHAPE, Color_foreground},
581         {"textrm",        ROMAN_FAMILY,
582                           inh_series, UP_SHAPE,Color_foreground},
583         {"textsc",        inh_family, inh_series,
584                           SMALLCAPS_SHAPE, Color_foreground},
585         {"textsf",        SANS_FAMILY, inh_series,
586                           inh_shape, Color_foreground},
587         {"textsl",        inh_family, inh_series,
588                           SLANTED_SHAPE, Color_foreground},
589         {"texttt",        TYPEWRITER_FAMILY, inh_series,
590                           inh_shape, Color_foreground},
591         {"textup",        inh_family, inh_series,
592                           UP_SHAPE, Color_foreground},
593
594         // TIPA support
595         {"textipa",       inh_family, inh_series,
596                           inh_shape, Color_foreground},
597
598         // mhchem support
599         {"ce",            inh_family, inh_series,
600                           inh_shape, Color_foreground},
601         {"cf",            inh_family, inh_series,
602                           inh_shape, Color_foreground},
603
604         // LyX internal usage
605         {"lyxtex",        inh_family, inh_series,
606                           UP_SHAPE, Color_latex},
607         {"lyxsymbol",     SYMBOL_FAMILY, inh_series,
608                           inh_shape, Color_math},
609         {"lyxboldsymbol", SYMBOL_FAMILY, BOLD_SERIES,
610                           inh_shape, Color_math},
611         {"lyxblacktext",  ROMAN_FAMILY, MEDIUM_SERIES,
612                           UP_SHAPE, Color_foreground},
613         {"lyxnochange",   inh_family, inh_series,
614                           inh_shape, Color_foreground},
615         {"lyxfakebb",     TYPEWRITER_FAMILY, BOLD_SERIES,
616                           UP_SHAPE, Color_math},
617         {"lyxfakecal",    SANS_FAMILY, MEDIUM_SERIES,
618                           ITALIC_SHAPE, Color_math},
619         {"lyxfakefrak",   ROMAN_FAMILY, BOLD_SERIES,
620                           ITALIC_SHAPE, Color_math}
621 };
622
623
624 fontinfo * lookupFont(docstring const & name0)
625 {
626         //lyxerr << "searching font '" << name << "'" << endl;
627         int const n = sizeof(fontinfos) / sizeof(fontinfo);
628         string name = to_utf8(name0);
629         for (int i = 0; i < n; ++i)
630                 if (fontinfos[i].cmd_ == name) {
631                         //lyxerr << "found '" << i << "'" << endl;
632                         return fontinfos + i;
633                 }
634         return 0;
635 }
636
637
638 fontinfo * searchFont(docstring const & name)
639 {
640         fontinfo * f = lookupFont(name);
641         return f ? f : fontinfos;
642         // this should be mathnormal
643         //return searchFont("mathnormal");
644 }
645
646
647 bool isFontName(docstring const & name)
648 {
649         return lookupFont(name);
650 }
651
652
653 bool isMathFont(docstring const & name)
654 {
655         fontinfo * f = lookupFont(name);
656         return f && f->color_ == Color_math;
657 }
658
659
660 bool isTextFont(docstring const & name)
661 {
662         fontinfo * f = lookupFont(name);
663         return f && f->color_ == Color_foreground;
664 }
665
666
667 FontInfo getFont(docstring const & name)
668 {
669         FontInfo font;
670         augmentFont(font, name);
671         return font;
672 }
673
674
675 void fakeFont(docstring const & orig, docstring const & fake)
676 {
677         fontinfo * forig = searchFont(orig);
678         fontinfo * ffake = searchFont(fake);
679         if (forig && ffake) {
680                 forig->family_ = ffake->family_;
681                 forig->series_ = ffake->series_;
682                 forig->shape_  = ffake->shape_;
683                 forig->color_  = ffake->color_;
684         } else {
685                 lyxerr << "Can't fake font '" << to_utf8(orig) << "' with '"
686                        << to_utf8(fake) << "'" << endl;
687         }
688 }
689
690
691 void augmentFont(FontInfo & font, docstring const & name)
692 {
693         static bool initialized = false;
694         if (!initialized) {
695                 initialized = true;
696                 // fake fonts if necessary
697                 if (!theFontLoader().available(getFont(from_ascii("mathfrak"))))
698                         fakeFont(from_ascii("mathfrak"), from_ascii("lyxfakefrak"));
699                 if (!theFontLoader().available(getFont(from_ascii("mathcal"))))
700                         fakeFont(from_ascii("mathcal"), from_ascii("lyxfakecal"));
701         }
702         fontinfo * info = searchFont(name);
703         if (info->family_ != inh_family)
704                 font.setFamily(info->family_);
705         if (info->series_ != inh_series)
706                 font.setSeries(info->series_);
707         if (info->shape_ != inh_shape)
708                 font.setShape(info->shape_);
709         if (info->color_ != Color_none)
710                 font.setColor(info->color_);
711 }
712
713
714 docstring asString(MathData const & ar)
715 {
716         odocstringstream os;
717         WriteStream ws(os);
718         ws << ar;
719         return os.str();
720 }
721
722
723 void asArray(docstring const & str, MathData & ar, Parse::flags pf)
724 {
725         bool quiet = pf & Parse::QUIET;
726         if ((str.size() == 1 && quiet) || (!mathed_parse_cell(ar, str, pf) && quiet))
727                 mathed_parse_cell(ar, str, pf | Parse::VERBATIM);
728 }
729
730
731 docstring asString(InsetMath const & inset)
732 {
733         odocstringstream os;
734         WriteStream ws(os);
735         inset.write(ws);
736         return os.str();
737 }
738
739
740 docstring asString(MathAtom const & at)
741 {
742         odocstringstream os;
743         WriteStream ws(os);
744         at->write(ws);
745         return os.str();
746 }
747
748
749 } // namespace lyx