]> git.lyx.org Git - features.git/blob - src/mathed/MathSupport.cpp
Partial fox for bug #6622: Display \utilde on screen correctly.
[features.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         {"utilde",              tilde,    0 },
291
292         // Delimiters
293         {"(",              parenth,    0 },
294         {")",              parenth,    2 },
295         {"{",              brace,      0 },
296         {"}",              brace,      2 },
297         {"lbrace",         brace,      0 },
298         {"rbrace",         brace,      2 },
299         {"[",              brack,      0 },
300         {"]",              brack,      2 },
301         {"|",              vert,       0 },
302         {"/",              slash,      0 },
303         {"slash",          slash,      0 },
304         {"vert",           vert,       0 },
305         {"Vert",           Vert,       0 },
306         {"'",              slash,      1 },
307         {"<",              angle,      0 },
308         {">",              angle,      2 },
309         {"\\",             slash,      1 },
310         {"backslash",      slash,      1 },
311         {"langle",         angle,      0 },
312         {"lceil",          corner,     0 },
313         {"lfloor",         corner,     1 },
314         {"rangle",         angle,      2 },
315         {"rceil",          corner,     3 },
316         {"rfloor",         corner,     2 },
317         {"downarrow",      arrow,      2 },
318         {"Downarrow",      Arrow,      2 },
319         {"uparrow",        arrow,      0 },
320         {"Uparrow",        Arrow,      0 },
321         {"updownarrow",    udarrow,    0 },
322         {"Updownarrow",    Udarrow,    0 },
323
324         // Accents
325         {"ddot",           ddot,       0 },
326         {"dddot",          dddot,      0 },
327         {"ddddot",         ddddot,     0 },
328         {"hat",            angle,      3 },
329         {"grave",          slash,      1 },
330         {"acute",          slash,      0 },
331         {"tilde",          tilde,      0 },
332         {"bar",            hline,      0 },
333         {"dot",            hlinesmall, 0 },
334         {"check",          angle,      1 },
335         {"breve",          parenth,    1 },
336         {"vec",            arrow,      3 },
337         {"mathring",       ring,       0 },
338
339         // Dots
340         {"dots",           hline3,     0 },
341         {"ldots",          hline3,     0 },
342         {"cdots",          hline3,     0 },
343         {"vdots",          hline3,     1 },
344         {"ddots",          dline3,     0 },
345         {"adots",          dline3,     1 },
346         {"iddots",         dline3,     1 },
347         {"dotsb",          hline3,     0 },
348         {"dotsc",          hline3,     0 },
349         {"dotsi",          hline3,     0 },
350         {"dotsm",          hline3,     0 },
351         {"dotso",          hline3,     0 }
352 };
353
354
355 map<docstring, deco_struct> deco_list;
356
357 // sort the table on startup
358 class init_deco_table {
359 public:
360         init_deco_table() {
361                 unsigned const n = sizeof(deco_table) / sizeof(deco_table[0]);
362                 for (named_deco_struct * p = deco_table; p != deco_table + n; ++p) {
363                         deco_struct d;
364                         d.data  = p->data;
365                         d.angle = p->angle;
366                         deco_list[from_ascii(p->name)] = d;
367                 }
368         }
369 };
370
371 static init_deco_table dummy;
372
373
374 deco_struct const * search_deco(docstring const & name)
375 {
376         map<docstring, deco_struct>::const_iterator p = deco_list.find(name);
377         return p == deco_list.end() ? 0 : &(p->second);
378 }
379
380
381 } // namespace anon
382
383
384 int mathed_char_width(FontInfo const & font, char_type c)
385 {
386         return theFontMetrics(font).width(c);
387 }
388
389
390 int mathed_char_kerning(FontInfo const & font, char_type c)
391 {
392         frontend::FontMetrics const & fm = theFontMetrics(font);
393         return fm.rbearing(c) - fm.width(c);
394 }
395
396
397 void mathed_string_dim(FontInfo const & font,
398                        docstring const & s,
399                        Dimension & dim)
400 {
401         frontend::FontMetrics const & fm = theFontMetrics(font);
402         dim.asc = 0;
403         dim.des = 0;
404         for (docstring::const_iterator it = s.begin();
405              it != s.end();
406              ++it) {
407                 dim.asc = max(dim.asc, fm.ascent(*it));
408                 dim.des = max(dim.des, fm.descent(*it));
409         }
410         dim.wid = fm.width(s);
411 }
412
413
414 int mathed_string_width(FontInfo const & font, docstring const & s)
415 {
416         return theFontMetrics(font).width(s);
417 }
418
419
420 void mathed_draw_deco(PainterInfo & pi, int x, int y, int w, int h,
421         docstring const & name)
422 {
423         if (name == ".") {
424                 pi.pain.line(x + w/2, y, x + w/2, y + h,
425                           Color_cursor, Painter::line_onoffdash);
426                 return;
427         }
428
429         deco_struct const * mds = search_deco(name);
430         if (!mds) {
431                 lyxerr << "Deco was not found. Programming error?" << endl;
432                 lyxerr << "name: '" << to_utf8(name) << "'" << endl;
433                 return;
434         }
435
436         int const n = (w < h) ? w : h;
437         int const r = mds->angle;
438         double const * d = mds->data;
439
440         if (h > 70 && (name == "(" || name == ")"))
441                 d = parenthHigh;
442
443         Matrix mt(r, w, h);
444         Matrix sqmt(r, n, n);
445
446         if (r > 0 && r < 3)
447                 y += h;
448
449         if (r >= 2)
450                 x += w;
451
452         for (int i = 0; d[i]; ) {
453                 int code = int(d[i++]);
454                 if (code & 1) {  // code == 1 || code == 3
455                         double xx = d[i++];
456                         double yy = d[i++];
457                         double x2 = d[i++];
458                         double y2 = d[i++];
459                         if (code == 3)
460                                 sqmt.transform(xx, yy);
461                         else
462                                 mt.transform(xx, yy);
463                         mt.transform(x2, y2);
464                         pi.pain.line(
465                                 int(x + xx + 0.5), int(y + yy + 0.5),
466                                 int(x + x2 + 0.5), int(y + y2 + 0.5),
467                                 pi.base.font.color());
468                 } else {
469                         int xp[32];
470                         int yp[32];
471                         int const n = int(d[i++]);
472                         for (int j = 0; j < n; ++j) {
473                                 double xx = d[i++];
474                                 double yy = d[i++];
475 //           lyxerr << ' ' << xx << ' ' << yy << ' ';
476                                 if (code == 4)
477                                         sqmt.transform(xx, yy);
478                                 else
479                                         mt.transform(xx, yy);
480                                 xp[j] = int(x + xx + 0.5);
481                                 yp[j] = int(y + yy + 0.5);
482                                 //  lyxerr << "P[" << j ' ' << xx << ' ' << yy << ' ' << x << ' ' << y << ']';
483                         }
484                         pi.pain.lines(xp, yp, n, pi.base.font.color());
485                 }
486         }
487 }
488
489
490 void drawStrRed(PainterInfo & pi, int x, int y, docstring const & str)
491 {
492         FontInfo f = pi.base.font;
493         f.setColor(Color_latex);
494         pi.pain.text(x, y, str, f);
495 }
496
497
498 void drawStrBlack(PainterInfo & pi, int x, int y, docstring const & str)
499 {
500         FontInfo f = pi.base.font;
501         f.setColor(Color_foreground);
502         pi.pain.text(x, y, str, f);
503 }
504
505
506 void math_font_max_dim(FontInfo const & font, int & asc, int & des)
507 {
508         frontend::FontMetrics const & fm = theFontMetrics(font);
509         asc = fm.maxAscent();
510         des = fm.maxDescent();
511 }
512
513
514 struct fontinfo {
515         string cmd_;
516         FontFamily family_;
517         FontSeries series_;
518         FontShape  shape_;
519         ColorCode        color_;
520 };
521
522
523 FontFamily const inh_family = INHERIT_FAMILY;
524 FontSeries const inh_series = INHERIT_SERIES;
525 FontShape  const inh_shape  = INHERIT_SHAPE;
526
527
528 // mathnormal should be the first, otherwise the fallback further down
529 // does not work
530 fontinfo fontinfos[] = {
531         // math fonts
532         {"mathnormal",    ROMAN_FAMILY, MEDIUM_SERIES,
533                           ITALIC_SHAPE, Color_math},
534         {"mathbf",        inh_family, BOLD_SERIES,
535                           inh_shape, Color_math},
536         {"mathcal",       CMSY_FAMILY, inh_series,
537                           inh_shape, Color_math},
538         {"mathfrak",      EUFRAK_FAMILY, inh_series,
539                           inh_shape, Color_math},
540         {"mathrm",        ROMAN_FAMILY, inh_series,
541                           UP_SHAPE, Color_math},
542         {"mathsf",        SANS_FAMILY, inh_series,
543                           inh_shape, Color_math},
544         {"mathbb",        MSB_FAMILY, inh_series,
545                           inh_shape, Color_math},
546         {"mathtt",        TYPEWRITER_FAMILY, inh_series,
547                           inh_shape, Color_math},
548         {"mathit",        inh_family, inh_series,
549                           ITALIC_SHAPE, Color_math},
550         {"mathscr",       RSFS_FAMILY, inh_series,
551                   inh_shape, Color_math}, 
552         {"cmex",          CMEX_FAMILY, inh_series,
553                           inh_shape, Color_math},
554         {"cmm",           CMM_FAMILY, inh_series,
555                           inh_shape, Color_math},
556         {"cmr",           CMR_FAMILY, inh_series,
557                           inh_shape, Color_math},
558         {"cmsy",          CMSY_FAMILY, inh_series,
559                           inh_shape, Color_math},
560         {"eufrak",        EUFRAK_FAMILY, inh_series,
561                           inh_shape, Color_math},
562         {"msa",           MSA_FAMILY, inh_series,
563                           inh_shape, Color_math},
564         {"msb",           MSB_FAMILY, inh_series,
565                           inh_shape, Color_math},
566         {"wasy",          WASY_FAMILY, inh_series,
567                           inh_shape, Color_math},
568         {"esint",         ESINT_FAMILY, inh_series,
569                           inh_shape, Color_math},
570
571         // Text fonts
572         {"text",          inh_family, inh_series,
573                           inh_shape, Color_foreground},
574         {"textbf",        inh_family, BOLD_SERIES,
575                           inh_shape, Color_foreground},
576         {"textit",        inh_family, inh_series,
577                           ITALIC_SHAPE, Color_foreground},
578         {"textmd",        inh_family, MEDIUM_SERIES,
579                           inh_shape, Color_foreground},
580         {"textnormal",    inh_family, inh_series,
581                           UP_SHAPE, Color_foreground},
582         {"textrm",        ROMAN_FAMILY,
583                           inh_series, UP_SHAPE,Color_foreground},
584         {"textsc",        inh_family, inh_series,
585                           SMALLCAPS_SHAPE, Color_foreground},
586         {"textsf",        SANS_FAMILY, inh_series,
587                           inh_shape, Color_foreground},
588         {"textsl",        inh_family, inh_series,
589                           SLANTED_SHAPE, Color_foreground},
590         {"texttt",        TYPEWRITER_FAMILY, inh_series,
591                           inh_shape, Color_foreground},
592         {"textup",        inh_family, inh_series,
593                           UP_SHAPE, Color_foreground},
594
595         // TIPA support
596         {"textipa",       inh_family, inh_series,
597                           inh_shape, Color_foreground},
598
599         // mhchem support
600         {"ce",            inh_family, inh_series,
601                           inh_shape, Color_foreground},
602         {"cf",            inh_family, inh_series,
603                           inh_shape, Color_foreground},
604
605         // LyX internal usage
606         {"lyxtex",        inh_family, inh_series,
607                           UP_SHAPE, Color_latex},
608         {"lyxsymbol",     SYMBOL_FAMILY, inh_series,
609                           inh_shape, Color_math},
610         {"lyxboldsymbol", SYMBOL_FAMILY, BOLD_SERIES,
611                           inh_shape, Color_math},
612         {"lyxblacktext",  ROMAN_FAMILY, MEDIUM_SERIES,
613                           UP_SHAPE, Color_foreground},
614         {"lyxnochange",   inh_family, inh_series,
615                           inh_shape, Color_foreground},
616         {"lyxfakebb",     TYPEWRITER_FAMILY, BOLD_SERIES,
617                           UP_SHAPE, Color_math},
618         {"lyxfakecal",    SANS_FAMILY, MEDIUM_SERIES,
619                           ITALIC_SHAPE, Color_math},
620         {"lyxfakefrak",   ROMAN_FAMILY, BOLD_SERIES,
621                           ITALIC_SHAPE, Color_math}
622 };
623
624
625 fontinfo * lookupFont(docstring const & name0)
626 {
627         //lyxerr << "searching font '" << name << "'" << endl;
628         int const n = sizeof(fontinfos) / sizeof(fontinfo);
629         string name = to_utf8(name0);
630         for (int i = 0; i < n; ++i)
631                 if (fontinfos[i].cmd_ == name) {
632                         //lyxerr << "found '" << i << "'" << endl;
633                         return fontinfos + i;
634                 }
635         return 0;
636 }
637
638
639 fontinfo * searchFont(docstring const & name)
640 {
641         fontinfo * f = lookupFont(name);
642         return f ? f : fontinfos;
643         // this should be mathnormal
644         //return searchFont("mathnormal");
645 }
646
647
648 bool isFontName(docstring const & name)
649 {
650         return lookupFont(name);
651 }
652
653
654 bool isMathFont(docstring const & name)
655 {
656         fontinfo * f = lookupFont(name);
657         return f && f->color_ == Color_math;
658 }
659
660
661 bool isTextFont(docstring const & name)
662 {
663         fontinfo * f = lookupFont(name);
664         return f && f->color_ == Color_foreground;
665 }
666
667
668 FontInfo getFont(docstring const & name)
669 {
670         FontInfo font;
671         augmentFont(font, name);
672         return font;
673 }
674
675
676 void fakeFont(docstring const & orig, docstring const & fake)
677 {
678         fontinfo * forig = searchFont(orig);
679         fontinfo * ffake = searchFont(fake);
680         if (forig && ffake) {
681                 forig->family_ = ffake->family_;
682                 forig->series_ = ffake->series_;
683                 forig->shape_  = ffake->shape_;
684                 forig->color_  = ffake->color_;
685         } else {
686                 lyxerr << "Can't fake font '" << to_utf8(orig) << "' with '"
687                        << to_utf8(fake) << "'" << endl;
688         }
689 }
690
691
692 void augmentFont(FontInfo & font, docstring const & name)
693 {
694         static bool initialized = false;
695         if (!initialized) {
696                 initialized = true;
697                 // fake fonts if necessary
698                 if (!theFontLoader().available(getFont(from_ascii("mathfrak"))))
699                         fakeFont(from_ascii("mathfrak"), from_ascii("lyxfakefrak"));
700                 if (!theFontLoader().available(getFont(from_ascii("mathcal"))))
701                         fakeFont(from_ascii("mathcal"), from_ascii("lyxfakecal"));
702         }
703         fontinfo * info = searchFont(name);
704         if (info->family_ != inh_family)
705                 font.setFamily(info->family_);
706         if (info->series_ != inh_series)
707                 font.setSeries(info->series_);
708         if (info->shape_ != inh_shape)
709                 font.setShape(info->shape_);
710         if (info->color_ != Color_none)
711                 font.setColor(info->color_);
712 }
713
714
715 docstring asString(MathData const & ar)
716 {
717         odocstringstream os;
718         WriteStream ws(os);
719         ws << ar;
720         return os.str();
721 }
722
723
724 void asArray(docstring const & str, MathData & ar, Parse::flags pf)
725 {
726         bool quiet = pf & Parse::QUIET;
727         if ((str.size() == 1 && quiet) || (!mathed_parse_cell(ar, str, pf) && quiet))
728                 mathed_parse_cell(ar, str, pf | Parse::VERBATIM);
729 }
730
731
732 docstring asString(InsetMath const & inset)
733 {
734         odocstringstream os;
735         WriteStream ws(os);
736         inset.write(ws);
737         return os.str();
738 }
739
740
741 docstring asString(MathAtom const & at)
742 {
743         odocstringstream os;
744         WriteStream ws(os);
745         at->write(ws);
746         return os.str();
747 }
748
749
750 } // namespace lyx