1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Mozilla MathML Project.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * The University Of Queensland.
19 : * Portions created by the Initial Developer are Copyright (C) 1999
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Roger B. Sidje <rbs@maths.uq.edu.au>
24 : * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
25 : * Frederic Wang <fred.wang@free.fr>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "nsCOMPtr.h"
42 : #include "nsString.h"
43 : #include "nsHashtable.h"
44 : #include "nsTArray.h"
45 :
46 : #include "nsIComponentManager.h"
47 : #include "nsIPersistentProperties2.h"
48 : #include "nsNetUtil.h"
49 : #include "nsCRT.h"
50 :
51 : #include "nsMathMLOperators.h"
52 :
53 : // operator dictionary entry
54 0 : struct OperatorData {
55 0 : OperatorData(void)
56 : : mFlags(0),
57 : mLeadingSpace(0.0f),
58 0 : mTrailingSpace(0.0f)
59 : {
60 0 : }
61 :
62 : // member data
63 : nsString mStr;
64 : nsOperatorFlags mFlags;
65 : float mLeadingSpace; // unit is em
66 : float mTrailingSpace; // unit is em
67 : };
68 :
69 : static PRInt32 gTableRefCount = 0;
70 : static PRUint32 gOperatorCount = 0;
71 : static OperatorData* gOperatorArray = nsnull;
72 : static nsHashtable* gOperatorTable = nsnull;
73 : static bool gInitialized = false;
74 : static nsTArray<nsString>* gInvariantCharArray = nsnull;
75 :
76 : static const PRUnichar kNullCh = PRUnichar('\0');
77 : static const PRUnichar kDashCh = PRUnichar('#');
78 : static const PRUnichar kColonCh = PRUnichar(':');
79 :
80 : static const char* const kMathVariant_name[] = {
81 : "normal",
82 : "bold",
83 : "italic",
84 : "bold-italic",
85 : "sans-serif",
86 : "bold-sans-serif",
87 : "sans-serif-italic",
88 : "sans-serif-bold-italic",
89 : "monospace",
90 : "script",
91 : "bold-script",
92 : "fraktur",
93 : "bold-fraktur",
94 : "double-struck"
95 : };
96 :
97 : static void
98 0 : SetBooleanProperty(OperatorData* aOperatorData,
99 : nsString aName)
100 : {
101 0 : if (aName.IsEmpty())
102 0 : return;
103 :
104 0 : if (aName.EqualsLiteral("stretchy") && (1 == aOperatorData->mStr.Length()))
105 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_STRETCHY;
106 0 : else if (aName.EqualsLiteral("fence"))
107 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_FENCE;
108 0 : else if (aName.EqualsLiteral("accent"))
109 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_ACCENT;
110 0 : else if (aName.EqualsLiteral("largeop"))
111 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_LARGEOP;
112 0 : else if (aName.EqualsLiteral("separator"))
113 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_SEPARATOR;
114 0 : else if (aName.EqualsLiteral("movablelimits"))
115 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_MOVABLELIMITS;
116 0 : else if (aName.EqualsLiteral("symmetric"))
117 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_SYMMETRIC;
118 0 : else if (aName.EqualsLiteral("integral"))
119 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_INTEGRAL;
120 0 : else if (aName.EqualsLiteral("mirrorable"))
121 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_MIRRORABLE;
122 : }
123 :
124 : static void
125 0 : SetProperty(OperatorData* aOperatorData,
126 : nsString aName,
127 : nsString aValue)
128 : {
129 0 : if (aName.IsEmpty() || aValue.IsEmpty())
130 0 : return;
131 :
132 : // XXX These ones are not kept in the dictionary
133 : // Support for these requires nsString member variables
134 : // maxsize (default: infinity)
135 : // minsize (default: 1)
136 :
137 0 : if (aName.EqualsLiteral("direction")) {
138 0 : if (aValue.EqualsLiteral("vertical"))
139 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_DIRECTION_VERTICAL;
140 0 : else if (aValue.EqualsLiteral("horizontal"))
141 0 : aOperatorData->mFlags |= NS_MATHML_OPERATOR_DIRECTION_HORIZONTAL;
142 0 : else return; // invalid value
143 : } else {
144 : bool isLeadingSpace;
145 0 : if (aName.EqualsLiteral("lspace"))
146 0 : isLeadingSpace = true;
147 0 : else if (aName.EqualsLiteral("rspace"))
148 0 : isLeadingSpace = false;
149 0 : else return; // input is not applicable
150 :
151 : // aValue is assumed to be a digit from 0 to 7
152 0 : PRInt32 error = 0;
153 0 : float space = aValue.ToFloat(&error) / 18.0;
154 0 : if (error) return;
155 :
156 0 : if (isLeadingSpace)
157 0 : aOperatorData->mLeadingSpace = space;
158 : else
159 0 : aOperatorData->mTrailingSpace = space;
160 : }
161 : }
162 :
163 : static bool
164 0 : SetOperator(OperatorData* aOperatorData,
165 : nsOperatorFlags aForm,
166 : const nsCString& aOperator,
167 : nsString& aAttributes)
168 :
169 : {
170 : // aOperator is in the expanded format \uNNNN\uNNNN ...
171 : // First compress these Unicode points to the internal nsString format
172 0 : PRInt32 i = 0;
173 0 : nsAutoString name, value;
174 0 : PRInt32 len = aOperator.Length();
175 0 : PRUnichar c = aOperator[i++];
176 0 : PRUint32 state = 0;
177 0 : PRUnichar uchar = 0;
178 0 : while (i <= len) {
179 0 : if (0 == state) {
180 0 : if (c != '\\')
181 0 : return false;
182 0 : if (i < len)
183 0 : c = aOperator[i];
184 0 : i++;
185 0 : if (('u' != c) && ('U' != c))
186 0 : return false;
187 0 : if (i < len)
188 0 : c = aOperator[i];
189 0 : i++;
190 0 : state++;
191 : }
192 : else {
193 0 : if (('0' <= c) && (c <= '9'))
194 0 : uchar = (uchar << 4) | (c - '0');
195 0 : else if (('a' <= c) && (c <= 'f'))
196 0 : uchar = (uchar << 4) | (c - 'a' + 0x0a);
197 0 : else if (('A' <= c) && (c <= 'F'))
198 0 : uchar = (uchar << 4) | (c - 'A' + 0x0a);
199 0 : else return false;
200 0 : if (i < len)
201 0 : c = aOperator[i];
202 0 : i++;
203 0 : state++;
204 0 : if (5 == state) {
205 0 : value.Append(uchar);
206 0 : uchar = 0;
207 0 : state = 0;
208 : }
209 : }
210 : }
211 0 : if (0 != state) return false;
212 :
213 : // Quick return when the caller doesn't care about the attributes and just wants
214 : // to know if this is a valid operator (this is the case at the first pass of the
215 : // parsing of the dictionary in InitOperators())
216 0 : if (!aForm) return true;
217 :
218 : // Add operator to hash table
219 0 : aOperatorData->mFlags |= aForm;
220 0 : aOperatorData->mStr.Assign(value);
221 0 : value.AppendInt(aForm, 10);
222 0 : nsStringKey key(value);
223 0 : gOperatorTable->Put(&key, aOperatorData);
224 :
225 : #ifdef NS_DEBUG
226 0 : NS_LossyConvertUTF16toASCII str(aAttributes);
227 : #endif
228 : // Loop over the space-delimited list of attributes to get the name:value pairs
229 0 : aAttributes.Append(kNullCh); // put an extra null at the end
230 0 : PRUnichar* start = aAttributes.BeginWriting();
231 0 : PRUnichar* end = start;
232 0 : while ((kNullCh != *start) && (kDashCh != *start)) {
233 0 : name.SetLength(0);
234 0 : value.SetLength(0);
235 : // skip leading space, the dash amounts to the end of the line
236 0 : while ((kNullCh!=*start) && (kDashCh!=*start) && nsCRT::IsAsciiSpace(*start)) {
237 0 : ++start;
238 : }
239 0 : end = start;
240 : // look for ':'
241 0 : while ((kNullCh!=*end) && (kDashCh!=*end) && !nsCRT::IsAsciiSpace(*end) &&
242 : (kColonCh!=*end)) {
243 0 : ++end;
244 : }
245 : // If ':' is not found, then it's a boolean property
246 0 : bool IsBooleanProperty = (kColonCh != *end);
247 0 : *end = kNullCh; // end segment here
248 : // this segment is the name
249 0 : if (start < end) {
250 0 : name.Assign(start);
251 : }
252 0 : if (IsBooleanProperty) {
253 0 : SetBooleanProperty(aOperatorData, name);
254 : } else {
255 0 : start = ++end;
256 : // look for space or end of line
257 0 : while ((kNullCh!=*end) && (kDashCh!=*end) &&
258 0 : !nsCRT::IsAsciiSpace(*end)) {
259 0 : ++end;
260 : }
261 0 : *end = kNullCh; // end segment here
262 0 : if (start < end) {
263 : // this segment is the value
264 0 : value.Assign(start);
265 : }
266 0 : SetProperty(aOperatorData, name, value);
267 : }
268 0 : start = ++end;
269 : }
270 0 : return true;
271 : }
272 :
273 : static nsresult
274 0 : InitOperators(void)
275 : {
276 : // Load the property file containing the Operator Dictionary
277 : nsresult rv;
278 0 : nsCOMPtr<nsIPersistentProperties> mathfontProp;
279 0 : rv = NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(mathfontProp),
280 0 : NS_LITERAL_CSTRING("resource://gre/res/fonts/mathfont.properties"));
281 0 : if (NS_FAILED(rv)) return rv;
282 :
283 : // Get the list of invariant chars
284 0 : for (PRInt32 i = 0; i < eMATHVARIANT_COUNT; ++i) {
285 0 : nsCAutoString key(NS_LITERAL_CSTRING("mathvariant."));
286 0 : key.Append(kMathVariant_name[i]);
287 0 : nsAutoString value;
288 0 : mathfontProp->GetStringProperty(key, value);
289 0 : gInvariantCharArray->AppendElement(value); // i.e., gInvariantCharArray[i] holds this list
290 : }
291 :
292 : // Parse the Operator Dictionary in two passes.
293 : // The first pass is to count the number of operators; the second pass is to
294 : // allocate the necessary space for them and to add them in the hash table.
295 0 : for (PRInt32 pass = 1; pass <= 2; pass++) {
296 0 : OperatorData dummyData;
297 0 : OperatorData* operatorData = &dummyData;
298 0 : nsCOMPtr<nsISimpleEnumerator> iterator;
299 0 : if (NS_SUCCEEDED(mathfontProp->Enumerate(getter_AddRefs(iterator)))) {
300 : bool more;
301 0 : PRUint32 index = 0;
302 0 : nsCAutoString name;
303 0 : nsAutoString attributes;
304 0 : while ((NS_SUCCEEDED(iterator->HasMoreElements(&more))) && more) {
305 0 : nsCOMPtr<nsIPropertyElement> element;
306 0 : if (NS_SUCCEEDED(iterator->GetNext(getter_AddRefs(element)))) {
307 0 : if (NS_SUCCEEDED(element->GetKey(name)) &&
308 0 : NS_SUCCEEDED(element->GetValue(attributes))) {
309 : // expected key: operator.\uNNNN.{infix,postfix,prefix}
310 0 : if ((21 <= name.Length()) && (0 == name.Find("operator.\\u"))) {
311 0 : name.Cut(0, 9); // 9 is the length of "operator.";
312 0 : PRInt32 len = name.Length();
313 0 : nsOperatorFlags form = 0;
314 0 : if (kNotFound != name.RFind(".infix")) {
315 0 : form = NS_MATHML_OPERATOR_FORM_INFIX;
316 0 : len -= 6; // 6 is the length of ".infix";
317 : }
318 0 : else if (kNotFound != name.RFind(".postfix")) {
319 0 : form = NS_MATHML_OPERATOR_FORM_POSTFIX;
320 0 : len -= 8; // 8 is the length of ".postfix";
321 : }
322 0 : else if (kNotFound != name.RFind(".prefix")) {
323 0 : form = NS_MATHML_OPERATOR_FORM_PREFIX;
324 0 : len -= 7; // 7 is the length of ".prefix";
325 : }
326 0 : else continue; // input is not applicable
327 0 : name.SetLength(len);
328 0 : if (2 == pass) { // allocate space and start the storage
329 0 : if (!gOperatorArray) {
330 0 : if (0 == gOperatorCount) return NS_ERROR_UNEXPECTED;
331 0 : gOperatorArray = new OperatorData[gOperatorCount];
332 0 : if (!gOperatorArray) return NS_ERROR_OUT_OF_MEMORY;
333 : }
334 0 : operatorData = &gOperatorArray[index];
335 : }
336 : else {
337 0 : form = 0; // to quickly return from SetOperator() at pass 1
338 : }
339 : // See if the operator should be retained
340 0 : if (SetOperator(operatorData, form, name, attributes)) {
341 0 : index++;
342 0 : if (1 == pass) gOperatorCount = index;
343 : }
344 : }
345 : }
346 : }
347 : }
348 : }
349 : }
350 0 : return NS_OK;
351 : }
352 :
353 : static nsresult
354 0 : InitGlobals()
355 : {
356 0 : gInitialized = true;
357 0 : nsresult rv = NS_ERROR_OUT_OF_MEMORY;
358 0 : gInvariantCharArray = new nsTArray<nsString>();
359 0 : if (gInvariantCharArray) {
360 0 : gOperatorTable = new nsHashtable();
361 0 : if (gOperatorTable) {
362 0 : rv = InitOperators();
363 : }
364 : }
365 0 : if (NS_FAILED(rv))
366 0 : nsMathMLOperators::CleanUp();
367 0 : return rv;
368 : }
369 :
370 : void
371 1403 : nsMathMLOperators::CleanUp()
372 : {
373 1403 : if (gInvariantCharArray) {
374 0 : delete gInvariantCharArray;
375 0 : gInvariantCharArray = nsnull;
376 : }
377 1403 : if (gOperatorArray) {
378 0 : delete[] gOperatorArray;
379 0 : gOperatorArray = nsnull;
380 : }
381 1403 : if (gOperatorTable) {
382 0 : delete gOperatorTable;
383 0 : gOperatorTable = nsnull;
384 : }
385 1403 : }
386 :
387 : void
388 1404 : nsMathMLOperators::AddRefTable(void)
389 : {
390 1404 : gTableRefCount++;
391 1404 : }
392 :
393 : void
394 1403 : nsMathMLOperators::ReleaseTable(void)
395 : {
396 1403 : if (0 == --gTableRefCount) {
397 1403 : CleanUp();
398 : }
399 1403 : }
400 :
401 : static OperatorData*
402 0 : GetOperatorData(const nsString& aOperator, nsOperatorFlags aForm)
403 : {
404 0 : nsAutoString key(aOperator);
405 0 : key.AppendInt(aForm);
406 0 : nsStringKey hkey(key);
407 0 : return (OperatorData*)gOperatorTable->Get(&hkey);
408 : }
409 :
410 : bool
411 0 : nsMathMLOperators::LookupOperator(const nsString& aOperator,
412 : const nsOperatorFlags aForm,
413 : nsOperatorFlags* aFlags,
414 : float* aLeadingSpace,
415 : float* aTrailingSpace)
416 : {
417 0 : if (!gInitialized) {
418 0 : InitGlobals();
419 : }
420 0 : if (gOperatorTable) {
421 0 : NS_ASSERTION(aFlags && aLeadingSpace && aTrailingSpace, "bad usage");
422 0 : NS_ASSERTION(aForm > 0 && aForm < 4, "*** invalid call ***");
423 :
424 : // The MathML REC says:
425 : // If the operator does not occur in the dictionary with the specified form,
426 : // the renderer should use one of the forms which is available there, in the
427 : // order of preference: infix, postfix, prefix.
428 :
429 : OperatorData* found;
430 0 : PRInt32 form = NS_MATHML_OPERATOR_GET_FORM(aForm);
431 0 : if (!(found = GetOperatorData(aOperator, form))) {
432 0 : if (form == NS_MATHML_OPERATOR_FORM_INFIX ||
433 : !(found =
434 : GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_INFIX))) {
435 0 : if (form == NS_MATHML_OPERATOR_FORM_POSTFIX ||
436 : !(found =
437 : GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_POSTFIX))) {
438 0 : if (form != NS_MATHML_OPERATOR_FORM_PREFIX) {
439 0 : found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_PREFIX);
440 : }
441 : }
442 : }
443 : }
444 0 : if (found) {
445 0 : NS_ASSERTION(found->mStr.Equals(aOperator), "bad setup");
446 0 : *aLeadingSpace = found->mLeadingSpace;
447 0 : *aTrailingSpace = found->mTrailingSpace;
448 0 : *aFlags &= ~NS_MATHML_OPERATOR_FORM; // clear the form bits
449 0 : *aFlags |= found->mFlags; // just add bits without overwriting
450 0 : return true;
451 : }
452 : }
453 0 : return false;
454 : }
455 :
456 : void
457 0 : nsMathMLOperators::LookupOperators(const nsString& aOperator,
458 : nsOperatorFlags* aFlags,
459 : float* aLeadingSpace,
460 : float* aTrailingSpace)
461 : {
462 0 : if (!gInitialized) {
463 0 : InitGlobals();
464 : }
465 :
466 0 : aFlags[NS_MATHML_OPERATOR_FORM_INFIX] = 0;
467 0 : aLeadingSpace[NS_MATHML_OPERATOR_FORM_INFIX] = 0.0f;
468 0 : aTrailingSpace[NS_MATHML_OPERATOR_FORM_INFIX] = 0.0f;
469 :
470 0 : aFlags[NS_MATHML_OPERATOR_FORM_POSTFIX] = 0;
471 0 : aLeadingSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = 0.0f;
472 0 : aTrailingSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = 0.0f;
473 :
474 0 : aFlags[NS_MATHML_OPERATOR_FORM_PREFIX] = 0;
475 0 : aLeadingSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = 0.0f;
476 0 : aTrailingSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = 0.0f;
477 :
478 0 : if (gOperatorTable) {
479 : OperatorData* found;
480 0 : found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_INFIX);
481 0 : if (found) {
482 0 : aFlags[NS_MATHML_OPERATOR_FORM_INFIX] = found->mFlags;
483 0 : aLeadingSpace[NS_MATHML_OPERATOR_FORM_INFIX] = found->mLeadingSpace;
484 0 : aTrailingSpace[NS_MATHML_OPERATOR_FORM_INFIX] = found->mTrailingSpace;
485 : }
486 0 : found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_POSTFIX);
487 0 : if (found) {
488 0 : aFlags[NS_MATHML_OPERATOR_FORM_POSTFIX] = found->mFlags;
489 0 : aLeadingSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = found->mLeadingSpace;
490 0 : aTrailingSpace[NS_MATHML_OPERATOR_FORM_POSTFIX] = found->mTrailingSpace;
491 : }
492 0 : found = GetOperatorData(aOperator, NS_MATHML_OPERATOR_FORM_PREFIX);
493 0 : if (found) {
494 0 : aFlags[NS_MATHML_OPERATOR_FORM_PREFIX] = found->mFlags;
495 0 : aLeadingSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = found->mLeadingSpace;
496 0 : aTrailingSpace[NS_MATHML_OPERATOR_FORM_PREFIX] = found->mTrailingSpace;
497 : }
498 : }
499 0 : }
500 :
501 : bool
502 0 : nsMathMLOperators::IsMutableOperator(const nsString& aOperator)
503 : {
504 0 : if (!gInitialized) {
505 0 : InitGlobals();
506 : }
507 : // lookup all the variants of the operator and return true if there
508 : // is a variant that is stretchy or largeop
509 : nsOperatorFlags flags[4];
510 : float lspace[4], rspace[4];
511 0 : nsMathMLOperators::LookupOperators(aOperator, flags, lspace, rspace);
512 : nsOperatorFlags allFlags =
513 0 : flags[NS_MATHML_OPERATOR_FORM_INFIX] |
514 0 : flags[NS_MATHML_OPERATOR_FORM_POSTFIX] |
515 0 : flags[NS_MATHML_OPERATOR_FORM_PREFIX];
516 : return NS_MATHML_OPERATOR_IS_STRETCHY(allFlags) ||
517 0 : NS_MATHML_OPERATOR_IS_LARGEOP(allFlags);
518 : }
519 :
520 : /* static */ bool
521 0 : nsMathMLOperators::IsMirrorableOperator(const nsString& aOperator)
522 : {
523 : // LookupOperator will search infix, postfix and prefix forms of aOperator and
524 : // return the first form found. It is assumed that all these forms have same
525 : // mirrorability.
526 0 : nsOperatorFlags flags = 0;
527 : float dummy;
528 : nsMathMLOperators::LookupOperator(aOperator,
529 : NS_MATHML_OPERATOR_FORM_INFIX,
530 0 : &flags, &dummy, &dummy);
531 0 : return NS_MATHML_OPERATOR_IS_MIRRORABLE(flags);
532 : }
533 :
534 : /* static */ nsStretchDirection
535 0 : nsMathMLOperators::GetStretchyDirection(const nsString& aOperator)
536 : {
537 : // LookupOperator will search infix, postfix and prefix forms of aOperator and
538 : // return the first form found. It is assumed that all these forms have same
539 : // direction.
540 0 : nsOperatorFlags flags = 0;
541 : float dummy;
542 : nsMathMLOperators::LookupOperator(aOperator,
543 : NS_MATHML_OPERATOR_FORM_INFIX,
544 0 : &flags, &dummy, &dummy);
545 :
546 0 : if (NS_MATHML_OPERATOR_IS_DIRECTION_VERTICAL(flags)) {
547 0 : return NS_STRETCH_DIRECTION_VERTICAL;
548 0 : } else if (NS_MATHML_OPERATOR_IS_DIRECTION_HORIZONTAL(flags)) {
549 0 : return NS_STRETCH_DIRECTION_HORIZONTAL;
550 : } else {
551 0 : return NS_STRETCH_DIRECTION_UNSUPPORTED;
552 : }
553 : }
554 :
555 : /* static */ eMATHVARIANT
556 0 : nsMathMLOperators::LookupInvariantChar(const nsAString& aChar)
557 : {
558 0 : if (!gInitialized) {
559 0 : InitGlobals();
560 : }
561 0 : if (gInvariantCharArray) {
562 0 : for (PRInt32 i = gInvariantCharArray->Length()-1; i >= 0; --i) {
563 0 : const nsString& list = gInvariantCharArray->ElementAt(i);
564 0 : nsString::const_iterator start, end;
565 0 : list.BeginReading(start);
566 0 : list.EndReading(end);
567 : // Style-invariant characters are at offset 3*j + 1.
568 0 : if (FindInReadable(aChar, start, end) &&
569 0 : start.size_backward() % 3 == 1) {
570 0 : return eMATHVARIANT(i);
571 : }
572 : }
573 : }
574 0 : return eMATHVARIANT_NONE;
575 : }
576 :
577 : /* static */ const nsDependentSubstring
578 0 : nsMathMLOperators::TransformVariantChar(const PRUnichar& aChar,
579 : eMATHVARIANT aVariant)
580 : {
581 0 : if (!gInitialized) {
582 0 : InitGlobals();
583 : }
584 0 : if (gInvariantCharArray) {
585 0 : nsString list = gInvariantCharArray->ElementAt(aVariant);
586 0 : PRInt32 index = list.FindChar(aChar);
587 : // BMP characters are at offset 3*j
588 0 : if (index != kNotFound && index % 3 == 0 && list.Length() - index >= 2 ) {
589 : // The style-invariant character is the next character
590 : // (and list should contain padding if the next character is in the BMP).
591 0 : ++index;
592 0 : PRUint32 len = NS_IS_HIGH_SURROGATE(list.CharAt(index)) ? 2 : 1;
593 0 : return nsDependentSubstring(list, index, len);
594 : }
595 : }
596 0 : return nsDependentSubstring(&aChar, &aChar + 1);
597 : }
|