191//
192// The MathML specification says:
193// (http://www.w3.org/TR/MathML/chapter2.html#fund.units)
194//
195// "Most presentation elements have attributes that accept values representing
196// lengths to be used for size, spacing or similar properties. The syntax of a
197// length is specified as
198//
199// number | number unit | namedspace
200//
201// There should be no space between the number and the unit of a length."
202//
203// "A trailing '%' represents a percent of the default value. The default
204// value, or how it is obtained, is listed in the table of attributes for each
205// element. [...] A number without a unit is intepreted as a multiple of the
206// default value."
207//
208// "The possible units in MathML are:
209//
210// Unit Description
211// em an em (font-relative unit traditionally used for horizontal lengths)
212// ex an ex (font-relative unit traditionally used for vertical lengths)
213// px pixels, or size of a pixel in the current display
214// in inches (1 inch = 2.54 centimeters)
215// cm centimeters
216// mm millimeters
217// pt points (1 point = 1/72 inch)
218// pc picas (1 pica = 12 points)
219// % percentage of default value"
220//
221// The numbers are defined that way:
222// - unsigned-number: "a string of decimal digits with up to one decimal point
223// (U+002E), representing a non-negative terminating decimal number (a type of
224// rational number)"
225// - number: "an optional prefix of '-' (U+002D), followed by an unsigned
226// number, representing a terminating decimal number (a type of rational
227// number)"
228//
229bool RenderMathMLBlock::ParseLength(const String& string, float& lengthValue, bool allowNegative)
230{
231 String s = string.simplifyWhiteSpace();
232
233 int stringLength = s.length();
234 if (!stringLength)
235 return false;
236
237 if (ParseNamedSpace(s, lengthValue, allowNegative))
238 return true;
239
240 StringBuilder number;
241 String unit;
242
243 // see if the negative sign is there
244 int i = 0;
245 UChar c = s[0];
246 if (c == '-') {
247 number.append(c);
248 i++;
249 }
250
251 // Gather up characters that make up the number
252 bool gotDot = false;
253 for ( ; i < stringLength; i++) {
254 c = s[i];
255 if (gotDot && c == '.')
256 return false; // two dots encountered
257 if (c == '.') {
258 gotDot = true;
259 } else if (!isASCIIDigit(c)) {
260 unit = s.substring(i, stringLength - i);
261 // some authors leave blanks before the unit, but that shouldn't
262 // be allowed, so don't simplifyWhitespace on 'unit'.
263 break;
264 }
265 number.append(c);
266 }
267
268 // Convert number to floating point
269 bool ok;
270 float floatValue = number.toString().toFloat(&ok);
271 if (!ok)
272 return false;
273 if (floatValue < 0 && !allowNegative)
274 return false;
275
276 if (unit.isEmpty()) {
277 // no explicit unit, this is a number that will act as a multiplier
278 lengthValue *= floatValue;
279 return true;
280 }
281 if (unit == "%") {
282 lengthValue *= floatValue / 100.0;
283 return true;
284 }
285 if (unit == "em") {
286 lengthValue = floatValue * style()->font().size();
287 return true;
288 }
289 if (unit == "ex") {
290 lengthValue = floatValue * style()->fontMetrics().xHeight();
291 return true;
292 }
293 if (unit == "px") {
294 lengthValue = floatValue;
295 return true;
296 }
297 if (unit == "pt") {
298 lengthValue = 4. / 3. * floatValue;
299 return true;
300 }
301 if (unit == "pc") {
302 lengthValue = (4. / 3. * floatValue) * 12.;
303 return true;
304 }
305 if (unit == "in") {
306 lengthValue = 96. * floatValue;
307 return true;
308 }
309 if (unit == "cm") {
310 lengthValue = 96. * floatValue / 2.54;
311 return true;
312 }
313 if (unit == "mm") {
314 lengthValue = (96. * floatValue / 2.54) / 10.;
315 return true;
316 }
317
318 // unexpected unit
319 return false;
320}
321
322bool RenderMathMLBlock::ParseNamedSpace(const String& string, float& lengthValue, bool allowNegative)
323{
324 float length = 0.;
325 // See if it is one of the namedspaces (ranging -7/18em, -6/18, ... 7/18em)
326 if (string == "veryverythinmathspace")
327 length = 1.;
328 else if (string == "verythinmathspace")
329 length = 2.;
330 else if (string == "thinmathspace")
331 length = 3.;
332 else if (string == "mediummathspace")
333 length = 4.;
334 else if (string == "thickmathspace")
335 length = 5.;
336 else if (string == "verythickmathspace")
337 length = 6.;
338 else if (string == "veryverythickmathspace")
339 length = 7.;
340 else if (allowNegative) {
341 if (string == "negativeveryverythinmathspace")
342 length = -1.;
343 else if (string == "negativeverythinmathspace")
344 length = -2.;
345 else if (string == "negativethinmathspace")
346 length = -3.;
347 else if (string == "negativemediummathspace")
348 length = -4.;
349 else if (string == "negativethickmathspace")
350 length = -5.;
351 else if (string == "negativeverythickmathspace")
352 length = -6.;
353 else if (string == "negativeveryverythickmathspace")
354 length = -7.;
355 }
356 if (length) {
357 lengthValue = length * style()->font().size() / 18.;
358 return true;
359 }
360 return false;
361}
362