]> git.lyx.org Git - lyx.git/blob - src/mathed/InsetMathChar.cpp
mathedSymbolDim only needs a MathBase
[lyx.git] / src / mathed / InsetMathChar.cpp
1 /**
2  * \file InsetMathChar.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 "InsetMathChar.h"
15
16 #include "MathParser.h"
17 #include "MathSupport.h"
18 #include "MathStream.h"
19 #include "MetricsInfo.h"
20
21 #include "Dimension.h"
22 #include "BufferEncodings.h"
23 #include "LaTeXFeatures.h"
24 #include "TextPainter.h"
25
26 #include "frontends/FontMetrics.h"
27
28 #include "support/debug.h"
29 #include "support/lstrings.h"
30 #include "support/textutils.h"
31
32
33 namespace lyx {
34
35 extern bool has_math_fonts;
36
37
38 namespace {
39 latexkeys const * makeSubstitute(char_type c)
40 {
41         std::string name;
42         switch (c) {
43         // Latex replaces ', *, -, and : with specific symbols. With unicode-math,
44         // these symbols are replaced respectively by ^U+2032, U+2217, U+2212 and
45         // U+2236 (the latter substitution can be turned off with a package
46         // option). Unicode-math also replaces ` with \backprime.
47                 // prime needs to be placed in superscript unless an opentype font is used.
48                 //case '\'':
49                 //name = "prime";
50                 //break;
51         case '*':
52                 name = "ast";
53                 break;
54         case '-':
55                 name = "lyxminus";// unicode-math: "minus"
56                 break;
57         case ':':
58                 name = "ordinarycolon";// unicode-math: "mathratio"
59                 break;
60         // The remaining replacements are not real character substitutions (from a
61         // unicode point of view) but are done here: 1. for cosmetic reasons, in the
62         // context of being stuck with CM fonts at the moment, to ensure consistency
63         // with related symbols: -, \leq, \geq, etc.  2. to get the proper spacing
64         // as defined in lib/symbols.
65         case '+':
66                 name = "lyxplus";//unicode-math: "mathplus"
67                 break;
68         case '>':
69                 name = "lyxgt";//unicode-math: "greater"
70                 break;
71         case '<':
72                 name = "lyxlt";//unicode-math: "less"
73                 break;
74         case '=':
75                 name = "lyxeqrel";//unicode-math: "equal"
76                 break;
77         //case ','://unicode-math: "mathcomma"
78         //case ';'://unicode-math: "mathsemicolon"
79         default:
80                 return nullptr;
81         }
82         return in_word_set(from_ascii(name));
83 }
84
85 } //anonymous namespace
86
87
88 static bool slanted(char_type c)
89 {
90         return isAlphaASCII(c) || Encodings::isMathAlpha(c);
91 }
92
93
94 InsetMathChar::InsetMathChar(char_type c)
95         : char_(c), kerning_(0), subst_(makeSubstitute(c))
96 {}
97
98
99
100 Inset * InsetMathChar::clone() const
101 {
102         return new InsetMathChar(*this);
103 }
104
105
106 void InsetMathChar::metrics(MetricsInfo & mi, Dimension & dim) const
107 {
108         bool const mathfont = isMathFont(mi.base.fontname);
109         if (mathfont && subst_) {
110                 // If the char has a substitute, draw the replacement symbol
111                 // instead, but only in math mode.
112                 mathedSymbolDim(mi.base, dim, subst_);
113                 kerning_ = mathed_char_kerning(mi.base.font, *subst_->draw.rbegin());
114                 return;
115         } else if (!slanted(char_) && mi.base.fontname == "mathnormal") {
116                 Changer dummy = mi.base.font.changeShape(UP_SHAPE);
117                 dim = theFontMetrics(mi.base.font).dimension(char_);
118         } else {
119                 frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
120                 dim = fm.dimension(char_);
121                 kerning_ = fm.rbearing(char_) - dim.wid;
122         }
123 }
124
125
126 void InsetMathChar::draw(PainterInfo & pi, int x, int y) const
127 {
128         //lyxerr << "drawing '" << char_ << "' font: " << pi.base.fontname << std::endl;
129         if (isMathFont(pi.base.fontname)) {
130                 if (subst_) {
131                         // If the char has a substitute, draw the replacement symbol
132                         // instead, but only in math mode.
133                         mathedSymbolDraw(pi, x, y, subst_);
134                         return;
135                 } else if (!slanted(char_) && pi.base.fontname == "mathnormal") {
136                         Changer dummy = pi.base.font.changeShape(UP_SHAPE);
137                         pi.draw(x, y, char_);
138                         return;
139                 }
140         }
141         pi.draw(x, y, char_);
142 }
143
144
145 void InsetMathChar::metricsT(TextMetricsInfo const &, Dimension & dim) const
146 {
147         dim.wid = 1;
148         dim.asc = 1;
149         dim.des = 0;
150 }
151
152
153 void InsetMathChar::drawT(TextPainter & pain, int x, int y) const
154 {
155         //lyxerr << "drawing text '" << char_ << "' code: " << code_ << endl;
156         pain.draw(x, y, char_);
157 }
158
159
160 void InsetMathChar::write(WriteStream & os) const
161 {
162         os.os().put(char_);
163 }
164
165
166 void InsetMathChar::validate(LaTeXFeatures & features) const
167 {
168         if (!isASCII(char_))
169                 BufferEncodings::validate(char_, features, true);
170 }
171
172
173 void InsetMathChar::normalize(NormalStream & os) const
174 {
175         os << "[char ";
176         os.os().put(char_);
177         os << " mathalpha]";
178 }
179
180
181 void InsetMathChar::octave(OctaveStream & os) const
182 {
183         os.os().put(char_);
184 }
185
186
187 // We have a bit of a problem here. MathML wants to know whether the
188 // character represents an "identifier" or an "operator", and we have
189 // no general way of telling. So we shall guess: If it's alpha or 
190 // mathalpha, then we'll treat it as an identifier, otherwise as an 
191 // operator.
192 // Worst case: We get bad spacing, or bad italics.
193 void InsetMathChar::mathmlize(MathStream & ms) const
194 {
195         std::string entity;
196         switch (char_) {
197                 case '<': entity = "&lt;"; break;
198                 case '>': entity = "&gt;"; break;
199                 case '&': entity = "&amp;"; break;
200                 case ' ': {
201                         ms << from_ascii("&nbsp;");
202                         return;
203                 }
204                 default: break;
205         }
206         
207         if (ms.inText()) {
208                 if (entity.empty())
209                         ms.os().put(char_);
210                 else 
211                         ms << from_ascii(entity);
212                 return;
213         }
214
215         if (!entity.empty()) {
216                 ms << "<mo>" << from_ascii(entity) << "</mo>";
217                 return;
218         }               
219
220         char const * type = 
221                 (isAlphaASCII(char_) || Encodings::isMathAlpha(char_))
222                         ? "mi" : "mo";
223         // we don't use MTag and ETag because we do not want the spacing
224         ms << "<" << type << ">" << char_type(char_) << "</" << type << ">";    
225 }
226
227
228 void InsetMathChar::htmlize(HtmlStream & ms) const
229 {
230         std::string entity;
231         // Not taking subst_ into account here because the MathML output of
232         // <>=+-* looks correct as it is. FIXME: ' is not output as ^\prime
233         switch (char_) {
234                 case '<': entity = "&lt;"; break;
235                 case '>': entity = "&gt;"; break;
236                 case '&': entity = "&amp;"; break;
237                 case ' ': entity = "&nbsp;"; break;
238                 default: break;
239         }
240         
241         bool have_entity = !entity.empty();
242         
243         if (ms.inText()) {
244                 if (have_entity)
245                         ms << from_ascii(entity);
246                 else
247                         ms.os().put(char_);
248                 return;
249         }
250         
251         if (have_entity) {
252                 // an operator, so give some space
253                 ms << ' ' << from_ascii(entity) << ' ';
254                 return;
255         }               
256
257         if (isAlphaASCII(char_) || Encodings::isMathAlpha(char_))
258                 // we don't use MTag and ETag because we do not want the spacing
259                 ms << MTag("i") << char_type(char_) << ETag("i");
260         else
261                 // an operator, so give some space
262                 ms << " " << char_type(char_) << " ";
263 }
264
265
266 MathClass InsetMathChar::mathClass() const
267 {
268         // this information comes from fontmath.ltx in LaTeX source.
269         char const ch = static_cast<char>(char_);
270         if (subst_)
271                 return string_to_class(subst_->extra);
272         else if (support::contains(",;", ch))
273                 return MC_PUNCT;
274         else if (support::contains("([", ch))
275                 return MC_OPEN;
276         else if (support::contains(")]!?", ch))
277                 return MC_CLOSE;
278         else return MC_ORD;
279 }
280
281
282 } // namespace lyx