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 : * Shyjan Mahamud <mahamud@cs.cmu.edu>
25 : * Karl Tomlinson <karlt+@karlt.net>, Mozilla Corporation
26 : * Frederic Wang <fred.wang@free.fr>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either of the GNU General Public License Version 2 or later (the "GPL"),
30 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #include "nsCOMPtr.h"
43 : #include "nsFrame.h"
44 : #include "nsPresContext.h"
45 : #include "nsStyleContext.h"
46 : #include "nsStyleConsts.h"
47 : #include "nsString.h"
48 : #include "nsUnicharUtils.h"
49 : #include "nsRenderingContext.h"
50 : #include "gfxPlatform.h"
51 :
52 : #include "mozilla/Preferences.h"
53 : #include "nsISupportsPrimitives.h"
54 : #include "nsIComponentManager.h"
55 : #include "nsIPersistentProperties2.h"
56 : #include "nsIServiceManager.h"
57 : #include "nsIObserverService.h"
58 : #include "nsIObserver.h"
59 : #include "nsNetUtil.h"
60 :
61 : #include "mozilla/LookAndFeel.h"
62 : #include "nsCSSRendering.h"
63 : #include "prprf.h" // For PR_snprintf()
64 :
65 : #include "nsDisplayList.h"
66 :
67 : #include "nsMathMLOperators.h"
68 : #include "nsMathMLChar.h"
69 :
70 : using namespace mozilla;
71 :
72 : //#define SHOW_BORDERS 1
73 : //#define NOISY_SEARCH 1
74 :
75 : // -----------------------------------------------------------------------------------
76 : static const PRUnichar kSpaceCh = PRUnichar(' ');
77 : static const nsGlyphCode kNullGlyph = {{0, 0}, 0};
78 : typedef enum {eExtension_base, eExtension_variants, eExtension_parts}
79 : nsMathfontPrefExtension;
80 :
81 : // -----------------------------------------------------------------------------------
82 : // nsGlyphTable is a class that provides an interface for accessing glyphs
83 : // of stretchy chars. It acts like a table that stores the variants of bigger
84 : // sizes (if any) and the partial glyphs needed to build extensible symbols.
85 : // An instance of nsGlyphTable is associated to one primary font. Extra glyphs
86 : // can be taken in other additional fonts when stretching certain characters.
87 : // These supplementary fonts are referred to as "external" fonts to the table.
88 : //
89 : // A char for which nsGlyphTable::Has(aChar) is true means that the table
90 : // contains some glyphs (bigger and/or partial) that can be used to render
91 : // the char. Bigger sizes (if any) of the char can then be retrieved with
92 : // BigOf(aSize). Partial glyphs can be retrieved with TopOf(), GlueOf(), etc.
93 : //
94 : // A table consists of "nsGlyphCode"s which are viewed either as Unicode
95 : // points or as direct glyph indices, depending on the type of the table.
96 : // XXX The latter is not yet supported.
97 :
98 : // General format of MathFont Property Files from which glyph data are retrieved:
99 : // -----------------------------------------------------------------------------------
100 : // Each font should have its set of glyph data. For example, the glyph data for
101 : // the "Symbol" font and the "MT Extra" font are in "mathfontSymbol.properties"
102 : // and "mathfontMTExtra.properties", respectively. The mathfont property file is a
103 : // set of all the stretchy MathML characters that can be rendered with that font
104 : // using larger and/or partial glyphs. The entry of each stretchy character in the
105 : // mathfont property file gives, in that order, the 4 partial glyphs: Top (or Left),
106 : // Middle, Bottom (or Right), Glue; and the variants of bigger sizes (if any).
107 : // A position that is not relevant to a particular character is indicated there
108 : // with the UNICODE REPLACEMENT CHARACTER 0xFFFD.
109 : // Characters that need to be built recursively from other characters are said
110 : // to be composite. For example, chars like over/underbrace in CMEX10 have to
111 : // be built from two half stretchy chars and joined in the middle (TeXbook, p.225).
112 : // Such chars are handled in a special manner by the nsMathMLChar class, which allows
113 : // several (2 or more) child chars to be composed in order to render another char.
114 : // To specify such chars, their list of glyphs in the property file should be given
115 : // as space-separated segments of glyphs. Each segment gives the 4 partial
116 : // glyphs with which to build the child char that will be joined with its other
117 : // siblings. In this code, when this situation happens (see the detailed description
118 : // of Stretch() below), the original char (referred to as "parent") creates a
119 : // singly-linked list of child chars, asking them to stretch in an equally divided
120 : // space. The nsGlyphTable embeds the necessary logic to guarantee correctness in a
121 : // recursive stretch (and in the use of TopOf(), GlueOf(), etc) on these child chars.
122 : // -----------------------------------------------------------------------------------
123 :
124 : #define NS_TABLE_TYPE_UNICODE 0
125 : #define NS_TABLE_TYPE_GLYPH_INDEX 1
126 :
127 : #define NS_TABLE_STATE_ERROR -1
128 : #define NS_TABLE_STATE_EMPTY 0
129 : #define NS_TABLE_STATE_READY 1
130 :
131 : // helper to trim off comments from data in a MathFont Property File
132 : static void
133 0 : Clean(nsString& aValue)
134 : {
135 : // chop the trailing # comment portion if any ...
136 0 : PRInt32 comment = aValue.RFindChar('#');
137 0 : if (comment > 0) aValue.Truncate(comment);
138 0 : aValue.CompressWhitespace();
139 0 : }
140 :
141 : // helper to load a MathFont Property File
142 : static nsresult
143 0 : LoadProperties(const nsString& aName,
144 : nsCOMPtr<nsIPersistentProperties>& aProperties)
145 : {
146 0 : nsAutoString uriStr;
147 0 : uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
148 0 : uriStr.Append(aName);
149 0 : uriStr.StripWhitespace(); // that may come from aName
150 0 : uriStr.AppendLiteral(".properties");
151 0 : return NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(aProperties),
152 0 : NS_ConvertUTF16toUTF8(uriStr));
153 : }
154 :
155 : // -----------------------------------------------------------------------------------
156 :
157 : class nsGlyphTable {
158 : public:
159 0 : explicit nsGlyphTable(const nsString& aPrimaryFontName)
160 : : mType(NS_TABLE_TYPE_UNICODE),
161 : mFontName(1), // ensure space for primary font name.
162 : mState(NS_TABLE_STATE_EMPTY),
163 0 : mCharCache(0)
164 : {
165 0 : MOZ_COUNT_CTOR(nsGlyphTable);
166 0 : mFontName.AppendElement(aPrimaryFontName);
167 0 : }
168 :
169 0 : ~nsGlyphTable() // not a virtual destructor: this class is not intended to be subclassed
170 0 : {
171 0 : MOZ_COUNT_DTOR(nsGlyphTable);
172 0 : }
173 :
174 0 : const nsAString& PrimaryFontName() const
175 : {
176 0 : return mFontName[0];
177 : }
178 :
179 0 : const nsAString& FontNameFor(const nsGlyphCode& aGlyphCode) const
180 : {
181 0 : return mFontName[aGlyphCode.font];
182 : }
183 :
184 : // True if this table contains some glyphs (variants and/or parts)
185 : // or contains child chars that can be used to render this char
186 : bool Has(nsPresContext* aPresContext, nsMathMLChar* aChar);
187 :
188 : // True if this table contains variants of larger sizes to render this char
189 : bool HasVariantsOf(nsPresContext* aPresContext, nsMathMLChar* aChar);
190 :
191 : // True if this table contains parts (or composite parts) to render this char
192 : bool HasPartsOf(nsPresContext* aPresContext, nsMathMLChar* aChar);
193 :
194 : // True if aChar is to be assembled from other child chars in this table
195 : bool IsComposite(nsPresContext* aPresContext, nsMathMLChar* aChar);
196 :
197 : // The number of child chars to assemble in order to render aChar
198 : PRInt32 ChildCountOf(nsPresContext* aPresContext, nsMathMLChar* aChar);
199 :
200 : // Getters for the parts
201 0 : nsGlyphCode TopOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
202 0 : return ElementAt(aPresContext, aChar, 0);
203 : }
204 0 : nsGlyphCode MiddleOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
205 0 : return ElementAt(aPresContext, aChar, 1);
206 : }
207 0 : nsGlyphCode BottomOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
208 0 : return ElementAt(aPresContext, aChar, 2);
209 : }
210 0 : nsGlyphCode GlueOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
211 0 : return ElementAt(aPresContext, aChar, 3);
212 : }
213 0 : nsGlyphCode BigOf(nsPresContext* aPresContext, nsMathMLChar* aChar, PRInt32 aSize) {
214 0 : return ElementAt(aPresContext, aChar, 4 + aSize);
215 : }
216 0 : nsGlyphCode LeftOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
217 0 : return ElementAt(aPresContext, aChar, 0);
218 : }
219 0 : nsGlyphCode RightOf(nsPresContext* aPresContext, nsMathMLChar* aChar) {
220 0 : return ElementAt(aPresContext, aChar, 2);
221 : }
222 :
223 : private:
224 : nsGlyphCode ElementAt(nsPresContext* aPresContext, nsMathMLChar* aChar, PRUint32 aPosition);
225 :
226 : // The type is either NS_TABLE_TYPE_UNICODE or NS_TABLE_TYPE_GLYPH_INDEX
227 : PRInt32 mType;
228 :
229 : // mFontName[0] is the primary font associated to this table. The others
230 : // are possible "external" fonts for glyphs not in the primary font
231 : // but which are needed to stretch certain characters in the table
232 : nsTArray<nsString> mFontName;
233 :
234 : // Tri-state variable for error/empty/ready
235 : PRInt32 mState;
236 :
237 : // The set of glyph data in this table, as provided by the MathFont Property File
238 : nsCOMPtr<nsIPersistentProperties> mGlyphProperties;
239 :
240 : // For speedy re-use, we always cache the last data used in the table.
241 : // mCharCache is the Unicode point of the last char that was queried in this
242 : // table. mGlyphCache is a buffer containing the glyph data associated to
243 : // that char. For a property line 'key = value' in the MathFont Property File,
244 : // mCharCache will retain the 'key' -- which is a Unicode point, while mGlyphCache
245 : // will retain the 'value', which is a consecutive list of nsGlyphCodes, i.e.,
246 : // the pairs of 'code@font' needed by the char -- in which 'code@0' can be specified
247 : // without the optional '@0'. However, to ease subsequent processing, mGlyphCache
248 : // excludes the '@' symbol and explicitly inserts all optional '0' that indicates
249 : // the primary font identifier. Specifically therefore, the k-th glyph is
250 : // characterized by :
251 : // 1) mGlyphCache[3*k],mGlyphCache[3*k+1] : its Unicode point (or glyph index -- depending on mType),
252 : // 2) mGlyphCache[3*k+2] : the numeric identifier of the font where it comes from.
253 : // A font identifier of '0' means the default primary font associated to this
254 : // table. Other digits map to the "external" fonts that may have been specified
255 : // in the MathFont Property File.
256 : nsString mGlyphCache;
257 : PRUnichar mCharCache;
258 : };
259 :
260 : nsGlyphCode
261 0 : nsGlyphTable::ElementAt(nsPresContext* aPresContext, nsMathMLChar* aChar, PRUint32 aPosition)
262 : {
263 0 : if (mState == NS_TABLE_STATE_ERROR) return kNullGlyph;
264 : // Load glyph properties if this is the first time we have been here
265 0 : if (mState == NS_TABLE_STATE_EMPTY) {
266 0 : nsresult rv = LoadProperties(mFontName[0], mGlyphProperties);
267 : #ifdef NS_DEBUG
268 0 : nsCAutoString uriStr;
269 0 : uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
270 0 : LossyAppendUTF16toASCII(mFontName[0], uriStr);
271 0 : uriStr.StripWhitespace(); // that may come from mFontName
272 0 : uriStr.AppendLiteral(".properties");
273 : printf("Loading %s ... %s\n",
274 : uriStr.get(),
275 0 : (NS_FAILED(rv)) ? "Failed" : "Done");
276 : #endif
277 0 : if (NS_FAILED(rv)) {
278 0 : mState = NS_TABLE_STATE_ERROR; // never waste time with this table again
279 0 : return kNullGlyph;
280 : }
281 0 : mState = NS_TABLE_STATE_READY;
282 :
283 : // see if there are external fonts needed for certain chars in this table
284 0 : nsCAutoString key;
285 0 : nsAutoString value;
286 0 : for (PRInt32 i = 1; ; i++) {
287 0 : key.AssignLiteral("external.");
288 0 : key.AppendInt(i, 10);
289 0 : rv = mGlyphProperties->GetStringProperty(key, value);
290 0 : if (NS_FAILED(rv)) break;
291 0 : Clean(value);
292 0 : mFontName.AppendElement(value); // i.e., mFontName[i] holds this font name
293 : }
294 : }
295 :
296 : // If aChar is a child char to be used by a parent composite char, make
297 : // sure that it is really attached to this table
298 0 : if (aChar->mParent && (aChar->mGlyphTable != this)) return kNullGlyph;
299 :
300 : // Update our cache if it is not associated to this character
301 0 : PRUnichar uchar = aChar->mData[0];
302 0 : if (mCharCache != uchar) {
303 : // The key in the property file is interpreted as ASCII and kept
304 : // as such ...
305 0 : char key[10]; PR_snprintf(key, sizeof(key), "\\u%04X", uchar);
306 0 : nsAutoString value;
307 0 : nsresult rv = mGlyphProperties->GetStringProperty(nsDependentCString(key), value);
308 0 : if (NS_FAILED(rv)) return kNullGlyph;
309 0 : Clean(value);
310 : // See if this char uses external fonts; e.g., if the 2nd glyph is taken from the
311 : // external font '1', the property line looks like \uNNNN = \uNNNN\uNNNN@1\uNNNN.
312 : // This is where mGlyphCache is pre-processed to explicitly store all glyph codes
313 : // as combined pairs of 'code@font', excluding the '@' separator. This means that
314 : // mGlyphCache[3*k],mGlyphCache[3*k+1] will later be rendered with mFontName[mGlyphCache[3*k+2]]
315 : // Note: font identifier is internally an ASCII digit to avoid the null char issue
316 0 : nsAutoString buffer;
317 0 : PRInt32 length = value.Length();
318 0 : PRInt32 i = 0; // index in value
319 0 : PRInt32 j = 0; // part/variant index
320 0 : while (i < length) {
321 0 : PRUnichar code = value[i];
322 0 : ++i;
323 0 : buffer.Append(code);
324 : // see if we are at the beginning of a child char
325 0 : if (code == kSpaceCh) {
326 : // reset the annotation indicator to be 0 for the next code point
327 0 : j = -1;
328 : }
329 : #if 0 // If we want this then the nsGlyphTableList must be declared
330 : // or the UnicodeTable could be made a global.
331 : // See if this code point is an *indirect reference* to the Unicode
332 : // table and lookup the code there.
333 : else if (code == PRUnichar(0xF8FF) && gGlyphTableList &&
334 : this != &gGlyphTableList->mUnicodeTable) {
335 : code = gGlyphTableList->mUnicodeTable.
336 : ElementAt(aPresContext, aChar, aPosition).code;
337 : }
338 : // see if this code point is a *direct reference* to
339 : // the Unicode table, and lookup the [TLMBRG1-9] position for code.
340 : else if ((i+1 < length) && (value[i] == PRUnichar('.'))) {
341 : ++i;
342 : // Need to implement this if we want it:
343 : // Set (new) code from the value[i] position for (current) code.
344 : if (1)
345 : return kNullGlyph;
346 : ++i;
347 : }
348 : #endif
349 : // Read the next word if we have a non-BMP character.
350 0 : if (i < length && NS_IS_HIGH_SURROGATE(code)) {
351 0 : code = value[i];
352 0 : ++i;
353 : } else {
354 0 : code = PRUnichar('\0');
355 : }
356 0 : buffer.Append(code);
357 :
358 : // See if an external font is needed for the code point.
359 : // Limit of 9 external fonts
360 0 : PRUnichar font = 0;
361 0 : if (i+1 < length && value[i] == PRUnichar('@') &&
362 0 : value[i+1] >= PRUnichar('0') && value[i+1] <= PRUnichar('9')) {
363 0 : ++i;
364 0 : font = value[i] - '0';
365 0 : ++i;
366 0 : if (font >= mFontName.Length()) {
367 0 : NS_ERROR("Nonexistent font referenced in glyph table");
368 0 : return kNullGlyph;
369 : }
370 : // The char cannot be handled if this font is not installed
371 0 : if (!mFontName[font].Length()) {
372 0 : return kNullGlyph;
373 : }
374 : }
375 0 : buffer.Append(font);
376 0 : ++j;
377 : }
378 : // update our cache with the new settings
379 0 : mGlyphCache.Assign(buffer);
380 0 : mCharCache = uchar;
381 : }
382 :
383 : // If aChar is a composite char, only its children are allowed
384 : // to use its glyphs in this table, i.e., the parent char itself
385 : // is disabled and cannot be stretched directly with these glyphs.
386 : // This guarantees a coherent behavior in Stretch().
387 0 : if (!aChar->mParent && (kNotFound != mGlyphCache.FindChar(kSpaceCh))) {
388 0 : return kNullGlyph;
389 : }
390 :
391 : // If aChar is a child char, the index of the glyph is relative to
392 : // the offset of the list of glyphs corresponding to the child char.
393 0 : PRUint32 offset = 0;
394 0 : PRUint32 length = mGlyphCache.Length();
395 0 : if (aChar->mParent) {
396 0 : nsMathMLChar* child = aChar->mParent->mSibling;
397 : // XXXkt composite chars can't have size variants
398 0 : while (child && (child != aChar)) {
399 0 : offset += 5; // skip the 4 partial glyphs + the whitespace separator
400 0 : child = child->mSibling;
401 : }
402 0 : length = 3*(offset + 4); // stay confined in the 4 partial glyphs of this child
403 : }
404 0 : PRUint32 index = 3*(offset + aPosition); // 3* is to account for the code@font pairs
405 0 : if (index+2 >= length) return kNullGlyph;
406 : nsGlyphCode ch;
407 0 : ch.code[0] = mGlyphCache.CharAt(index);
408 0 : ch.code[1] = mGlyphCache.CharAt(index + 1);
409 0 : ch.font = mGlyphCache.CharAt(index + 2);
410 0 : return ch.code[0] == PRUnichar(0xFFFD) ? kNullGlyph : ch;
411 : }
412 :
413 : bool
414 0 : nsGlyphTable::IsComposite(nsPresContext* aPresContext, nsMathMLChar* aChar)
415 : {
416 : // there is only one level of recursion in our model. a child
417 : // cannot be composite because it cannot have its own children
418 0 : if (aChar->mParent) return false;
419 : // shortcut to sync the cache with this char...
420 0 : mCharCache = 0; mGlyphCache.Truncate(); ElementAt(aPresContext, aChar, 0);
421 : // the cache remained empty if the char wasn't found in this table
422 0 : if (4*3 >= mGlyphCache.Length()) return false;
423 : // the lists of glyphs of a composite char are space-separated
424 0 : return (kSpaceCh == mGlyphCache.CharAt(4*3));
425 : }
426 :
427 : PRInt32
428 0 : nsGlyphTable::ChildCountOf(nsPresContext* aPresContext, nsMathMLChar* aChar)
429 : {
430 : // this will sync the cache as well ...
431 0 : if (!IsComposite(aPresContext, aChar)) return 0;
432 : // the lists of glyphs of a composite char are space-separated
433 0 : return 1 + mGlyphCache.CountChar(kSpaceCh);
434 : }
435 :
436 : bool
437 0 : nsGlyphTable::Has(nsPresContext* aPresContext, nsMathMLChar* aChar)
438 : {
439 0 : return HasVariantsOf(aPresContext, aChar) || HasPartsOf(aPresContext, aChar);
440 : }
441 :
442 : bool
443 0 : nsGlyphTable::HasVariantsOf(nsPresContext* aPresContext, nsMathMLChar* aChar)
444 : {
445 : //XXXkt all variants must be in the same file as size 1
446 0 : return BigOf(aPresContext, aChar, 1).Exists();
447 : }
448 :
449 : bool
450 0 : nsGlyphTable::HasPartsOf(nsPresContext* aPresContext, nsMathMLChar* aChar)
451 : {
452 0 : return GlueOf(aPresContext, aChar).Exists() ||
453 0 : TopOf(aPresContext, aChar).Exists() ||
454 0 : BottomOf(aPresContext, aChar).Exists() ||
455 0 : MiddleOf(aPresContext, aChar).Exists() ||
456 0 : IsComposite(aPresContext, aChar);
457 : }
458 :
459 : // -----------------------------------------------------------------------------------
460 : // This is the list of all the applicable glyph tables.
461 : // We will maintain a single global instance that will only reveal those
462 : // glyph tables that are associated to fonts currently installed on the
463 : // user' system. The class is an XPCOM shutdown observer to allow us to
464 : // free its allocated data at shutdown
465 :
466 : class nsGlyphTableList : public nsIObserver
467 : {
468 : public:
469 : NS_DECL_ISUPPORTS
470 : NS_DECL_NSIOBSERVER
471 :
472 : nsGlyphTable mUnicodeTable;
473 :
474 0 : nsGlyphTableList()
475 0 : : mUnicodeTable(NS_LITERAL_STRING("Unicode"))
476 : {
477 0 : MOZ_COUNT_CTOR(nsGlyphTableList);
478 0 : }
479 :
480 0 : virtual ~nsGlyphTableList()
481 0 : {
482 0 : MOZ_COUNT_DTOR(nsGlyphTableList);
483 0 : }
484 :
485 : nsresult Initialize();
486 : nsresult Finalize();
487 :
488 : // Add a glyph table in the list, return the new table that was added
489 : nsGlyphTable*
490 : AddGlyphTable(const nsString& aPrimaryFontName);
491 :
492 : // Find a glyph table in the list that has a glyph for the given char
493 : nsGlyphTable*
494 : GetGlyphTableFor(nsPresContext* aPresContext,
495 : nsMathMLChar* aChar);
496 :
497 : // Find the glyph table in the list corresponding to the given font family.
498 : nsGlyphTable*
499 : GetGlyphTableFor(const nsAString& aFamily);
500 :
501 : private:
502 0 : nsGlyphTable* TableAt(PRInt32 aIndex) {
503 0 : return &mTableList.ElementAt(aIndex);
504 : }
505 0 : PRInt32 Count() {
506 0 : return mTableList.Length();
507 : }
508 :
509 : // List of glyph tables;
510 : nsTArray<nsGlyphTable> mTableList;
511 : };
512 :
513 0 : NS_IMPL_ISUPPORTS1(nsGlyphTableList, nsIObserver)
514 :
515 : // -----------------------------------------------------------------------------------
516 : // Here is the global list of applicable glyph tables that we will be using
517 : static nsGlyphTableList* gGlyphTableList = nsnull;
518 :
519 : static bool gInitialized = false;
520 :
521 : // XPCOM shutdown observer
522 : NS_IMETHODIMP
523 0 : nsGlyphTableList::Observe(nsISupports* aSubject,
524 : const char* aTopic,
525 : const PRUnichar* someData)
526 : {
527 0 : Finalize();
528 0 : return NS_OK;
529 : }
530 :
531 : // Add an observer to XPCOM shutdown so that we can free our data at shutdown
532 : nsresult
533 0 : nsGlyphTableList::Initialize()
534 : {
535 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
536 0 : if (!obs)
537 0 : return NS_ERROR_FAILURE;
538 :
539 0 : nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
540 0 : NS_ENSURE_SUCCESS(rv, rv);
541 :
542 0 : return NS_OK;
543 : }
544 :
545 : // Remove our observer and free the memory that were allocated for us
546 : nsresult
547 0 : nsGlyphTableList::Finalize()
548 : {
549 : // Remove our observer from the observer service
550 0 : nsresult rv = NS_OK;
551 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
552 0 : if (obs)
553 0 : rv = obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
554 : else
555 0 : rv = NS_ERROR_FAILURE;
556 :
557 0 : gInitialized = false;
558 : // our oneself will be destroyed when our |Release| is called by the observer
559 0 : return rv;
560 : }
561 :
562 : nsGlyphTable*
563 0 : nsGlyphTableList::AddGlyphTable(const nsString& aPrimaryFontName)
564 : {
565 : // See if there is already a special table for this family.
566 0 : nsGlyphTable* glyphTable = GetGlyphTableFor(aPrimaryFontName);
567 0 : if (glyphTable != &mUnicodeTable)
568 0 : return glyphTable;
569 :
570 : // allocate a table
571 0 : glyphTable = mTableList.AppendElement(aPrimaryFontName);
572 0 : return glyphTable;
573 : }
574 :
575 : nsGlyphTable*
576 0 : nsGlyphTableList::GetGlyphTableFor(nsPresContext* aPresContext,
577 : nsMathMLChar* aChar)
578 : {
579 0 : if (mUnicodeTable.Has(aPresContext, aChar))
580 0 : return &mUnicodeTable;
581 :
582 : PRInt32 i;
583 0 : for (i = 0; i < Count(); i++) {
584 0 : nsGlyphTable* glyphTable = TableAt(i);
585 0 : if (glyphTable->Has(aPresContext, aChar)) {
586 0 : return glyphTable;
587 : }
588 : }
589 0 : return nsnull;
590 : }
591 :
592 : nsGlyphTable*
593 0 : nsGlyphTableList::GetGlyphTableFor(const nsAString& aFamily)
594 : {
595 0 : for (PRInt32 i = 0; i < Count(); i++) {
596 0 : nsGlyphTable* glyphTable = TableAt(i);
597 0 : const nsAString& fontName = glyphTable->PrimaryFontName();
598 : // TODO: would be nice to consider StripWhitespace and other aliasing
599 0 : if (fontName.Equals(aFamily, nsCaseInsensitiveStringComparator())) {
600 0 : return glyphTable;
601 : }
602 : }
603 : // Fall back to default Unicode table
604 0 : return &mUnicodeTable;
605 : }
606 :
607 : // -----------------------------------------------------------------------------------
608 :
609 : // Lookup the preferences:
610 : // "font.mathfont-family.\uNNNN.base" -- fonts for the base size
611 : // "font.mathfont-family.\uNNNN.variants" -- fonts for larger glyphs
612 : // "font.mathfont-family.\uNNNN.parts" -- fonts for partial glyphs
613 : // Given the char code and mode of stretch, retrieve the preferred extension
614 : // font families.
615 : static bool
616 0 : GetFontExtensionPref(PRUnichar aChar,
617 : nsMathfontPrefExtension aExtension, nsString& aValue)
618 : {
619 : // initialize OUT param
620 0 : aValue.Truncate();
621 :
622 : // We are going to try two keys because some users specify their pref as
623 : // user_pref("font.mathfont-family.\uNNNN.base", "...") rather than
624 : // user_pref("font.mathfont-family.\\uNNNN.base", "...").
625 : // The \uNNNN in the former is interpreted as an UTF16 escape sequence by
626 : // JavaScript and is converted to the internal UTF8 string that JavaScript uses.
627 : // But clueless users who are not savvy of JavaScript have no idea as to what
628 : // is going on and are baffled as to why their pref setting is not working.
629 : // So to save countless explanations, we are going to support both keys.
630 :
631 : static const char* kMathFontPrefix = "font.mathfont-family.";
632 :
633 0 : nsCAutoString extension;
634 0 : switch (aExtension)
635 : {
636 : case eExtension_base:
637 0 : extension.AssignLiteral(".base");
638 0 : break;
639 : case eExtension_variants:
640 0 : extension.AssignLiteral(".variants");
641 0 : break;
642 : case eExtension_parts:
643 0 : extension.AssignLiteral(".parts");
644 0 : break;
645 : default:
646 0 : return false;
647 : }
648 :
649 : // .\\uNNNN key
650 0 : nsCAutoString key;
651 0 : key.AssignASCII(kMathFontPrefix);
652 : char ustr[10];
653 0 : PR_snprintf(ustr, sizeof(ustr), "\\u%04X", aChar);
654 0 : key.Append(ustr);
655 0 : key.Append(extension);
656 : // .\uNNNN key
657 0 : nsCAutoString alternateKey;
658 0 : alternateKey.AssignASCII(kMathFontPrefix);
659 0 : NS_ConvertUTF16toUTF8 tmp(&aChar, 1);
660 0 : alternateKey.Append(tmp);
661 0 : alternateKey.Append(extension);
662 :
663 0 : aValue = Preferences::GetString(key.get());
664 0 : if (aValue.IsEmpty()) {
665 0 : aValue = Preferences::GetString(alternateKey.get());
666 : }
667 0 : return !aValue.IsEmpty();
668 : }
669 :
670 :
671 : static bool
672 0 : MathFontEnumCallback(const nsString& aFamily, bool aGeneric, void *aData)
673 : {
674 0 : if (!gGlyphTableList->AddGlyphTable(aFamily))
675 0 : return false; // stop in low-memory situations
676 0 : return true; // don't stop
677 : }
678 :
679 : static nsresult
680 0 : InitGlobals(nsPresContext* aPresContext)
681 : {
682 0 : NS_ASSERTION(!gInitialized, "Error -- already initialized");
683 0 : gInitialized = true;
684 :
685 : // Allocate the placeholders for the preferred parts and variants
686 0 : nsresult rv = NS_ERROR_OUT_OF_MEMORY;
687 0 : gGlyphTableList = new nsGlyphTableList();
688 0 : if (gGlyphTableList) {
689 0 : rv = gGlyphTableList->Initialize();
690 : }
691 0 : if (NS_FAILED(rv)) {
692 0 : delete gGlyphTableList;
693 0 : gGlyphTableList = nsnull;
694 0 : return rv;
695 : }
696 : /*
697 : else
698 : The gGlyphTableList has been successfully registered as a shutdown observer.
699 : It will be deleted at shutdown, even if a failure happens below.
700 : */
701 :
702 0 : nsCAutoString key;
703 0 : nsAutoString value;
704 0 : nsCOMPtr<nsIPersistentProperties> mathfontProp;
705 :
706 : // Add the math fonts in the gGlyphTableList in order of preference ...
707 : // Note: we only load font-names at this stage. The actual glyph tables will
708 : // be loaded lazily (see nsGlyphTable::ElementAt()).
709 :
710 : // Load the "mathfont.properties" file
711 0 : value.Truncate();
712 0 : rv = LoadProperties(value, mathfontProp);
713 0 : if (NS_FAILED(rv)) return rv;
714 :
715 : // Get the list of mathfonts having special glyph tables to be used for
716 : // stretchy characters.
717 : // We just want to iterate over the font-family list using the
718 : // callback mechanism that nsFont has...
719 0 : nsFont font("", 0, 0, 0, 0, 0, 0);
720 0 : NS_NAMED_LITERAL_CSTRING(defaultKey, "font.mathfont-glyph-tables");
721 0 : rv = mathfontProp->GetStringProperty(defaultKey, font.name);
722 0 : if (NS_FAILED(rv)) return rv;
723 :
724 : // Parse the font list and append an entry for each family to gGlyphTableList
725 0 : nsAutoString missingFamilyList;
726 :
727 0 : font.EnumerateFamilies(MathFontEnumCallback, nsnull);
728 0 : return rv;
729 : }
730 :
731 : // -----------------------------------------------------------------------------------
732 : // And now the implementation of nsMathMLChar
733 :
734 : nsStyleContext*
735 0 : nsMathMLChar::GetStyleContext() const
736 : {
737 0 : NS_ASSERTION(!mParent, "invalid call - not allowed for child chars");
738 0 : NS_ASSERTION(mStyleContext, "chars should always have style context");
739 0 : return mStyleContext;
740 : return NS_OK;
741 : }
742 :
743 : void
744 0 : nsMathMLChar::SetStyleContext(nsStyleContext* aStyleContext)
745 : {
746 0 : NS_ASSERTION(!mParent, "invalid call - not allowed for child chars");
747 0 : NS_PRECONDITION(aStyleContext, "null ptr");
748 0 : if (aStyleContext != mStyleContext) {
749 0 : if (mStyleContext)
750 0 : mStyleContext->Release();
751 0 : if (aStyleContext) {
752 0 : mStyleContext = aStyleContext;
753 0 : aStyleContext->AddRef();
754 :
755 : // Sync the pointers of child chars.
756 0 : nsMathMLChar* child = mSibling;
757 0 : while (child) {
758 0 : child->mStyleContext = mStyleContext;
759 0 : child = child->mSibling;
760 : }
761 : }
762 : }
763 0 : }
764 :
765 : void
766 0 : nsMathMLChar::SetData(nsPresContext* aPresContext,
767 : nsString& aData)
768 : {
769 0 : NS_ASSERTION(!mParent, "invalid call - not allowed for child chars");
770 0 : if (!gInitialized) {
771 0 : InitGlobals(aPresContext);
772 : }
773 0 : mData = aData;
774 : // some assumptions until proven otherwise
775 : // note that mGlyph is not initialized
776 0 : mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
777 0 : mBoundingMetrics = nsBoundingMetrics();
778 0 : mGlyphTable = nsnull;
779 : // check if stretching is applicable ...
780 0 : if (gGlyphTableList && (1 == mData.Length())) {
781 0 : mDirection = nsMathMLOperators::GetStretchyDirection(mData);
782 : // default tentative table (not the one that is necessarily going
783 : // to be used)
784 0 : mGlyphTable = gGlyphTableList->GetGlyphTableFor(aPresContext, this);
785 : }
786 0 : }
787 :
788 : // -----------------------------------------------------------------------------------
789 : /*
790 : The Stretch:
791 : @param aContainerSize - suggested size for the stretched char
792 : @param aDesiredStretchSize - OUT parameter. The desired size
793 : after stretching. If no stretching is done, the output will
794 : simply give the base size.
795 :
796 : How it works?
797 : Summary:-
798 : The Stretch() method first looks for a glyph of appropriate
799 : size; If a glyph is found, it is cached by this object and
800 : its size is returned in aDesiredStretchSize. The cached
801 : glyph will then be used at the painting stage.
802 : If no glyph of appropriate size is found, a search is made
803 : to see if the char can be built by parts.
804 :
805 : Details:-
806 : A character gets stretched through the following pipeline :
807 :
808 : 1) If the base size of the char is sufficient to cover the
809 : container' size, we use that. If not, it will still be
810 : used as a fallback if the other stages in the pipeline fail.
811 : Issues :
812 : a) The base size, the parts and the variants of a char can
813 : be in different fonts. For eg., the base size for '(' should
814 : come from a normal ascii font if CMEX10 is used, since CMEX10
815 : only contains the stretched versions. Hence, there are two
816 : style contexts in use throughout the process. The leaf style
817 : context of the char holds fonts with which to try to stretch
818 : the char. The parent style context of the char contains fonts
819 : for normal rendering. So the parent context is the one used
820 : to get the initial base size at the start of the pipeline.
821 : b) For operators that can be largeop's in display mode,
822 : we will skip the base size even if it fits, so that
823 : the next stage in the pipeline is given a chance to find
824 : a largeop variant. If the next stage fails, we fallback
825 : to the base size.
826 :
827 : 2) We search for the first larger variant of the char that fits the
828 : container' size. We first search for larger variants using the glyph
829 : table corresponding to the first existing font specified in the list of
830 : stretchy fonts held by the leaf style context (from -moz-math-stretchy in
831 : mathml.css). Generic fonts are resolved by the preference
832 : "font.mathfont-family".
833 : Issues :
834 : a) the largeop and display settings determine the starting
835 : size when we do the above search, regardless of whether
836 : smaller variants already fit the container' size.
837 : b) if it is a largeopOnly request (i.e., a displaystyle operator
838 : with largeop=true and stretchy=false), we break after finding
839 : the first starting variant, regardless of whether that
840 : variant fits the container's size.
841 :
842 : 3) If a variant of appropriate size wasn't found, we see if the char
843 : can be built by parts using the same glyph table.
844 : Issues:
845 : a) Certain chars like over/underbrace in CMEX10 have to be built
846 : from two half stretchy chars and joined in the middle. Such
847 : chars are handled in a special manner. When this situation is
848 : detected, the initial char (referred to as "parent") creates a
849 : singly-linked list of child chars, asking them to stretch in
850 : a divided space. A convention is used in the setup of
851 : nsGlyphTable to express that a composite parent char can be built
852 : from child chars.
853 : b) There are some chars that have no middle and glue glyphs. For
854 : such chars, the parts need to be joined using the rule.
855 : By convention (TeXbook p.225), the descent of the parts is
856 : zero while their ascent gives the thickness of the rule that
857 : should be used to join them.
858 :
859 : 4) If a match was not found in that glyph table, repeat from 2 to search the
860 : ordered list of stretchy fonts for the first font with a glyph table that
861 : provides a fit to the container size. If no fit is found, the closest fit
862 : is used.
863 :
864 : Of note:
865 : When the pipeline completes successfully, the desired size of the
866 : stretched char can actually be slightly larger or smaller than
867 : aContainerSize. But it is the responsibility of the caller to
868 : account for the spacing when setting aContainerSize, and to leave
869 : any extra margin when placing the stretched char.
870 : */
871 : // -----------------------------------------------------------------------------------
872 :
873 :
874 : // plain TeX settings (TeXbook p.152)
875 : #define NS_MATHML_DELIMITER_FACTOR 0.901f
876 : #define NS_MATHML_DELIMITER_SHORTFALL_POINTS 5.0f
877 :
878 : static bool
879 0 : IsSizeOK(nsPresContext* aPresContext, nscoord a, nscoord b, PRUint32 aHint)
880 : {
881 : // Normal: True if 'a' is around +/-10% of the target 'b' (10% is
882 : // 1-DelimiterFactor). This often gives a chance to the base size to
883 : // win, especially in the context of <mfenced> without tall elements
884 : // or in sloppy markups without protective <mrow></mrow>
885 : bool isNormal =
886 : (aHint & NS_STRETCH_NORMAL)
887 0 : && bool(float(NS_ABS(a - b))
888 0 : < (1.0f - NS_MATHML_DELIMITER_FACTOR) * float(b));
889 : // Nearer: True if 'a' is around max{ +/-10% of 'b' , 'b' - 5pt },
890 : // as documented in The TeXbook, Ch.17, p.152.
891 : // i.e. within 10% and within 5pt
892 0 : bool isNearer = false;
893 0 : if (aHint & (NS_STRETCH_NEARER | NS_STRETCH_LARGEOP)) {
894 : float c = NS_MAX(float(b) * NS_MATHML_DELIMITER_FACTOR,
895 0 : float(b) - nsPresContext::CSSPointsToAppUnits(NS_MATHML_DELIMITER_SHORTFALL_POINTS));
896 0 : isNearer = bool(float(NS_ABS(b - a)) <= (float(b) - c));
897 : }
898 : // Smaller: Mainly for transitory use, to compare two candidate
899 : // choices
900 : bool isSmaller =
901 : (aHint & NS_STRETCH_SMALLER)
902 : && bool((float(a) >= (NS_MATHML_DELIMITER_FACTOR * float(b)))
903 0 : && (a <= b));
904 : // Larger: Critical to the sqrt code to ensure that the radical
905 : // size is tall enough
906 : bool isLarger =
907 : (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP))
908 0 : && bool(a >= b);
909 0 : return (isNormal || isSmaller || isNearer || isLarger);
910 : }
911 :
912 : static bool
913 0 : IsSizeBetter(nscoord a, nscoord olda, nscoord b, PRUint32 aHint)
914 : {
915 0 : if (0 == olda)
916 0 : return true;
917 0 : if (aHint & (NS_STRETCH_LARGER | NS_STRETCH_LARGEOP))
918 0 : return (a >= olda) ? (olda < b) : (a >= b);
919 0 : if (aHint & NS_STRETCH_SMALLER)
920 0 : return (a <= olda) ? (olda > b) : (a <= b);
921 :
922 : // XXXkt prob want log scale here i.e. 1.5 is closer to 1 than 0.5
923 0 : return NS_ABS(a - b) < NS_ABS(olda - b);
924 : }
925 :
926 : // We want to place the glyphs even when they don't fit at their
927 : // full extent, i.e., we may clip to tolerate a small amount of
928 : // overlap between the parts. This is important to cater for fonts
929 : // with long glues.
930 : static nscoord
931 0 : ComputeSizeFromParts(nsPresContext* aPresContext,
932 : nsGlyphCode* aGlyphs,
933 : nscoord* aSizes,
934 : nscoord aTargetSize)
935 : {
936 : enum {first, middle, last, glue};
937 : // Add the parts that cannot be left out.
938 0 : nscoord sum = 0;
939 0 : for (PRInt32 i = first; i <= last; i++) {
940 0 : if (aGlyphs[i] != aGlyphs[glue]) {
941 0 : sum += aSizes[i];
942 : }
943 : }
944 :
945 : // Determine how much is used in joins
946 0 : nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
947 0 : PRInt32 joins = aGlyphs[middle] == aGlyphs[glue] ? 1 : 2;
948 :
949 : // Pick a maximum size using a maximum number of glue glyphs that we are
950 : // prepared to draw for one character.
951 0 : const PRInt32 maxGlyphs = 1000;
952 :
953 : // This also takes into account the fact that, if the glue has no size,
954 : // then the character can't be lengthened.
955 0 : nscoord maxSize = sum - 2 * joins * oneDevPixel + maxGlyphs * aSizes[glue];
956 0 : if (maxSize < aTargetSize)
957 0 : return maxSize; // settle with the maximum size
958 :
959 : // Get the minimum allowable size using some flex.
960 0 : nscoord minSize = NSToCoordRound(NS_MATHML_DELIMITER_FACTOR * sum);
961 :
962 0 : if (minSize > aTargetSize)
963 0 : return minSize; // settle with the minimum size
964 :
965 : // Fill-up the target area
966 0 : return aTargetSize;
967 : }
968 :
969 : // Insert aFallbackFamilies before the first generic family in or at the end
970 : // of a CSS aFontName.
971 : static void
972 0 : AddFallbackFonts(nsAString& aFontName, const nsAString& aFallbackFamilies)
973 : {
974 0 : if (aFallbackFamilies.IsEmpty())
975 0 : return;
976 :
977 0 : if (aFontName.IsEmpty()) {
978 0 : return;
979 : }
980 :
981 : static const PRUnichar kSingleQuote = PRUnichar('\'');
982 : static const PRUnichar kDoubleQuote = PRUnichar('\"');
983 : static const PRUnichar kComma = PRUnichar(',');
984 :
985 : const PRUnichar *p_begin, *p_end;
986 0 : aFontName.BeginReading(p_begin);
987 0 : aFontName.EndReading(p_end);
988 :
989 0 : const PRUnichar *p = p_begin;
990 0 : const PRUnichar *p_name = nsnull;
991 0 : while (p < p_end) {
992 0 : while (nsCRT::IsAsciiSpace(*p))
993 0 : if (++p == p_end)
994 0 : goto insert;
995 :
996 0 : p_name = p;
997 0 : if (*p == kSingleQuote || *p == kDoubleQuote) {
998 : // quoted font family
999 0 : PRUnichar quoteMark = *p;
1000 0 : if (++p == p_end)
1001 0 : goto insert;
1002 :
1003 : // XXX What about CSS character escapes?
1004 0 : while (*p != quoteMark)
1005 0 : if (++p == p_end)
1006 0 : goto insert;
1007 :
1008 0 : while (++p != p_end && *p != kComma)
1009 0 : /* nothing */ ;
1010 :
1011 : } else {
1012 : // unquoted font family
1013 0 : const PRUnichar *nameStart = p;
1014 0 : while (++p != p_end && *p != kComma)
1015 : /* nothing */ ;
1016 :
1017 0 : nsAutoString family;
1018 0 : family = Substring(nameStart, p);
1019 0 : family.CompressWhitespace(false, true);
1020 :
1021 : PRUint8 generic;
1022 0 : nsFont::GetGenericID(family, &generic);
1023 0 : if (generic != kGenericFont_NONE)
1024 : goto insert;
1025 : }
1026 :
1027 0 : ++p; // may advance past p_end
1028 : }
1029 :
1030 0 : aFontName.Append(NS_LITERAL_STRING(",") + aFallbackFamilies);
1031 0 : return;
1032 :
1033 : insert:
1034 0 : if (p_name) {
1035 0 : aFontName.Insert(aFallbackFamilies + NS_LITERAL_STRING(","),
1036 0 : p_name - p_begin);
1037 : }
1038 : else { // whitespace or empty
1039 0 : aFontName = aFallbackFamilies;
1040 : }
1041 : }
1042 :
1043 : // Update the font and rendering context if there is a family change
1044 : static void
1045 0 : SetFontFamily(nsStyleContext* aStyleContext,
1046 : nsRenderingContext& aRenderingContext,
1047 : nsFont& aFont,
1048 : const nsGlyphTable* aGlyphTable,
1049 : const nsGlyphCode& aGlyphCode,
1050 : const nsAString& aDefaultFamily)
1051 : {
1052 : const nsAString& family =
1053 0 : aGlyphCode.font ? aGlyphTable->FontNameFor(aGlyphCode) : aDefaultFamily;
1054 0 : if (! family.Equals(aFont.name)) {
1055 0 : aFont.name = family;
1056 0 : nsRefPtr<nsFontMetrics> fm;
1057 : aRenderingContext.DeviceContext()->GetMetricsFor(aFont,
1058 0 : aStyleContext->GetStyleFont()->mLanguage,
1059 : aStyleContext->PresContext()->GetUserFontSet(),
1060 0 : *getter_AddRefs(fm));
1061 0 : aRenderingContext.SetFont(fm);
1062 : }
1063 0 : }
1064 :
1065 0 : class nsMathMLChar::StretchEnumContext {
1066 : public:
1067 0 : StretchEnumContext(nsMathMLChar* aChar,
1068 : nsPresContext* aPresContext,
1069 : nsRenderingContext& aRenderingContext,
1070 : nsStretchDirection aStretchDirection,
1071 : nscoord aTargetSize,
1072 : PRUint32 aStretchHint,
1073 : nsBoundingMetrics& aStretchedMetrics,
1074 : const nsAString& aFamilies,
1075 : bool& aGlyphFound)
1076 : : mChar(aChar),
1077 : mPresContext(aPresContext),
1078 : mRenderingContext(aRenderingContext),
1079 : mDirection(aStretchDirection),
1080 : mTargetSize(aTargetSize),
1081 : mStretchHint(aStretchHint),
1082 : mBoundingMetrics(aStretchedMetrics),
1083 : mFamilies(aFamilies),
1084 : mTryVariants(true),
1085 : mTryParts(true),
1086 0 : mGlyphFound(aGlyphFound) {}
1087 :
1088 : static bool
1089 : EnumCallback(const nsString& aFamily, bool aGeneric, void *aData);
1090 :
1091 : private:
1092 : static bool
1093 : ResolverCallback (const nsAString& aFamily, void *aData);
1094 :
1095 : bool TryVariants(nsGlyphTable* aGlyphTable, const nsAString& aFamily);
1096 : bool TryParts(nsGlyphTable* aGlyphTable, const nsAString& aFamily);
1097 :
1098 : nsMathMLChar* mChar;
1099 : nsPresContext* mPresContext;
1100 : nsRenderingContext& mRenderingContext;
1101 : const nsStretchDirection mDirection;
1102 : const nscoord mTargetSize;
1103 : const PRUint32 mStretchHint;
1104 : nsBoundingMetrics& mBoundingMetrics;
1105 : // Font families to search
1106 : const nsAString& mFamilies;
1107 :
1108 : public:
1109 : bool mTryVariants;
1110 : bool mTryParts;
1111 :
1112 : private:
1113 : nsAutoTArray<nsGlyphTable*,16> mTablesTried;
1114 : nsGlyphTable* mGlyphTable; // for this callback
1115 : bool& mGlyphFound;
1116 : };
1117 :
1118 :
1119 : // 2. See if there are any glyphs of the appropriate size.
1120 : // Returns true if the size is OK, false to keep searching.
1121 : // Always updates the char if a better match is found.
1122 : bool
1123 0 : nsMathMLChar::StretchEnumContext::TryVariants(nsGlyphTable* aGlyphTable,
1124 : const nsAString& aFamily)
1125 : {
1126 : // Use our stretchy style context now that stretching is in progress
1127 0 : nsStyleContext *sc = mChar->mStyleContext;
1128 0 : nsFont font = sc->GetStyleFont()->mFont;
1129 : // Ensure mRenderingContext.SetFont will be called:
1130 0 : font.name.Truncate();
1131 :
1132 0 : bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
1133 0 : bool largeop = (NS_STRETCH_LARGEOP & mStretchHint) != 0;
1134 : bool largeopOnly =
1135 0 : largeop && (NS_STRETCH_VARIABLE_MASK & mStretchHint) == 0;
1136 0 : bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
1137 :
1138 : nscoord bestSize =
1139 : isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent
1140 0 : : mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
1141 0 : bool haveBetter = false;
1142 :
1143 : // start at size = 1 (size = 0 is the char at its normal size)
1144 0 : PRInt32 size = 1;
1145 : #ifdef NOISY_SEARCH
1146 : printf(" searching in %s ...\n",
1147 : NS_LossyConvertUTF16toASCII(aFamily).get());
1148 : #endif
1149 :
1150 : nsGlyphCode ch;
1151 0 : while ((ch = aGlyphTable->BigOf(mPresContext, mChar, size)).Exists()) {
1152 :
1153 0 : SetFontFamily(sc, mRenderingContext, font, aGlyphTable, ch, aFamily);
1154 :
1155 0 : NS_ASSERTION(maxWidth || ch.code[0] != mChar->mGlyph.code[0] ||
1156 : ch.code[1] != mChar->mGlyph.code[1] ||
1157 : !font.name.Equals(mChar->mFamily),
1158 : "glyph table incorrectly set -- duplicate found");
1159 :
1160 : nsBoundingMetrics bm = mRenderingContext.GetBoundingMetrics(ch.code,
1161 0 : ch.Length());
1162 : nscoord charSize =
1163 : isVertical ? bm.ascent + bm.descent
1164 0 : : bm.rightBearing - bm.leftBearing;
1165 :
1166 0 : if (largeopOnly ||
1167 0 : IsSizeBetter(charSize, bestSize, mTargetSize, mStretchHint)) {
1168 0 : mGlyphFound = true;
1169 0 : if (maxWidth) {
1170 : // IsSizeBetter() checked that charSize < maxsize;
1171 : // Leave ascent, descent, and bestsize as these contain maxsize.
1172 0 : if (mBoundingMetrics.width < bm.width)
1173 0 : mBoundingMetrics.width = bm.width;
1174 0 : if (mBoundingMetrics.leftBearing > bm.leftBearing)
1175 0 : mBoundingMetrics.leftBearing = bm.leftBearing;
1176 0 : if (mBoundingMetrics.rightBearing < bm.rightBearing)
1177 0 : mBoundingMetrics.rightBearing = bm.rightBearing;
1178 : // Continue to check other sizes unless largeopOnly
1179 0 : haveBetter = largeopOnly;
1180 : }
1181 : else {
1182 0 : mBoundingMetrics = bm;
1183 0 : haveBetter = true;
1184 0 : bestSize = charSize;
1185 0 : mChar->mGlyphTable = aGlyphTable;
1186 0 : mChar->mGlyph = ch;
1187 0 : mChar->mFamily = font.name;
1188 : }
1189 : #ifdef NOISY_SEARCH
1190 : printf(" size:%d Current best\n", size);
1191 : #endif
1192 : }
1193 : else {
1194 : #ifdef NOISY_SEARCH
1195 : printf(" size:%d Rejected!\n", size);
1196 : #endif
1197 0 : if (haveBetter)
1198 0 : break; // Not making an futher progress, stop searching
1199 : }
1200 :
1201 : // if largeopOnly is set, break now
1202 0 : if (largeopOnly) break;
1203 0 : ++size;
1204 : }
1205 :
1206 : return haveBetter &&
1207 0 : (largeopOnly || IsSizeOK(mPresContext, bestSize, mTargetSize, mStretchHint));
1208 : }
1209 :
1210 : // 3. Build by parts.
1211 : // Returns true if the size is OK, false to keep searching.
1212 : // Always updates the char if a better match is found.
1213 : bool
1214 0 : nsMathMLChar::StretchEnumContext::TryParts(nsGlyphTable* aGlyphTable,
1215 : const nsAString& aFamily)
1216 : {
1217 0 : if (!aGlyphTable->HasPartsOf(mPresContext, mChar))
1218 0 : return false; // to next table
1219 :
1220 : // See if this is a composite character /////////////////////////////////////
1221 0 : if (aGlyphTable->IsComposite(mPresContext, mChar)) {
1222 : // let the child chars do the job
1223 0 : nsBoundingMetrics compositeSize;
1224 : nsresult rv =
1225 : mChar->ComposeChildren(mPresContext, mRenderingContext, aGlyphTable,
1226 0 : mTargetSize, compositeSize, mStretchHint);
1227 : #ifdef NOISY_SEARCH
1228 : printf(" Composing %d chars in font %s %s!\n",
1229 : aGlyphTable->ChildCountOf(mPresContext, mChar),
1230 : NS_LossyConvertUTF16toASCII(fontName).get(),
1231 : NS_SUCCEEDED(rv)? "OK" : "Rejected");
1232 : #endif
1233 0 : if (NS_FAILED(rv))
1234 0 : return false; // to next table
1235 :
1236 : // all went well, painting will be delegated from now on to children
1237 0 : mChar->mGlyph = kNullGlyph; // this will tell paint to build by parts
1238 0 : mGlyphFound = true;
1239 0 : mChar->mGlyphTable = aGlyphTable;
1240 0 : mBoundingMetrics = compositeSize;
1241 0 : return true; // no more searching
1242 : }
1243 :
1244 : // See if the parts of this table fit in the desired space //////////////////
1245 :
1246 : // Use our stretchy style context now that stretching is in progress
1247 0 : nsFont font = mChar->mStyleContext->GetStyleFont()->mFont;
1248 : // Ensure mRenderingContext.SetFont will be called:
1249 0 : font.name.Truncate();
1250 :
1251 : // Compute the bounding metrics of all partial glyphs
1252 : nsGlyphCode chdata[4];
1253 0 : nsBoundingMetrics bmdata[4];
1254 : nscoord sizedata[4];
1255 0 : nsGlyphCode glue = aGlyphTable->GlueOf(mPresContext, mChar);
1256 :
1257 0 : bool isVertical = (mDirection == NS_STRETCH_DIRECTION_VERTICAL);
1258 0 : bool maxWidth = (NS_STRETCH_MAXWIDTH & mStretchHint) != 0;
1259 :
1260 0 : for (PRInt32 i = 0; i < 4; i++) {
1261 : nsGlyphCode ch;
1262 0 : switch (i) {
1263 0 : case 0: ch = aGlyphTable->TopOf(mPresContext, mChar); break;
1264 0 : case 1: ch = aGlyphTable->MiddleOf(mPresContext, mChar); break;
1265 0 : case 2: ch = aGlyphTable->BottomOf(mPresContext, mChar); break;
1266 0 : case 3: ch = glue; break;
1267 : }
1268 : // empty slots are filled with the glue if it is not null
1269 0 : if (!ch.Exists()) ch = glue;
1270 0 : chdata[i] = ch;
1271 0 : if (!ch.Exists()) {
1272 : // Null glue indicates that a rule will be drawn, which can stretch to
1273 : // fill any space. Leave bounding metrics at 0.
1274 0 : sizedata[i] = mTargetSize;
1275 : }
1276 : else {
1277 : SetFontFamily(mChar->mStyleContext, mRenderingContext,
1278 0 : font, aGlyphTable, ch, aFamily);
1279 : nsBoundingMetrics bm = mRenderingContext.GetBoundingMetrics(ch.code,
1280 0 : ch.Length());
1281 :
1282 : // TODO: For the generic Unicode table, ideally we should check that the
1283 : // glyphs are actually found and that they each come from the same
1284 : // font.
1285 0 : bmdata[i] = bm;
1286 : sizedata[i] = isVertical ? bm.ascent + bm.descent
1287 0 : : bm.rightBearing - bm.leftBearing;
1288 : }
1289 : }
1290 :
1291 : // Build by parts if we have successfully computed the
1292 : // bounding metrics of all parts.
1293 : nscoord computedSize = ComputeSizeFromParts(mPresContext, chdata, sizedata,
1294 0 : mTargetSize);
1295 :
1296 : nscoord currentSize =
1297 : isVertical ? mBoundingMetrics.ascent + mBoundingMetrics.descent
1298 0 : : mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
1299 :
1300 0 : if (!IsSizeBetter(computedSize, currentSize, mTargetSize, mStretchHint)) {
1301 : #ifdef NOISY_SEARCH
1302 : printf(" Font %s Rejected!\n",
1303 : NS_LossyConvertUTF16toASCII(fontName).get());
1304 : #endif
1305 0 : return false; // to next table
1306 : }
1307 :
1308 : #ifdef NOISY_SEARCH
1309 : printf(" Font %s Current best!\n",
1310 : NS_LossyConvertUTF16toASCII(fontName).get());
1311 : #endif
1312 :
1313 : // The computed size is the best we have found so far...
1314 : // now is the time to compute and cache our bounding metrics
1315 0 : if (isVertical) {
1316 : PRInt32 i;
1317 : nscoord lbearing;
1318 : nscoord rbearing;
1319 : nscoord width;
1320 0 : if (maxWidth) {
1321 0 : lbearing = mBoundingMetrics.leftBearing;
1322 0 : rbearing = mBoundingMetrics.rightBearing;
1323 0 : width = mBoundingMetrics.width;
1324 0 : i = 0;
1325 : }
1326 : else {
1327 0 : lbearing = bmdata[0].leftBearing;
1328 0 : rbearing = bmdata[0].rightBearing;
1329 0 : width = bmdata[0].width;
1330 0 : i = 1;
1331 : }
1332 0 : for (; i < 4; i++) {
1333 0 : const nsBoundingMetrics& bm = bmdata[i];
1334 0 : if (width < bm.width) width = bm.width;
1335 0 : if (lbearing > bm.leftBearing) lbearing = bm.leftBearing;
1336 0 : if (rbearing < bm.rightBearing) rbearing = bm.rightBearing;
1337 : }
1338 0 : mBoundingMetrics.width = width;
1339 : // When maxWidth, updating ascent and descent indicates that no characters
1340 : // larger than this character's minimum size need to be checked as they
1341 : // will not be used.
1342 0 : mBoundingMetrics.ascent = bmdata[0].ascent; // not used except with descent for height
1343 0 : mBoundingMetrics.descent = computedSize - mBoundingMetrics.ascent;
1344 0 : mBoundingMetrics.leftBearing = lbearing;
1345 0 : mBoundingMetrics.rightBearing = rbearing;
1346 : }
1347 : else {
1348 0 : nscoord ascent = bmdata[0].ascent;
1349 0 : nscoord descent = bmdata[0].descent;
1350 0 : for (PRInt32 i = 1; i < 4; i++) {
1351 0 : const nsBoundingMetrics& bm = bmdata[i];
1352 0 : if (ascent < bm.ascent) ascent = bm.ascent;
1353 0 : if (descent < bm.descent) descent = bm.descent;
1354 : }
1355 0 : mBoundingMetrics.width = computedSize;
1356 0 : mBoundingMetrics.ascent = ascent;
1357 0 : mBoundingMetrics.descent = descent;
1358 0 : mBoundingMetrics.leftBearing = 0;
1359 0 : mBoundingMetrics.rightBearing = computedSize;
1360 : }
1361 0 : mGlyphFound = true;
1362 0 : if (maxWidth)
1363 0 : return false; // Continue to check other sizes
1364 :
1365 : // reset
1366 0 : mChar->mGlyph = kNullGlyph; // this will tell paint to build by parts
1367 0 : mChar->mGlyphTable = aGlyphTable;
1368 0 : mChar->mFamily = aFamily;
1369 :
1370 0 : return IsSizeOK(mPresContext, computedSize, mTargetSize, mStretchHint);
1371 : }
1372 :
1373 : // This is only called for glyph table corresponding to a family that exists.
1374 : // See if the table has a glyph that matches the container
1375 : bool
1376 0 : nsMathMLChar::StretchEnumContext::ResolverCallback (const nsAString& aFamily,
1377 : void *aData)
1378 : {
1379 0 : StretchEnumContext* context = static_cast<StretchEnumContext*>(aData);
1380 0 : nsGlyphTable* glyphTable = context->mGlyphTable;
1381 :
1382 : // Only try this table once.
1383 0 : context->mTablesTried.AppendElement(glyphTable);
1384 :
1385 : // If the unicode table is being used, then search all font families. If a
1386 : // special table is being used then the font in this family should have the
1387 : // specified glyphs.
1388 : const nsAString& family = glyphTable == &gGlyphTableList->mUnicodeTable ?
1389 0 : context->mFamilies : aFamily;
1390 :
1391 0 : if(context->mTryVariants) {
1392 0 : bool isOK = context->TryVariants(glyphTable, family);
1393 0 : if (isOK)
1394 0 : return false; // no need to continue
1395 : }
1396 :
1397 0 : if(context->mTryParts) {
1398 0 : bool isOK = context->TryParts(glyphTable, family);
1399 0 : if (isOK)
1400 0 : return false; // no need to continue
1401 : }
1402 0 : return true;
1403 : }
1404 :
1405 : // This is called for each family, whether it exists or not
1406 : bool
1407 0 : nsMathMLChar::StretchEnumContext::EnumCallback(const nsString& aFamily,
1408 : bool aGeneric, void *aData)
1409 : {
1410 0 : StretchEnumContext* context = static_cast<StretchEnumContext*>(aData);
1411 :
1412 : // See if there is a special table for the family, but always use the
1413 : // Unicode table for generic fonts.
1414 : nsGlyphTable* glyphTable = aGeneric ?
1415 0 : &gGlyphTableList->mUnicodeTable : gGlyphTableList->GetGlyphTableFor(aFamily);
1416 :
1417 0 : if (context->mTablesTried.Contains(glyphTable))
1418 0 : return true; // already tried this one
1419 :
1420 0 : context->mGlyphTable = glyphTable;
1421 :
1422 0 : if (aGeneric)
1423 0 : return ResolverCallback(aFamily, aData);
1424 :
1425 : bool aborted;
1426 0 : gfxPlatform *pf = gfxPlatform::GetPlatform();
1427 : nsresult rv =
1428 0 : pf->ResolveFontName(aFamily, ResolverCallback, aData, aborted);
1429 0 : return NS_SUCCEEDED(rv) && !aborted; // true means continue
1430 : }
1431 :
1432 : nsresult
1433 0 : nsMathMLChar::StretchInternal(nsPresContext* aPresContext,
1434 : nsRenderingContext& aRenderingContext,
1435 : nsStretchDirection& aStretchDirection,
1436 : const nsBoundingMetrics& aContainerSize,
1437 : nsBoundingMetrics& aDesiredStretchSize,
1438 : PRUint32 aStretchHint,
1439 : // These are currently only used when
1440 : // aStretchHint & NS_STRETCH_MAXWIDTH:
1441 : float aMaxSize,
1442 : bool aMaxSizeIsAbsolute)
1443 : {
1444 : // if we have been called before, and we didn't actually stretch, our
1445 : // direction may have been set to NS_STRETCH_DIRECTION_UNSUPPORTED.
1446 : // So first set our direction back to its instrinsic value
1447 0 : nsStretchDirection direction = nsMathMLOperators::GetStretchyDirection(mData);
1448 :
1449 : // Set default font and get the default bounding metrics
1450 : // mStyleContext is a leaf context used only when stretching happens.
1451 : // For the base size, the default font should come from the parent context
1452 0 : nsFont font = mStyleContext->GetParent()->GetStyleFont()->mFont;
1453 :
1454 : // Override with specific fonts if applicable for this character
1455 0 : nsAutoString families;
1456 0 : if (GetFontExtensionPref(mData[0], eExtension_base, families)) {
1457 0 : font.name = families;
1458 : }
1459 :
1460 : // Don't modify this nsMathMLChar when doing GetMaxWidth()
1461 0 : bool maxWidth = (NS_STRETCH_MAXWIDTH & aStretchHint) != 0;
1462 0 : if (!maxWidth) {
1463 : // Record the families in case there is no stretch. But don't bother
1464 : // storing families when they are just those from the StyleContext.
1465 0 : mFamily = families;
1466 : }
1467 :
1468 0 : nsRefPtr<nsFontMetrics> fm;
1469 : aRenderingContext.DeviceContext()->GetMetricsFor(font,
1470 0 : mStyleContext->GetStyleFont()->mLanguage,
1471 0 : aPresContext->GetUserFontSet(), *getter_AddRefs(fm));
1472 0 : aRenderingContext.SetFont(fm);
1473 : aDesiredStretchSize =
1474 0 : aRenderingContext.GetBoundingMetrics(mData.get(), PRUint32(mData.Length()));
1475 :
1476 0 : if (!maxWidth) {
1477 0 : mUnscaledAscent = aDesiredStretchSize.ascent;
1478 : }
1479 :
1480 : ////////////////////////////////////////////////////////////////////////////////////
1481 : // 1. Check the common situations where stretching is not actually needed
1482 : ////////////////////////////////////////////////////////////////////////////////////
1483 :
1484 : // quick return if there is nothing special about this char
1485 0 : if ((aStretchDirection != direction &&
1486 : aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT) ||
1487 : (aStretchHint & ~NS_STRETCH_MAXWIDTH) == NS_STRETCH_NONE) {
1488 0 : mDirection = NS_STRETCH_DIRECTION_UNSUPPORTED;
1489 0 : return NS_OK;
1490 : }
1491 :
1492 : // if no specified direction, attempt to stretch in our preferred direction
1493 0 : if (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT) {
1494 0 : aStretchDirection = direction;
1495 : }
1496 :
1497 : // see if this is a particular largeop or largeopOnly request
1498 0 : bool largeop = (NS_STRETCH_LARGEOP & aStretchHint) != 0;
1499 0 : bool stretchy = (NS_STRETCH_VARIABLE_MASK & aStretchHint) != 0;
1500 0 : bool largeopOnly = largeop && !stretchy;
1501 :
1502 0 : bool isVertical = (direction == NS_STRETCH_DIRECTION_VERTICAL);
1503 :
1504 : nscoord targetSize =
1505 : isVertical ? aContainerSize.ascent + aContainerSize.descent
1506 0 : : aContainerSize.rightBearing - aContainerSize.leftBearing;
1507 :
1508 0 : if (maxWidth) {
1509 : // See if it is only necessary to consider glyphs up to some maximum size.
1510 : // Set the current height to the maximum size, and set aStretchHint to
1511 : // NS_STRETCH_SMALLER if the size is variable, so that only smaller sizes
1512 : // are considered. targetSize from GetMaxWidth() is 0.
1513 0 : if (stretchy) {
1514 : // variable size stretch - consider all sizes < maxsize
1515 : aStretchHint =
1516 0 : (aStretchHint & ~NS_STRETCH_VARIABLE_MASK) | NS_STRETCH_SMALLER;
1517 : }
1518 :
1519 : // Use NS_MATHML_DELIMITER_FACTOR to allow some slightly larger glyphs as
1520 : // maxsize is not enforced exactly.
1521 0 : if (aMaxSize == NS_MATHML_OPERATOR_SIZE_INFINITY) {
1522 0 : aDesiredStretchSize.ascent = nscoord_MAX;
1523 0 : aDesiredStretchSize.descent = 0;
1524 : }
1525 : else {
1526 0 : nscoord height = aDesiredStretchSize.ascent + aDesiredStretchSize.descent;
1527 0 : if (height == 0) {
1528 0 : if (aMaxSizeIsAbsolute) {
1529 : aDesiredStretchSize.ascent =
1530 0 : NSToCoordRound(aMaxSize / NS_MATHML_DELIMITER_FACTOR);
1531 0 : aDesiredStretchSize.descent = 0;
1532 : }
1533 : // else: leave height as 0
1534 : }
1535 : else {
1536 0 : float scale = aMaxSizeIsAbsolute ? aMaxSize / height : aMaxSize;
1537 0 : scale /= NS_MATHML_DELIMITER_FACTOR;
1538 : aDesiredStretchSize.ascent =
1539 0 : NSToCoordRound(scale * aDesiredStretchSize.ascent);
1540 : aDesiredStretchSize.descent =
1541 0 : NSToCoordRound(scale * aDesiredStretchSize.descent);
1542 : }
1543 : }
1544 : }
1545 :
1546 0 : nsBoundingMetrics initialSize = aDesiredStretchSize;
1547 : nscoord charSize =
1548 : isVertical ? initialSize.ascent + initialSize.descent
1549 0 : : initialSize.rightBearing - initialSize.leftBearing;
1550 :
1551 0 : bool done = (mGlyphTable ? false : true);
1552 :
1553 0 : if (!done && !maxWidth && !largeop) {
1554 : // Doing Stretch() not GetMaxWidth(),
1555 : // and not a largeop in display mode; we're done if size fits
1556 0 : if ((targetSize <= 0) ||
1557 : ((isVertical && charSize >= targetSize) ||
1558 0 : IsSizeOK(aPresContext, charSize, targetSize, aStretchHint)))
1559 0 : done = true;
1560 : }
1561 :
1562 : ////////////////////////////////////////////////////////////////////////////////////
1563 : // 2/3. Search for a glyph or set of part glyphs of appropriate size
1564 : ////////////////////////////////////////////////////////////////////////////////////
1565 :
1566 0 : bool glyphFound = false;
1567 0 : nsAutoString cssFamilies;
1568 :
1569 0 : if (!done) {
1570 0 : font = mStyleContext->GetStyleFont()->mFont;
1571 0 : cssFamilies = font.name;
1572 : }
1573 :
1574 : // See if there are preferred fonts for the variants of this char
1575 0 : if (!done && GetFontExtensionPref(mData[0], eExtension_variants, families)) {
1576 0 : font.name = families;
1577 :
1578 : StretchEnumContext enumData(this, aPresContext, aRenderingContext,
1579 : aStretchDirection, targetSize, aStretchHint,
1580 0 : aDesiredStretchSize, font.name, glyphFound);
1581 0 : enumData.mTryParts = false;
1582 :
1583 0 : done = !font.EnumerateFamilies(StretchEnumContext::EnumCallback, &enumData);
1584 : }
1585 :
1586 : // See if there are preferred fonts for the parts of this char
1587 0 : if (!done && !largeopOnly
1588 0 : && GetFontExtensionPref(mData[0], eExtension_parts, families)) {
1589 0 : font.name = families;
1590 :
1591 : StretchEnumContext enumData(this, aPresContext, aRenderingContext,
1592 : aStretchDirection, targetSize, aStretchHint,
1593 0 : aDesiredStretchSize, font.name, glyphFound);
1594 0 : enumData.mTryVariants = false;
1595 :
1596 0 : done = !font.EnumerateFamilies(StretchEnumContext::EnumCallback, &enumData);
1597 : }
1598 :
1599 0 : if (!done) { // normal case
1600 : // Use the css font-family but add preferred fallback fonts.
1601 0 : font.name = cssFamilies;
1602 0 : NS_NAMED_LITERAL_CSTRING(defaultKey, "font.mathfont-family");
1603 0 : nsAdoptingString fallbackFonts = Preferences::GetString(defaultKey.get());
1604 0 : if (!fallbackFonts.IsEmpty()) {
1605 0 : AddFallbackFonts(font.name, fallbackFonts);
1606 : }
1607 :
1608 : #ifdef NOISY_SEARCH
1609 : printf("Searching in "%s" for a glyph of appropriate size for: 0x%04X:%c\n",
1610 : font.name, mData[0], mData[0]&0x00FF);
1611 : #endif
1612 : StretchEnumContext enumData(this, aPresContext, aRenderingContext,
1613 : aStretchDirection, targetSize, aStretchHint,
1614 0 : aDesiredStretchSize, font.name, glyphFound);
1615 0 : enumData.mTryParts = !largeopOnly;
1616 :
1617 0 : font.EnumerateFamilies(StretchEnumContext::EnumCallback, &enumData);
1618 : }
1619 :
1620 0 : if (!maxWidth) {
1621 : // Now, we know how we are going to draw the char. Update the member
1622 : // variables accordingly.
1623 0 : mDrawNormal = !glyphFound;
1624 0 : mUnscaledAscent = aDesiredStretchSize.ascent;
1625 : }
1626 :
1627 : // stretchy character
1628 0 : if (stretchy) {
1629 0 : if (isVertical) {
1630 : float scale =
1631 : float(aContainerSize.ascent + aContainerSize.descent) /
1632 0 : (aDesiredStretchSize.ascent + aDesiredStretchSize.descent);
1633 0 : if (!largeop || scale > 1.0) {
1634 : // make the character match the desired height.
1635 0 : if (!maxWidth) {
1636 0 : mScaleY *= scale;
1637 : }
1638 0 : aDesiredStretchSize.ascent *= scale;
1639 0 : aDesiredStretchSize.descent *= scale;
1640 : }
1641 : } else {
1642 : float scale =
1643 : float(aContainerSize.rightBearing - aContainerSize.leftBearing) /
1644 0 : (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing);
1645 0 : if (!largeop || scale > 1.0) {
1646 : // make the character match the desired width.
1647 0 : if (!maxWidth) {
1648 0 : mScaleX *= scale;
1649 : }
1650 0 : aDesiredStretchSize.leftBearing *= scale;
1651 0 : aDesiredStretchSize.rightBearing *= scale;
1652 0 : aDesiredStretchSize.width *= scale;
1653 : }
1654 : }
1655 : }
1656 :
1657 : // We do not have a char variant for this largeop in display mode, so we
1658 : // apply a scale transform to the base char.
1659 0 : if (!glyphFound && largeop) {
1660 : float scale;
1661 0 : float largeopFactor = M_SQRT2;
1662 :
1663 : // increase the width if it is not largeopFactor times larger
1664 : // than the initial one.
1665 0 : if ((aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing) <
1666 : largeopFactor * (initialSize.rightBearing - initialSize.leftBearing)) {
1667 : scale = (largeopFactor *
1668 : (initialSize.rightBearing - initialSize.leftBearing)) /
1669 0 : (aDesiredStretchSize.rightBearing - aDesiredStretchSize.leftBearing);
1670 0 : if (!maxWidth) {
1671 0 : mScaleX *= scale;
1672 : }
1673 0 : aDesiredStretchSize.leftBearing *= scale;
1674 0 : aDesiredStretchSize.rightBearing *= scale;
1675 0 : aDesiredStretchSize.width *= scale;
1676 : }
1677 :
1678 : // increase the height if it is not largeopFactor times larger
1679 : // than the initial one.
1680 0 : if (NS_STRETCH_INTEGRAL & aStretchHint) {
1681 : // integrals are drawn taller
1682 0 : largeopFactor = 2.0;
1683 : }
1684 0 : if ((aDesiredStretchSize.ascent + aDesiredStretchSize.descent) <
1685 : largeopFactor * (initialSize.ascent + initialSize.descent)) {
1686 : scale = (largeopFactor *
1687 : (initialSize.ascent + initialSize.descent)) /
1688 0 : (aDesiredStretchSize.ascent + aDesiredStretchSize.descent);
1689 0 : if (!maxWidth) {
1690 0 : mScaleY *= scale;
1691 : }
1692 0 : aDesiredStretchSize.ascent *= scale;
1693 0 : aDesiredStretchSize.descent *= scale;
1694 : }
1695 : }
1696 :
1697 0 : return NS_OK;
1698 : }
1699 :
1700 : nsresult
1701 0 : nsMathMLChar::Stretch(nsPresContext* aPresContext,
1702 : nsRenderingContext& aRenderingContext,
1703 : nsStretchDirection aStretchDirection,
1704 : const nsBoundingMetrics& aContainerSize,
1705 : nsBoundingMetrics& aDesiredStretchSize,
1706 : PRUint32 aStretchHint,
1707 : bool aRTL)
1708 : {
1709 0 : NS_ASSERTION(!(aStretchHint &
1710 : ~(NS_STRETCH_VARIABLE_MASK | NS_STRETCH_LARGEOP |
1711 : NS_STRETCH_INTEGRAL)),
1712 : "Unexpected stretch flags");
1713 :
1714 0 : mDrawNormal = true;
1715 0 : mMirrored = aRTL && nsMathMLOperators::IsMirrorableOperator(mData);
1716 0 : mScaleY = mScaleX = 1.0;
1717 0 : mDirection = aStretchDirection;
1718 : nsresult rv =
1719 : StretchInternal(aPresContext, aRenderingContext, mDirection,
1720 0 : aContainerSize, aDesiredStretchSize, aStretchHint);
1721 :
1722 : // Record the metrics
1723 0 : mBoundingMetrics = aDesiredStretchSize;
1724 :
1725 0 : return rv;
1726 : }
1727 :
1728 : // What happens here is that the StretchInternal algorithm is used but
1729 : // modified by passing the NS_STRETCH_MAXWIDTH stretch hint. That causes
1730 : // StretchInternal to return horizontal bounding metrics that are the maximum
1731 : // that might be returned from a Stretch.
1732 : //
1733 : // In order to avoid considering widths of some characters in fonts that will
1734 : // not be used for any stretch size, StretchInternal sets the initial height
1735 : // to infinity and looks for any characters smaller than this height. When a
1736 : // character built from parts is considered, (it will be used by Stretch for
1737 : // any characters greater than its minimum size, so) the height is set to its
1738 : // minimum size, so that only widths of smaller subsequent characters are
1739 : // considered.
1740 : nscoord
1741 0 : nsMathMLChar::GetMaxWidth(nsPresContext* aPresContext,
1742 : nsRenderingContext& aRenderingContext,
1743 : PRUint32 aStretchHint,
1744 : float aMaxSize, bool aMaxSizeIsAbsolute)
1745 : {
1746 0 : nsBoundingMetrics bm;
1747 0 : nsStretchDirection direction = NS_STRETCH_DIRECTION_VERTICAL;
1748 0 : const nsBoundingMetrics container; // zero target size
1749 :
1750 : StretchInternal(aPresContext, aRenderingContext, direction, container,
1751 0 : bm, aStretchHint | NS_STRETCH_MAXWIDTH);
1752 :
1753 0 : return NS_MAX(bm.width, bm.rightBearing) - NS_MIN(0, bm.leftBearing);
1754 : }
1755 :
1756 : nsresult
1757 0 : nsMathMLChar::ComposeChildren(nsPresContext* aPresContext,
1758 : nsRenderingContext& aRenderingContext,
1759 : nsGlyphTable* aGlyphTable,
1760 : nscoord aTargetSize,
1761 : nsBoundingMetrics& aCompositeSize,
1762 : PRUint32 aStretchHint)
1763 : {
1764 0 : PRInt32 i = 0;
1765 : nsMathMLChar* child;
1766 0 : PRInt32 count = aGlyphTable->ChildCountOf(aPresContext, this);
1767 0 : NS_ASSERTION(count, "something is wrong somewhere");
1768 0 : if (!count) return NS_ERROR_FAILURE;
1769 : // if we haven't been here before, create the linked list of children now
1770 : // otherwise, use what we have, adding more children as needed or deleting the extra
1771 0 : nsMathMLChar* last = this;
1772 0 : while ((i < count) && last->mSibling) {
1773 0 : i++;
1774 0 : last = last->mSibling;
1775 : }
1776 0 : while (i < count) {
1777 0 : child = new nsMathMLChar(this);
1778 0 : last->mSibling = child;
1779 0 : last = child;
1780 0 : i++;
1781 : }
1782 0 : if (last->mSibling) {
1783 0 : delete last->mSibling;
1784 0 : last->mSibling = nsnull;
1785 : }
1786 : // let children stretch in an equal space
1787 0 : nsBoundingMetrics splitSize;
1788 0 : if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
1789 0 : splitSize.width = aTargetSize / count;
1790 : else {
1791 0 : splitSize.ascent = aTargetSize / (count * 2);
1792 0 : splitSize.descent = splitSize.ascent;
1793 : }
1794 0 : nscoord dx = 0, dy = 0;
1795 0 : for (i = 0, child = mSibling; child; child = child->mSibling, i++) {
1796 : // child chars should just inherit our values - which may change between calls...
1797 0 : child->mData = mData;
1798 0 : child->mDirection = mDirection;
1799 0 : child->mStyleContext = mStyleContext;
1800 0 : child->mGlyphTable = aGlyphTable; // the child is associated to this table
1801 0 : child->mMirrored = mMirrored;
1802 : // there goes the Stretch() ...
1803 0 : nsBoundingMetrics childSize;
1804 : nsresult rv = child->Stretch(aPresContext, aRenderingContext, mDirection,
1805 0 : splitSize, childSize, aStretchHint, mMirrored);
1806 : // check if something went wrong or the child couldn't fit in the alloted space
1807 0 : if (NS_FAILED(rv) || (NS_STRETCH_DIRECTION_UNSUPPORTED == child->mDirection)) {
1808 0 : delete mSibling; // don't leave a dangling list behind ...
1809 0 : mSibling = nsnull;
1810 0 : return NS_ERROR_FAILURE;
1811 : }
1812 0 : child->SetRect(nsRect(dx, dy, childSize.width, childSize.ascent+childSize.descent));
1813 0 : if (0 == i)
1814 0 : aCompositeSize = childSize;
1815 : else {
1816 0 : if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
1817 0 : aCompositeSize += childSize;
1818 : else {
1819 0 : aCompositeSize.descent += childSize.ascent + childSize.descent;
1820 0 : if (aCompositeSize.leftBearing > childSize.leftBearing)
1821 0 : aCompositeSize.leftBearing = childSize.leftBearing;
1822 0 : if (aCompositeSize.rightBearing < childSize.rightBearing)
1823 0 : aCompositeSize.rightBearing = childSize.rightBearing;
1824 : }
1825 : }
1826 0 : if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
1827 0 : dx += childSize.width;
1828 : else
1829 0 : dy += childSize.ascent + childSize.descent;
1830 : }
1831 0 : return NS_OK;
1832 : }
1833 :
1834 : class nsDisplayMathMLSelectionRect : public nsDisplayItem {
1835 : public:
1836 0 : nsDisplayMathMLSelectionRect(nsDisplayListBuilder* aBuilder,
1837 : nsIFrame* aFrame, const nsRect& aRect)
1838 0 : : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
1839 0 : MOZ_COUNT_CTOR(nsDisplayMathMLSelectionRect);
1840 0 : }
1841 : #ifdef NS_BUILD_REFCNT_LOGGING
1842 0 : virtual ~nsDisplayMathMLSelectionRect() {
1843 0 : MOZ_COUNT_DTOR(nsDisplayMathMLSelectionRect);
1844 0 : }
1845 : #endif
1846 :
1847 : virtual void Paint(nsDisplayListBuilder* aBuilder,
1848 : nsRenderingContext* aCtx);
1849 0 : NS_DISPLAY_DECL_NAME("MathMLSelectionRect", TYPE_MATHML_SELECTION_RECT)
1850 : private:
1851 : nsRect mRect;
1852 : };
1853 :
1854 0 : void nsDisplayMathMLSelectionRect::Paint(nsDisplayListBuilder* aBuilder,
1855 : nsRenderingContext* aCtx)
1856 : {
1857 : // get color to use for selection from the look&feel object
1858 : nscolor bgColor =
1859 : LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectBackground,
1860 0 : NS_RGB(0, 0, 0));
1861 0 : aCtx->SetColor(bgColor);
1862 0 : aCtx->FillRect(mRect + ToReferenceFrame());
1863 0 : }
1864 :
1865 : class nsDisplayMathMLCharBackground : public nsDisplayItem {
1866 : public:
1867 0 : nsDisplayMathMLCharBackground(nsDisplayListBuilder* aBuilder,
1868 : nsIFrame* aFrame, const nsRect& aRect,
1869 : nsStyleContext* aStyleContext)
1870 0 : : nsDisplayItem(aBuilder, aFrame), mStyleContext(aStyleContext), mRect(aRect) {
1871 0 : MOZ_COUNT_CTOR(nsDisplayMathMLCharBackground);
1872 0 : }
1873 : #ifdef NS_BUILD_REFCNT_LOGGING
1874 0 : virtual ~nsDisplayMathMLCharBackground() {
1875 0 : MOZ_COUNT_DTOR(nsDisplayMathMLCharBackground);
1876 0 : }
1877 : #endif
1878 :
1879 : virtual void Paint(nsDisplayListBuilder* aBuilder,
1880 : nsRenderingContext* aCtx);
1881 0 : NS_DISPLAY_DECL_NAME("MathMLCharBackground", TYPE_MATHML_CHAR_BACKGROUND)
1882 : private:
1883 : nsStyleContext* mStyleContext;
1884 : nsRect mRect;
1885 : };
1886 :
1887 0 : void nsDisplayMathMLCharBackground::Paint(nsDisplayListBuilder* aBuilder,
1888 : nsRenderingContext* aCtx)
1889 : {
1890 0 : const nsStyleBorder* border = mStyleContext->GetStyleBorder();
1891 0 : nsRect rect(mRect + ToReferenceFrame());
1892 : nsCSSRendering::PaintBackgroundWithSC(mFrame->PresContext(), *aCtx, mFrame,
1893 : mVisibleRect, rect,
1894 : mStyleContext, *border,
1895 0 : aBuilder->GetBackgroundPaintFlags());
1896 0 : }
1897 :
1898 : class nsDisplayMathMLCharForeground : public nsDisplayItem {
1899 : public:
1900 0 : nsDisplayMathMLCharForeground(nsDisplayListBuilder* aBuilder,
1901 : nsIFrame* aFrame, nsMathMLChar* aChar,
1902 : bool aIsSelected)
1903 0 : : nsDisplayItem(aBuilder, aFrame), mChar(aChar), mIsSelected(aIsSelected) {
1904 0 : MOZ_COUNT_CTOR(nsDisplayMathMLCharForeground);
1905 0 : }
1906 : #ifdef NS_BUILD_REFCNT_LOGGING
1907 0 : virtual ~nsDisplayMathMLCharForeground() {
1908 0 : MOZ_COUNT_DTOR(nsDisplayMathMLCharForeground);
1909 0 : }
1910 : #endif
1911 :
1912 0 : virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) {
1913 0 : nsRect rect;
1914 0 : mChar->GetRect(rect);
1915 0 : nsPoint offset = ToReferenceFrame() + rect.TopLeft();
1916 0 : nsBoundingMetrics bm;
1917 0 : mChar->GetBoundingMetrics(bm);
1918 : return nsRect(offset.x + bm.leftBearing, offset.y,
1919 0 : bm.rightBearing - bm.leftBearing, bm.ascent + bm.descent);
1920 : }
1921 :
1922 0 : virtual void Paint(nsDisplayListBuilder* aBuilder,
1923 : nsRenderingContext* aCtx)
1924 : {
1925 : mChar->PaintForeground(mFrame->PresContext(), *aCtx,
1926 0 : ToReferenceFrame(), mIsSelected);
1927 0 : }
1928 :
1929 0 : NS_DISPLAY_DECL_NAME("MathMLCharForeground", TYPE_MATHML_CHAR_FOREGROUND)
1930 :
1931 0 : virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder)
1932 : {
1933 0 : return GetBounds(aBuilder);
1934 : }
1935 :
1936 : private:
1937 : nsMathMLChar* mChar;
1938 : bool mIsSelected;
1939 : };
1940 :
1941 : #ifdef NS_DEBUG
1942 : class nsDisplayMathMLCharDebug : public nsDisplayItem {
1943 : public:
1944 : nsDisplayMathMLCharDebug(nsDisplayListBuilder* aBuilder,
1945 : nsIFrame* aFrame, const nsRect& aRect)
1946 : : nsDisplayItem(aBuilder, aFrame), mRect(aRect) {
1947 : MOZ_COUNT_CTOR(nsDisplayMathMLCharDebug);
1948 : }
1949 : #ifdef NS_BUILD_REFCNT_LOGGING
1950 0 : virtual ~nsDisplayMathMLCharDebug() {
1951 0 : MOZ_COUNT_DTOR(nsDisplayMathMLCharDebug);
1952 0 : }
1953 : #endif
1954 :
1955 : virtual void Paint(nsDisplayListBuilder* aBuilder,
1956 : nsRenderingContext* aCtx);
1957 0 : NS_DISPLAY_DECL_NAME("MathMLCharDebug", TYPE_MATHML_CHAR_DEBUG)
1958 :
1959 : private:
1960 : nsRect mRect;
1961 : };
1962 :
1963 0 : void nsDisplayMathMLCharDebug::Paint(nsDisplayListBuilder* aBuilder,
1964 : nsRenderingContext* aCtx)
1965 : {
1966 : // for visual debug
1967 0 : PRIntn skipSides = 0;
1968 0 : nsPresContext* presContext = mFrame->PresContext();
1969 0 : nsStyleContext* styleContext = mFrame->GetStyleContext();
1970 0 : nsRect rect = mRect + ToReferenceFrame();
1971 : nsCSSRendering::PaintBorder(presContext, *aCtx, mFrame,
1972 0 : mVisibleRect, rect, styleContext, skipSides);
1973 : nsCSSRendering::PaintOutline(presContext, *aCtx, mFrame,
1974 0 : mVisibleRect, rect, styleContext);
1975 0 : }
1976 : #endif
1977 :
1978 :
1979 : nsresult
1980 0 : nsMathMLChar::Display(nsDisplayListBuilder* aBuilder,
1981 : nsIFrame* aForFrame,
1982 : const nsDisplayListSet& aLists,
1983 : const nsRect* aSelectedRect)
1984 : {
1985 0 : nsresult rv = NS_OK;
1986 0 : nsStyleContext* parentContext = mStyleContext->GetParent();
1987 0 : nsStyleContext* styleContext = mStyleContext;
1988 :
1989 0 : if (mDrawNormal) {
1990 : // normal drawing if there is nothing special about this char
1991 : // Set default context to the parent context
1992 0 : styleContext = parentContext;
1993 : }
1994 :
1995 0 : if (!styleContext->GetStyleVisibility()->IsVisible())
1996 0 : return NS_OK;
1997 :
1998 : // if the leaf style context that we use for stretchy chars has a background
1999 : // color we use it -- this feature is mostly used for testing and debugging
2000 : // purposes. Normally, users will set the background on the container frame.
2001 : // paint the selection background -- beware MathML frames overlap a lot
2002 0 : if (aSelectedRect && !aSelectedRect->IsEmpty()) {
2003 : rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
2004 0 : nsDisplayMathMLSelectionRect(aBuilder, aForFrame, *aSelectedRect));
2005 0 : NS_ENSURE_SUCCESS(rv, rv);
2006 : }
2007 0 : else if (mRect.width && mRect.height) {
2008 0 : const nsStyleBackground* backg = styleContext->GetStyleBackground();
2009 0 : if (styleContext != parentContext &&
2010 : NS_GET_A(backg->mBackgroundColor) > 0) {
2011 : rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
2012 0 : nsDisplayMathMLCharBackground(aBuilder, aForFrame, mRect, styleContext));
2013 0 : NS_ENSURE_SUCCESS(rv, rv);
2014 : }
2015 : //else
2016 : // our container frame will take care of painting its background
2017 :
2018 : #if defined(NS_DEBUG) && defined(SHOW_BOUNDING_BOX)
2019 : // for visual debug
2020 : rv = aLists.BorderBackground()->AppendToTop(new (aBuilder)
2021 : nsDisplayMathMLCharDebug(aBuilder, aForFrame, mRect));
2022 : NS_ENSURE_SUCCESS(rv, rv);
2023 : #endif
2024 : }
2025 : return aLists.Content()->AppendNewToTop(new (aBuilder)
2026 : nsDisplayMathMLCharForeground(aBuilder, aForFrame, this,
2027 0 : aSelectedRect && !aSelectedRect->IsEmpty()));
2028 : }
2029 :
2030 : void
2031 0 : nsMathMLChar::ApplyTransforms(nsRenderingContext& aRenderingContext, nsRect &r)
2032 : {
2033 : // apply the transforms
2034 0 : if (mMirrored) {
2035 0 : aRenderingContext.Translate(r.TopRight());
2036 0 : aRenderingContext.Scale(-mScaleX, mScaleY);
2037 : } else {
2038 0 : aRenderingContext.Translate(r.TopLeft());
2039 0 : aRenderingContext.Scale(mScaleX, mScaleY);
2040 : }
2041 :
2042 : // update the bounding rectangle.
2043 0 : r.x = r.y = 0;
2044 0 : r.width /= mScaleX;
2045 0 : r.height /= mScaleY;
2046 0 : }
2047 :
2048 : void
2049 0 : nsMathMLChar::PaintForeground(nsPresContext* aPresContext,
2050 : nsRenderingContext& aRenderingContext,
2051 : nsPoint aPt,
2052 : bool aIsSelected)
2053 : {
2054 0 : nsStyleContext* parentContext = mStyleContext->GetParent();
2055 0 : nsStyleContext* styleContext = mStyleContext;
2056 :
2057 0 : if (mDrawNormal) {
2058 : // normal drawing if there is nothing special about this char
2059 : // Set default context to the parent context
2060 0 : styleContext = parentContext;
2061 : }
2062 :
2063 : // Set color ...
2064 0 : nscolor fgColor = styleContext->GetVisitedDependentColor(eCSSProperty_color);
2065 0 : if (aIsSelected) {
2066 : // get color to use for selection from the look&feel object
2067 : fgColor = LookAndFeel::GetColor(LookAndFeel::eColorID_TextSelectForeground,
2068 0 : fgColor);
2069 : }
2070 0 : aRenderingContext.SetColor(fgColor);
2071 :
2072 0 : nsFont theFont(styleContext->GetStyleFont()->mFont);
2073 0 : if (! mFamily.IsEmpty()) {
2074 0 : theFont.name = mFamily;
2075 : }
2076 0 : nsRefPtr<nsFontMetrics> fm;
2077 : aRenderingContext.DeviceContext()->GetMetricsFor(theFont,
2078 0 : styleContext->GetStyleFont()->mLanguage,
2079 : aPresContext->GetUserFontSet(),
2080 0 : *getter_AddRefs(fm));
2081 0 : aRenderingContext.SetFont(fm);
2082 :
2083 0 : aRenderingContext.PushState();
2084 0 : nsRect r = mRect + aPt;
2085 0 : ApplyTransforms(aRenderingContext, r);
2086 :
2087 0 : if (mDrawNormal) {
2088 : // normal drawing if there is nothing special about this char ...
2089 : // Grab some metrics to adjust the placements ...
2090 0 : PRUint32 len = PRUint32(mData.Length());
2091 : //printf("Painting %04X like a normal char\n", mData[0]);
2092 : //aRenderingContext.SetColor(NS_RGB(255,0,0));
2093 0 : aRenderingContext.DrawString(mData.get(), len, 0, mUnscaledAscent);
2094 : }
2095 : else {
2096 : // Grab some metrics to adjust the placements ...
2097 : // if there is a glyph of appropriate size, paint that glyph
2098 0 : if (mGlyph.Exists()) {
2099 : //printf("Painting %04X with a glyph of appropriate size\n", mData[0]);
2100 : //aRenderingContext.SetColor(NS_RGB(0,0,255));
2101 0 : aRenderingContext.DrawString(mGlyph.code, mGlyph.Length(),
2102 0 : 0, mUnscaledAscent);
2103 : }
2104 : else { // paint by parts
2105 : //aRenderingContext.SetColor(NS_RGB(0,255,0));
2106 0 : if (NS_STRETCH_DIRECTION_VERTICAL == mDirection)
2107 : PaintVertically(aPresContext, aRenderingContext, theFont, styleContext,
2108 0 : mGlyphTable, r);
2109 0 : else if (NS_STRETCH_DIRECTION_HORIZONTAL == mDirection)
2110 : PaintHorizontally(aPresContext, aRenderingContext, theFont, styleContext,
2111 0 : mGlyphTable, r);
2112 : }
2113 : }
2114 :
2115 0 : aRenderingContext.PopState();
2116 0 : }
2117 :
2118 : /* =================================================================================
2119 : And now the helper routines that actually do the job of painting the char by parts
2120 : */
2121 :
2122 : class AutoPushClipRect {
2123 : nsRenderingContext& mCtx;
2124 : public:
2125 0 : AutoPushClipRect(nsRenderingContext& aCtx, const nsRect& aRect)
2126 0 : : mCtx(aCtx) {
2127 0 : mCtx.PushState();
2128 0 : mCtx.IntersectClip(aRect);
2129 0 : }
2130 0 : ~AutoPushClipRect() {
2131 0 : mCtx.PopState();
2132 0 : }
2133 : };
2134 :
2135 : static nsPoint
2136 0 : SnapToDevPixels(const gfxContext* aThebesContext, PRInt32 aAppUnitsPerGfxUnit,
2137 : const nsPoint& aPt)
2138 : {
2139 0 : gfxPoint pt(NSAppUnitsToFloatPixels(aPt.x, aAppUnitsPerGfxUnit),
2140 0 : NSAppUnitsToFloatPixels(aPt.y, aAppUnitsPerGfxUnit));
2141 0 : pt = aThebesContext->UserToDevice(pt);
2142 0 : pt.Round();
2143 0 : pt = aThebesContext->DeviceToUser(pt);
2144 : return nsPoint(NSFloatPixelsToAppUnits(pt.x, aAppUnitsPerGfxUnit),
2145 0 : NSFloatPixelsToAppUnits(pt.y, aAppUnitsPerGfxUnit));
2146 : }
2147 :
2148 : // paint a stretchy char by assembling glyphs vertically
2149 : nsresult
2150 0 : nsMathMLChar::PaintVertically(nsPresContext* aPresContext,
2151 : nsRenderingContext& aRenderingContext,
2152 : nsFont& aFont,
2153 : nsStyleContext* aStyleContext,
2154 : nsGlyphTable* aGlyphTable,
2155 : nsRect& aRect)
2156 : {
2157 : // Get the device pixel size in the vertical direction.
2158 : // (This makes no effort to optimize for non-translation transformations.)
2159 0 : nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
2160 :
2161 : // get metrics data to be re-used later
2162 0 : PRInt32 i = 0;
2163 : nsGlyphCode ch, chdata[4];
2164 0 : nsBoundingMetrics bmdata[4];
2165 : PRInt32 glue, bottom;
2166 0 : nsGlyphCode chGlue = aGlyphTable->GlueOf(aPresContext, this);
2167 0 : for (PRInt32 j = 0; j < 4; ++j) {
2168 0 : switch (j) {
2169 : case 0:
2170 0 : ch = aGlyphTable->TopOf(aPresContext, this);
2171 0 : break;
2172 : case 1:
2173 0 : ch = aGlyphTable->MiddleOf(aPresContext, this);
2174 0 : if (!ch.Exists())
2175 0 : continue; // no middle
2176 0 : break;
2177 : case 2:
2178 0 : ch = aGlyphTable->BottomOf(aPresContext, this);
2179 0 : bottom = i;
2180 0 : break;
2181 : case 3:
2182 0 : ch = chGlue;
2183 0 : glue = i;
2184 0 : break;
2185 : }
2186 : // empty slots are filled with the glue if it is not null
2187 0 : if (!ch.Exists()) ch = chGlue;
2188 : // if (!ch.Exists()) glue is null, leave bounding metrics at 0
2189 0 : if (ch.Exists()) {
2190 : SetFontFamily(aStyleContext, aRenderingContext,
2191 0 : aFont, aGlyphTable, ch, mFamily);
2192 0 : bmdata[i] = aRenderingContext.GetBoundingMetrics(ch.code, ch.Length());
2193 : }
2194 0 : chdata[i] = ch;
2195 0 : ++i;
2196 : }
2197 0 : nscoord dx = aRect.x;
2198 : nscoord offset[3], start[3], end[3];
2199 0 : nsRefPtr<gfxContext> ctx = aRenderingContext.ThebesContext();
2200 0 : for (i = 0; i <= bottom; ++i) {
2201 0 : ch = chdata[i];
2202 0 : const nsBoundingMetrics& bm = bmdata[i];
2203 : nscoord dy;
2204 0 : if (0 == i) { // top
2205 0 : dy = aRect.y + bm.ascent;
2206 : }
2207 0 : else if (bottom == i) { // bottom
2208 0 : dy = aRect.y + aRect.height - bm.descent;
2209 : }
2210 : else { // middle
2211 0 : dy = aRect.y + bm.ascent + (aRect.height - (bm.ascent + bm.descent))/2;
2212 : }
2213 : // _cairo_scaled_font_show_glyphs snaps origins to device pixels.
2214 : // Do this now so that we can get the other dimensions right.
2215 : // (This may not achieve much with non-rectangular transformations.)
2216 0 : dy = SnapToDevPixels(ctx, oneDevPixel, nsPoint(dx, dy)).y;
2217 : // abcissa passed to DrawString
2218 0 : offset[i] = dy;
2219 : // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
2220 : // pixel, so the bm values can include 1 row of faint pixels on each edge.
2221 : // Don't rely on this pixel as it can look like a gap.
2222 0 : start[i] = dy - bm.ascent + oneDevPixel; // top join
2223 0 : end[i] = dy + bm.descent - oneDevPixel; // bottom join
2224 : }
2225 :
2226 : // If there are overlaps, then join at the mid point
2227 0 : for (i = 0; i < bottom; ++i) {
2228 0 : if (end[i] > start[i+1]) {
2229 0 : end[i] = (end[i] + start[i+1]) / 2;
2230 0 : start[i+1] = end[i];
2231 : }
2232 : }
2233 :
2234 0 : nsRect unionRect = aRect;
2235 0 : unionRect.x += mBoundingMetrics.leftBearing;
2236 : unionRect.width =
2237 0 : mBoundingMetrics.rightBearing - mBoundingMetrics.leftBearing;
2238 0 : unionRect.Inflate(oneDevPixel, oneDevPixel);
2239 :
2240 : /////////////////////////////////////
2241 : // draw top, middle, bottom
2242 0 : for (i = 0; i <= bottom; ++i) {
2243 0 : ch = chdata[i];
2244 : // glue can be null, and other parts could have been set to glue
2245 0 : if (ch.Exists()) {
2246 : #ifdef SHOW_BORDERS
2247 : // bounding box of the part
2248 : aRenderingContext.SetColor(NS_RGB(0,0,0));
2249 : aRenderingContext.DrawRect(nsRect(dx,start[i],aRect.width+30*(i+1),end[i]-start[i]));
2250 : #endif
2251 0 : nscoord dy = offset[i];
2252 : // Draw a glyph in a clipped area so that we don't have hairy chars
2253 : // pending outside
2254 0 : nsRect clipRect = unionRect;
2255 : // Clip at the join to get a solid edge (without overlap or gap), when
2256 : // this won't change the glyph too much. If the glyph is too small to
2257 : // clip then we'll overlap rather than have a gap.
2258 0 : nscoord height = bmdata[i].ascent + bmdata[i].descent;
2259 0 : if (ch == chGlue ||
2260 : height * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) {
2261 0 : if (0 == i) { // top
2262 0 : clipRect.height = end[i] - clipRect.y;
2263 : }
2264 0 : else if (bottom == i) { // bottom
2265 0 : clipRect.height -= start[i] - clipRect.y;
2266 0 : clipRect.y = start[i];
2267 : }
2268 : else { // middle
2269 0 : clipRect.y = start[i];
2270 0 : clipRect.height = end[i] - start[i];
2271 : }
2272 : }
2273 0 : if (!clipRect.IsEmpty()) {
2274 0 : AutoPushClipRect clip(aRenderingContext, clipRect);
2275 : SetFontFamily(aStyleContext, aRenderingContext,
2276 0 : aFont, aGlyphTable, ch, mFamily);
2277 0 : aRenderingContext.DrawString(ch.code, ch.Length(), dx, dy);
2278 : }
2279 : }
2280 : }
2281 :
2282 : ///////////////
2283 : // fill the gap between top and middle, and between middle and bottom.
2284 0 : if (!chGlue.Exists()) { // null glue : draw a rule
2285 : // figure out the dimensions of the rule to be drawn :
2286 : // set lbearing to rightmost lbearing among the two current successive parts.
2287 : // set rbearing to leftmost rbearing among the two current successive parts.
2288 : // this not only satisfies the convention used for over/underbraces
2289 : // in TeX, but also takes care of broken fonts like the stretchy integral
2290 : // in Symbol for small font sizes in unix.
2291 : nscoord lbearing, rbearing;
2292 0 : PRInt32 first = 0, last = 1;
2293 0 : while (last <= bottom) {
2294 0 : if (chdata[last].Exists()) {
2295 0 : lbearing = bmdata[last].leftBearing;
2296 0 : rbearing = bmdata[last].rightBearing;
2297 0 : if (chdata[first].Exists()) {
2298 0 : if (lbearing < bmdata[first].leftBearing)
2299 0 : lbearing = bmdata[first].leftBearing;
2300 0 : if (rbearing > bmdata[first].rightBearing)
2301 0 : rbearing = bmdata[first].rightBearing;
2302 : }
2303 : }
2304 0 : else if (chdata[first].Exists()) {
2305 0 : lbearing = bmdata[first].leftBearing;
2306 0 : rbearing = bmdata[first].rightBearing;
2307 : }
2308 : else {
2309 0 : NS_ERROR("Cannot stretch - All parts missing");
2310 0 : return NS_ERROR_UNEXPECTED;
2311 : }
2312 : // paint the rule between the parts
2313 : nsRect rule(aRect.x + lbearing, end[first],
2314 0 : rbearing - lbearing, start[last] - end[first]);
2315 0 : if (!rule.IsEmpty())
2316 0 : aRenderingContext.FillRect(rule);
2317 0 : first = last;
2318 0 : last++;
2319 : }
2320 : }
2321 0 : else if (bmdata[glue].ascent + bmdata[glue].descent > 0) {
2322 : // glue is present
2323 0 : nsBoundingMetrics& bm = bmdata[glue];
2324 : // Ensure the stride for the glue is not reduced to less than one pixel
2325 0 : if (bm.ascent + bm.descent >= 3 * oneDevPixel) {
2326 : // To protect against gaps, pretend the glue is smaller than it is,
2327 : // in order to trim off ends and thus get a solid edge for the join.
2328 0 : bm.ascent -= oneDevPixel;
2329 0 : bm.descent -= oneDevPixel;
2330 : }
2331 :
2332 : SetFontFamily(aStyleContext, aRenderingContext,
2333 0 : aFont, aGlyphTable, chGlue, mFamily);
2334 0 : nsRect clipRect = unionRect;
2335 :
2336 0 : for (i = 0; i < bottom; ++i) {
2337 : // Make sure not to draw outside the character
2338 0 : nscoord dy = NS_MAX(end[i], aRect.y);
2339 0 : nscoord fillEnd = NS_MIN(start[i+1], aRect.YMost());
2340 : #ifdef SHOW_BORDERS
2341 : // exact area to fill
2342 : aRenderingContext.SetColor(NS_RGB(255,0,0));
2343 : clipRect.y = dy;
2344 : clipRect.height = fillEnd - dy;
2345 : aRenderingContext.DrawRect(clipRect);
2346 : {
2347 : #endif
2348 0 : while (dy < fillEnd) {
2349 0 : clipRect.y = dy;
2350 0 : clipRect.height = NS_MIN(bm.ascent + bm.descent, fillEnd - dy);
2351 0 : AutoPushClipRect clip(aRenderingContext, clipRect);
2352 0 : dy += bm.ascent;
2353 0 : aRenderingContext.DrawString(chGlue.code, chGlue.Length(), dx, dy);
2354 0 : dy += bm.descent;
2355 : }
2356 : #ifdef SHOW_BORDERS
2357 : }
2358 : // last glyph that may cross past its boundary and collide with the next
2359 : nscoord height = bm.ascent + bm.descent;
2360 : aRenderingContext.SetColor(NS_RGB(0,255,0));
2361 : aRenderingContext.DrawRect(nsRect(dx, dy-bm.ascent, aRect.width, height));
2362 : #endif
2363 : }
2364 : }
2365 : #ifdef DEBUG
2366 : else {
2367 0 : for (i = 0; i < bottom; ++i) {
2368 0 : NS_ASSERTION(end[i] >= start[i+1],
2369 : "gap between parts with missing glue glyph");
2370 : }
2371 : }
2372 : #endif
2373 0 : return NS_OK;
2374 : }
2375 :
2376 : // paint a stretchy char by assembling glyphs horizontally
2377 : nsresult
2378 0 : nsMathMLChar::PaintHorizontally(nsPresContext* aPresContext,
2379 : nsRenderingContext& aRenderingContext,
2380 : nsFont& aFont,
2381 : nsStyleContext* aStyleContext,
2382 : nsGlyphTable* aGlyphTable,
2383 : nsRect& aRect)
2384 : {
2385 : // Get the device pixel size in the horizontal direction.
2386 : // (This makes no effort to optimize for non-translation transformations.)
2387 0 : nscoord oneDevPixel = aPresContext->AppUnitsPerDevPixel();
2388 :
2389 : // get metrics data to be re-used later
2390 0 : PRInt32 i = 0;
2391 : nsGlyphCode ch, chdata[4];
2392 0 : nsBoundingMetrics bmdata[4];
2393 : PRInt32 glue, right;
2394 0 : nsGlyphCode chGlue = aGlyphTable->GlueOf(aPresContext, this);
2395 0 : for (PRInt32 j = 0; j < 4; ++j) {
2396 0 : switch (j) {
2397 : case 0:
2398 0 : ch = aGlyphTable->LeftOf(aPresContext, this);
2399 0 : break;
2400 : case 1:
2401 0 : ch = aGlyphTable->MiddleOf(aPresContext, this);
2402 0 : if (!ch.Exists())
2403 0 : continue; // no middle
2404 0 : break;
2405 : case 2:
2406 0 : ch = aGlyphTable->RightOf(aPresContext, this);
2407 0 : right = i;
2408 0 : break;
2409 : case 3:
2410 0 : ch = chGlue;
2411 0 : glue = i;
2412 0 : break;
2413 : }
2414 : // empty slots are filled with the glue if it is not null
2415 0 : if (!ch.Exists()) ch = chGlue;
2416 : // if (!ch.Exists()) glue is null, leave bounding metrics at 0.
2417 0 : if (ch.Exists()) {
2418 : SetFontFamily(aStyleContext, aRenderingContext,
2419 0 : aFont, aGlyphTable, ch, mFamily);
2420 0 : bmdata[i] = aRenderingContext.GetBoundingMetrics(ch.code, ch.Length());
2421 : }
2422 0 : chdata[i] = ch;
2423 0 : ++i;
2424 : }
2425 0 : nscoord dy = aRect.y + mBoundingMetrics.ascent;
2426 : nscoord offset[3], start[3], end[3];
2427 0 : nsRefPtr<gfxContext> ctx = aRenderingContext.ThebesContext();
2428 0 : for (i = 0; i <= right; ++i) {
2429 0 : ch = chdata[i];
2430 0 : const nsBoundingMetrics& bm = bmdata[i];
2431 : nscoord dx;
2432 0 : if (0 == i) { // left
2433 0 : dx = aRect.x - bm.leftBearing;
2434 : }
2435 0 : else if (right == i) { // right
2436 0 : dx = aRect.x + aRect.width - bm.rightBearing;
2437 : }
2438 : else { // middle
2439 0 : dx = aRect.x + (aRect.width - bm.width)/2;
2440 : }
2441 : // _cairo_scaled_font_show_glyphs snaps origins to device pixels.
2442 : // Do this now so that we can get the other dimensions right.
2443 : // (This may not achieve much with non-rectangular transformations.)
2444 0 : dx = SnapToDevPixels(ctx, oneDevPixel, nsPoint(dx, dy)).x;
2445 : // abcissa passed to DrawString
2446 0 : offset[i] = dx;
2447 : // _cairo_scaled_font_glyph_device_extents rounds outwards to the nearest
2448 : // pixel, so the bm values can include 1 row of faint pixels on each edge.
2449 : // Don't rely on this pixel as it can look like a gap.
2450 0 : start[i] = dx + bm.leftBearing + oneDevPixel; // left join
2451 0 : end[i] = dx + bm.rightBearing - oneDevPixel; // right join
2452 : }
2453 :
2454 : // If there are overlaps, then join at the mid point
2455 0 : for (i = 0; i < right; ++i) {
2456 0 : if (end[i] > start[i+1]) {
2457 0 : end[i] = (end[i] + start[i+1]) / 2;
2458 0 : start[i+1] = end[i];
2459 : }
2460 : }
2461 :
2462 0 : nsRect unionRect = aRect;
2463 0 : unionRect.Inflate(oneDevPixel, oneDevPixel);
2464 :
2465 : ///////////////////////////
2466 : // draw left, middle, right
2467 0 : for (i = 0; i <= right; ++i) {
2468 0 : ch = chdata[i];
2469 : // glue can be null, and other parts could have been set to glue
2470 0 : if (ch.Exists()) {
2471 : #ifdef SHOW_BORDERS
2472 : aRenderingContext.SetColor(NS_RGB(255,0,0));
2473 : aRenderingContext.DrawRect(nsRect(start[i], dy - bmdata[i].ascent,
2474 : end[i] - start[i], bmdata[i].ascent + bmdata[i].descent));
2475 : #endif
2476 0 : nscoord dx = offset[i];
2477 0 : nsRect clipRect = unionRect;
2478 : // Clip at the join to get a solid edge (without overlap or gap), when
2479 : // this won't change the glyph too much. If the glyph is too small to
2480 : // clip then we'll overlap rather than have a gap.
2481 0 : nscoord width = bmdata[i].rightBearing - bmdata[i].leftBearing;
2482 0 : if (ch == chGlue ||
2483 : width * (1.0 - NS_MATHML_DELIMITER_FACTOR) > oneDevPixel) {
2484 0 : if (0 == i) { // left
2485 0 : clipRect.width = end[i] - clipRect.x;
2486 : }
2487 0 : else if (right == i) { // right
2488 0 : clipRect.width -= start[i] - clipRect.x;
2489 0 : clipRect.x = start[i];
2490 : }
2491 : else { // middle
2492 0 : clipRect.x = start[i];
2493 0 : clipRect.width = end[i] - start[i];
2494 : }
2495 : }
2496 0 : if (!clipRect.IsEmpty()) {
2497 0 : AutoPushClipRect clip(aRenderingContext, clipRect);
2498 : SetFontFamily(aStyleContext, aRenderingContext,
2499 0 : aFont, aGlyphTable, ch, mFamily);
2500 0 : aRenderingContext.DrawString(ch.code, ch.Length(), dx, dy);
2501 : }
2502 : }
2503 : }
2504 :
2505 : ////////////////
2506 : // fill the gap between left and middle, and between middle and right.
2507 0 : if (!chGlue.Exists()) { // null glue : draw a rule
2508 : // figure out the dimensions of the rule to be drawn :
2509 : // set ascent to lowest ascent among the two current successive parts.
2510 : // set descent to highest descent among the two current successive parts.
2511 : // this satisfies the convention used for over/underbraces, and helps
2512 : // fix broken fonts.
2513 : nscoord ascent, descent;
2514 0 : PRInt32 first = 0, last = 1;
2515 0 : while (last <= right) {
2516 0 : if (chdata[last].Exists()) {
2517 0 : ascent = bmdata[last].ascent;
2518 0 : descent = bmdata[last].descent;
2519 0 : if (chdata[first].Exists()) {
2520 0 : if (ascent > bmdata[first].ascent)
2521 0 : ascent = bmdata[first].ascent;
2522 0 : if (descent > bmdata[first].descent)
2523 0 : descent = bmdata[first].descent;
2524 : }
2525 : }
2526 0 : else if (chdata[first].Exists()) {
2527 0 : ascent = bmdata[first].ascent;
2528 0 : descent = bmdata[first].descent;
2529 : }
2530 : else {
2531 0 : NS_ERROR("Cannot stretch - All parts missing");
2532 0 : return NS_ERROR_UNEXPECTED;
2533 : }
2534 : // paint the rule between the parts
2535 : nsRect rule(end[first], dy - ascent,
2536 0 : start[last] - end[first], ascent + descent);
2537 0 : if (!rule.IsEmpty())
2538 0 : aRenderingContext.FillRect(rule);
2539 0 : first = last;
2540 0 : last++;
2541 : }
2542 : }
2543 0 : else if (bmdata[glue].rightBearing - bmdata[glue].leftBearing > 0) {
2544 : // glue is present
2545 0 : nsBoundingMetrics& bm = bmdata[glue];
2546 : // Ensure the stride for the glue is not reduced to less than one pixel
2547 0 : if (bm.rightBearing - bm.leftBearing >= 3 * oneDevPixel) {
2548 : // To protect against gaps, pretend the glue is smaller than it is,
2549 : // in order to trim off ends and thus get a solid edge for the join.
2550 0 : bm.leftBearing += oneDevPixel;
2551 0 : bm.rightBearing -= oneDevPixel;
2552 : }
2553 :
2554 : SetFontFamily(aStyleContext, aRenderingContext,
2555 0 : aFont, aGlyphTable, chGlue, mFamily);
2556 0 : nsRect clipRect = unionRect;
2557 :
2558 0 : for (i = 0; i < right; ++i) {
2559 : // Make sure not to draw outside the character
2560 0 : nscoord dx = NS_MAX(end[i], aRect.x);
2561 0 : nscoord fillEnd = NS_MIN(start[i+1], aRect.XMost());
2562 : #ifdef SHOW_BORDERS
2563 : // rectangles in-between that are to be filled
2564 : aRenderingContext.SetColor(NS_RGB(255,0,0));
2565 : clipRect.x = dx;
2566 : clipRect.width = fillEnd - dx;
2567 : aRenderingContext.DrawRect(clipRect);
2568 : {
2569 : #endif
2570 0 : while (dx < fillEnd) {
2571 0 : clipRect.x = dx;
2572 0 : clipRect.width = NS_MIN(bm.rightBearing - bm.leftBearing, fillEnd - dx);
2573 0 : AutoPushClipRect clip(aRenderingContext, clipRect);
2574 0 : dx -= bm.leftBearing;
2575 0 : aRenderingContext.DrawString(chGlue.code, chGlue.Length(), dx, dy);
2576 0 : dx += bm.rightBearing;
2577 : }
2578 : #ifdef SHOW_BORDERS
2579 : }
2580 : // last glyph that may cross past its boundary and collide with the next
2581 : nscoord width = bm.rightBearing - bm.leftBearing;
2582 : aRenderingContext.SetColor(NS_RGB(0,255,0));
2583 : aRenderingContext.DrawRect(nsRect(dx + bm.leftBearing, aRect.y, width, aRect.height));
2584 : #endif
2585 : }
2586 : }
2587 : #ifdef DEBUG
2588 : else { // no glue
2589 0 : for (i = 0; i < right; ++i) {
2590 0 : NS_ASSERTION(end[i] >= start[i+1],
2591 : "gap between parts with missing glue glyph");
2592 : }
2593 : }
2594 : #endif
2595 0 : return NS_OK;
2596 : }
|