1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 Foundation code.
16 : *
17 : * The Initial Developer of the Original Code is Mozilla Foundation.
18 : * Portions created by the Initial Developer are Copyright (C) 2005-2009
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Stuart Parmenter <stuart@mozilla.com>
23 : * Masayuki Nakano <masayuki@d-toybox.com>
24 : * John Daggett <jdaggett@mozilla.com>
25 : * Jonathan Kew <jfkthame@gmail.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either the GNU General Public License Version 2 or later (the "GPL"), or
29 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #ifdef MOZ_LOGGING
42 : #define FORCE_PR_LOG /* Allow logging in the release build */
43 : #endif
44 : #include "prlog.h"
45 :
46 : #include "nsServiceManagerUtils.h"
47 : #include "nsReadableUtils.h"
48 : #include "nsExpirationTracker.h"
49 : #include "nsILanguageAtomService.h"
50 : #include "nsIMemoryReporter.h"
51 : #include "nsITimer.h"
52 :
53 : #include "gfxFont.h"
54 : #include "gfxPlatform.h"
55 : #include "gfxAtoms.h"
56 :
57 : #include "prtypes.h"
58 : #include "gfxTypes.h"
59 : #include "nsAlgorithm.h"
60 : #include "gfxContext.h"
61 : #include "gfxFontMissingGlyphs.h"
62 : #include "gfxUserFontSet.h"
63 : #include "gfxPlatformFontList.h"
64 : #include "gfxScriptItemizer.h"
65 : #include "nsUnicodeProperties.h"
66 : #include "nsMathUtils.h"
67 : #include "nsBidiUtils.h"
68 : #include "nsUnicodeRange.h"
69 : #include "nsCompressedCharMap.h"
70 : #include "nsStyleConsts.h"
71 : #include "mozilla/Preferences.h"
72 : #include "mozilla/Services.h"
73 : #include "mozilla/Telemetry.h"
74 :
75 : #include "cairo.h"
76 : #include "gfxFontTest.h"
77 :
78 : #include "harfbuzz/hb-blob.h"
79 :
80 : #include "nsCRT.h"
81 :
82 : #include <algorithm>
83 :
84 : using namespace mozilla;
85 : using namespace mozilla::gfx;
86 : using namespace mozilla::unicode;
87 : using mozilla::services::GetObserverService;
88 :
89 : gfxFontCache *gfxFontCache::gGlobalCache = nsnull;
90 :
91 : #ifdef DEBUG_roc
92 : #define DEBUG_TEXT_RUN_STORAGE_METRICS
93 : #endif
94 :
95 : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
96 : static PRUint32 gTextRunStorageHighWaterMark = 0;
97 : static PRUint32 gTextRunStorage = 0;
98 : static PRUint32 gFontCount = 0;
99 : static PRUint32 gGlyphExtentsCount = 0;
100 : static PRUint32 gGlyphExtentsWidthsTotalSize = 0;
101 : static PRUint32 gGlyphExtentsSetupEagerSimple = 0;
102 : static PRUint32 gGlyphExtentsSetupEagerTight = 0;
103 : static PRUint32 gGlyphExtentsSetupLazyTight = 0;
104 : static PRUint32 gGlyphExtentsSetupFallBackToTight = 0;
105 : #endif
106 :
107 0 : gfxFontEntry::~gfxFontEntry()
108 : {
109 0 : delete mUserFontData;
110 0 : }
111 :
112 0 : bool gfxFontEntry::IsSymbolFont()
113 : {
114 0 : return mSymbolFont;
115 : }
116 :
117 0 : bool gfxFontEntry::TestCharacterMap(PRUint32 aCh)
118 : {
119 0 : if (!mCmapInitialized) {
120 0 : ReadCMAP();
121 : }
122 0 : return mCharacterMap.test(aCh);
123 : }
124 :
125 0 : nsresult gfxFontEntry::InitializeUVSMap()
126 : {
127 : // mUVSOffset will not be initialized
128 : // until cmap is initialized.
129 0 : if (!mCmapInitialized) {
130 0 : ReadCMAP();
131 : }
132 :
133 0 : if (!mUVSOffset) {
134 0 : return NS_ERROR_FAILURE;
135 : }
136 :
137 0 : if (!mUVSData) {
138 0 : const PRUint32 kCmapTag = TRUETYPE_TAG('c','m','a','p');
139 0 : AutoFallibleTArray<PRUint8,16384> buffer;
140 0 : if (GetFontTable(kCmapTag, buffer) != NS_OK) {
141 0 : mUVSOffset = 0; // don't bother to read the table again
142 0 : return NS_ERROR_FAILURE;
143 : }
144 :
145 : PRUint8* uvsData;
146 : nsresult rv = gfxFontUtils::ReadCMAPTableFormat14(
147 0 : buffer.Elements() + mUVSOffset,
148 0 : buffer.Length() - mUVSOffset,
149 0 : uvsData);
150 0 : if (NS_FAILED(rv)) {
151 0 : mUVSOffset = 0; // don't bother to read the table again
152 0 : return rv;
153 : }
154 :
155 0 : mUVSData = uvsData;
156 : }
157 :
158 0 : return NS_OK;
159 : }
160 :
161 0 : PRUint16 gfxFontEntry::GetUVSGlyph(PRUint32 aCh, PRUint32 aVS)
162 : {
163 0 : InitializeUVSMap();
164 :
165 0 : if (mUVSData) {
166 0 : return gfxFontUtils::MapUVSToGlyphFormat14(mUVSData, aCh, aVS);
167 : }
168 :
169 0 : return 0;
170 : }
171 :
172 0 : nsresult gfxFontEntry::ReadCMAP()
173 : {
174 0 : mCmapInitialized = true;
175 0 : return NS_OK;
176 : }
177 :
178 0 : nsString gfxFontEntry::FamilyName() const
179 : {
180 0 : NS_ASSERTION(mFamily, "orphaned font entry");
181 0 : if (mFamily) {
182 0 : return mFamily->Name();
183 : } else {
184 0 : return nsString();
185 : }
186 : }
187 :
188 : nsString
189 0 : gfxFontEntry::RealFaceName()
190 : {
191 0 : FallibleTArray<PRUint8> nameTable;
192 0 : nsresult rv = GetFontTable(TRUETYPE_TAG('n','a','m','e'), nameTable);
193 0 : if (NS_SUCCEEDED(rv)) {
194 0 : nsAutoString name;
195 0 : rv = gfxFontUtils::GetFullNameFromTable(nameTable, name);
196 0 : if (NS_SUCCEEDED(rv)) {
197 0 : return name;
198 : }
199 : }
200 0 : return Name();
201 : }
202 :
203 : already_AddRefed<gfxFont>
204 0 : gfxFontEntry::FindOrMakeFont(const gfxFontStyle *aStyle, bool aNeedsBold)
205 : {
206 : // the font entry name is the psname, not the family name
207 0 : nsRefPtr<gfxFont> font = gfxFontCache::GetCache()->Lookup(this, aStyle);
208 :
209 0 : if (!font) {
210 0 : gfxFont *newFont = CreateFontInstance(aStyle, aNeedsBold);
211 0 : if (!newFont)
212 0 : return nsnull;
213 0 : if (!newFont->Valid()) {
214 0 : delete newFont;
215 0 : return nsnull;
216 : }
217 0 : font = newFont;
218 0 : gfxFontCache::GetCache()->AddNew(font);
219 : }
220 0 : gfxFont *f = nsnull;
221 0 : font.swap(f);
222 0 : return f;
223 : }
224 :
225 : /**
226 : * FontTableBlobData
227 : *
228 : * See FontTableHashEntry for the general strategy.
229 : */
230 :
231 : class gfxFontEntry::FontTableBlobData {
232 : public:
233 : // Adopts the content of aBuffer.
234 : // Pass a non-null aHashEntry only if it should be cleared if/when this
235 : // FontTableBlobData is deleted.
236 0 : FontTableBlobData(FallibleTArray<PRUint8>& aBuffer,
237 : FontTableHashEntry *aHashEntry)
238 0 : : mHashEntry(aHashEntry), mHashtable()
239 : {
240 0 : MOZ_COUNT_CTOR(FontTableBlobData);
241 0 : mTableData.SwapElements(aBuffer);
242 0 : }
243 :
244 0 : ~FontTableBlobData() {
245 0 : MOZ_COUNT_DTOR(FontTableBlobData);
246 0 : if (mHashEntry) {
247 0 : if (mHashtable) {
248 0 : mHashtable->RemoveEntry(mHashEntry->GetKey());
249 : } else {
250 0 : mHashEntry->Clear();
251 : }
252 : }
253 0 : }
254 :
255 : // Useful for creating blobs
256 0 : const char *GetTable() const
257 : {
258 0 : return reinterpret_cast<const char*>(mTableData.Elements());
259 : }
260 0 : PRUint32 GetTableLength() const { return mTableData.Length(); }
261 :
262 : // Tell this FontTableBlobData to remove the HashEntry when this is
263 : // destroyed.
264 0 : void ManageHashEntry(nsTHashtable<FontTableHashEntry> *aHashtable)
265 : {
266 0 : mHashtable = aHashtable;
267 0 : }
268 :
269 : // Disconnect from the HashEntry (because the blob has already been
270 : // removed from the hashtable).
271 0 : void ForgetHashEntry()
272 : {
273 0 : mHashEntry = nsnull;
274 0 : }
275 :
276 : private:
277 : // The font table data block, owned (via adoption)
278 : FallibleTArray<PRUint8> mTableData;
279 : // The blob destroy function needs to know the hashtable entry,
280 : FontTableHashEntry *mHashEntry;
281 : // and the owning hashtable, so that it can remove the entry.
282 : nsTHashtable<FontTableHashEntry> *mHashtable;
283 :
284 : // not implemented
285 : FontTableBlobData(const FontTableBlobData&);
286 : };
287 :
288 : void
289 0 : gfxFontEntry::FontTableHashEntry::SaveTable(FallibleTArray<PRUint8>& aTable)
290 : {
291 0 : Clear();
292 : // adopts elements of aTable
293 0 : FontTableBlobData *data = new FontTableBlobData(aTable, nsnull);
294 : mBlob = hb_blob_create(data->GetTable(), data->GetTableLength(),
295 : HB_MEMORY_MODE_READONLY,
296 0 : data, DeleteFontTableBlobData);
297 0 : }
298 :
299 : hb_blob_t *
300 0 : gfxFontEntry::FontTableHashEntry::
301 : ShareTableAndGetBlob(FallibleTArray<PRUint8>& aTable,
302 : nsTHashtable<FontTableHashEntry> *aHashtable)
303 : {
304 0 : Clear();
305 : // adopts elements of aTable
306 0 : mSharedBlobData = new FontTableBlobData(aTable, this);
307 : mBlob = hb_blob_create(mSharedBlobData->GetTable(),
308 : mSharedBlobData->GetTableLength(),
309 : HB_MEMORY_MODE_READONLY,
310 0 : mSharedBlobData, DeleteFontTableBlobData);
311 0 : if (!mSharedBlobData) {
312 : // The FontTableBlobData was destroyed during hb_blob_create().
313 : // The (empty) blob is still be held in the hashtable with a strong
314 : // reference.
315 0 : return hb_blob_reference(mBlob);
316 : }
317 :
318 : // Tell the FontTableBlobData to remove this hash entry when destroyed.
319 : // The hashtable does not keep a strong reference.
320 0 : mSharedBlobData->ManageHashEntry(aHashtable);
321 0 : return mBlob;
322 : }
323 :
324 : void
325 0 : gfxFontEntry::FontTableHashEntry::Clear()
326 : {
327 : // If the FontTableBlobData is managing the hash entry, then the blob is
328 : // not owned by this HashEntry; otherwise there is strong reference to the
329 : // blob that must be removed.
330 0 : if (mSharedBlobData) {
331 0 : mSharedBlobData->ForgetHashEntry();
332 0 : mSharedBlobData = nsnull;
333 0 : } else if (mBlob) {
334 0 : hb_blob_destroy(mBlob);
335 : }
336 0 : mBlob = nsnull;
337 0 : }
338 :
339 : // a hb_destroy_func for hb_blob_create
340 :
341 : /* static */ void
342 0 : gfxFontEntry::FontTableHashEntry::DeleteFontTableBlobData(void *aBlobData)
343 : {
344 0 : delete static_cast<FontTableBlobData*>(aBlobData);
345 0 : }
346 :
347 : hb_blob_t *
348 0 : gfxFontEntry::FontTableHashEntry::GetBlob() const
349 : {
350 0 : return hb_blob_reference(mBlob);
351 : }
352 :
353 : bool
354 0 : gfxFontEntry::GetExistingFontTable(PRUint32 aTag, hb_blob_t **aBlob)
355 : {
356 0 : if (!mFontTableCache.IsInitialized()) {
357 : // we do this here rather than on fontEntry construction
358 : // because not all shapers will access the table cache at all
359 0 : mFontTableCache.Init(10);
360 : }
361 :
362 0 : FontTableHashEntry *entry = mFontTableCache.GetEntry(aTag);
363 0 : if (!entry) {
364 0 : return false;
365 : }
366 :
367 0 : *aBlob = entry->GetBlob();
368 0 : return true;
369 : }
370 :
371 : hb_blob_t *
372 0 : gfxFontEntry::ShareFontTableAndGetBlob(PRUint32 aTag,
373 : FallibleTArray<PRUint8>* aBuffer)
374 : {
375 0 : if (NS_UNLIKELY(!mFontTableCache.IsInitialized())) {
376 : // we do this here rather than on fontEntry construction
377 : // because not all shapers will access the table cache at all
378 0 : mFontTableCache.Init(10);
379 : }
380 :
381 0 : FontTableHashEntry *entry = mFontTableCache.PutEntry(aTag);
382 0 : if (NS_UNLIKELY(!entry)) { // OOM
383 0 : return nsnull;
384 : }
385 :
386 0 : if (!aBuffer) {
387 : // ensure the entry is null
388 0 : entry->Clear();
389 0 : return nsnull;
390 : }
391 :
392 0 : return entry->ShareTableAndGetBlob(*aBuffer, &mFontTableCache);
393 : }
394 :
395 : #ifdef MOZ_GRAPHITE
396 : void
397 0 : gfxFontEntry::CheckForGraphiteTables()
398 : {
399 0 : AutoFallibleTArray<PRUint8,16384> buffer;
400 : mHasGraphiteTables =
401 0 : NS_SUCCEEDED(GetFontTable(TRUETYPE_TAG('S','i','l','f'), buffer));
402 0 : }
403 : #endif
404 :
405 : //////////////////////////////////////////////////////////////////////////////
406 : //
407 : // class gfxFontFamily
408 : //
409 : //////////////////////////////////////////////////////////////////////////////
410 :
411 : // we consider faces with mStandardFace == true to be "greater than" those with false,
412 : // because during style matching, later entries will replace earlier ones
413 : class FontEntryStandardFaceComparator {
414 : public:
415 0 : bool Equals(const nsRefPtr<gfxFontEntry>& a, const nsRefPtr<gfxFontEntry>& b) const {
416 0 : return a->mStandardFace == b->mStandardFace;
417 : }
418 0 : bool LessThan(const nsRefPtr<gfxFontEntry>& a, const nsRefPtr<gfxFontEntry>& b) const {
419 0 : return (a->mStandardFace == false && b->mStandardFace == true);
420 : }
421 : };
422 :
423 : void
424 0 : gfxFontFamily::SortAvailableFonts()
425 : {
426 0 : mAvailableFonts.Sort(FontEntryStandardFaceComparator());
427 0 : }
428 :
429 : bool
430 0 : gfxFontFamily::HasOtherFamilyNames()
431 : {
432 : // need to read in other family names to determine this
433 0 : if (!mOtherFamilyNamesInitialized) {
434 0 : ReadOtherFamilyNames(gfxPlatformFontList::PlatformFontList()); // sets mHasOtherFamilyNames
435 : }
436 0 : return mHasOtherFamilyNames;
437 : }
438 :
439 : gfxFontEntry*
440 0 : gfxFontFamily::FindFontForStyle(const gfxFontStyle& aFontStyle,
441 : bool& aNeedsSyntheticBold)
442 : {
443 0 : if (!mHasStyles)
444 0 : FindStyleVariations(); // collect faces for the family, if not already done
445 :
446 0 : NS_ASSERTION(mAvailableFonts.Length() > 0, "font family with no faces!");
447 :
448 0 : aNeedsSyntheticBold = false;
449 :
450 0 : PRInt8 baseWeight = aFontStyle.ComputeWeight();
451 0 : bool wantBold = baseWeight >= 6;
452 :
453 : // If the family has only one face, we simply return it; no further checking needed
454 0 : if (mAvailableFonts.Length() == 1) {
455 0 : gfxFontEntry *fe = mAvailableFonts[0];
456 0 : aNeedsSyntheticBold = wantBold && !fe->IsBold();
457 0 : return fe;
458 : }
459 :
460 0 : bool wantItalic = (aFontStyle.style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) != 0;
461 :
462 : // Most families are "simple", having just Regular/Bold/Italic/BoldItalic,
463 : // or some subset of these. In this case, we have exactly 4 entries in mAvailableFonts,
464 : // stored in the above order; note that some of the entries may be NULL.
465 : // We can then pick the required entry based on whether the request is for
466 : // bold or non-bold, italic or non-italic, without running the more complex
467 : // matching algorithm used for larger families with many weights and/or widths.
468 :
469 0 : if (mIsSimpleFamily) {
470 : // Family has no more than the "standard" 4 faces, at fixed indexes;
471 : // calculate which one we want.
472 : // Note that we cannot simply return it as not all 4 faces are necessarily present.
473 : PRUint8 faceIndex = (wantItalic ? kItalicMask : 0) |
474 0 : (wantBold ? kBoldMask : 0);
475 :
476 : // if the desired style is available, return it directly
477 0 : gfxFontEntry *fe = mAvailableFonts[faceIndex];
478 0 : if (fe) {
479 : // no need to set aNeedsSyntheticBold here as we matched the boldness request
480 0 : return fe;
481 : }
482 :
483 : // order to check fallback faces in a simple family, depending on requested style
484 : static const PRUint8 simpleFallbacks[4][3] = {
485 : { kBoldFaceIndex, kItalicFaceIndex, kBoldItalicFaceIndex }, // fallbacks for Regular
486 : { kRegularFaceIndex, kBoldItalicFaceIndex, kItalicFaceIndex },// Bold
487 : { kBoldItalicFaceIndex, kRegularFaceIndex, kBoldFaceIndex }, // Italic
488 : { kItalicFaceIndex, kBoldFaceIndex, kRegularFaceIndex } // BoldItalic
489 : };
490 0 : const PRUint8 *order = simpleFallbacks[faceIndex];
491 :
492 0 : for (PRUint8 trial = 0; trial < 3; ++trial) {
493 : // check remaining faces in order of preference to find the first that actually exists
494 0 : fe = mAvailableFonts[order[trial]];
495 0 : if (fe) {
496 0 : aNeedsSyntheticBold = wantBold && !fe->IsBold();
497 0 : return fe;
498 : }
499 : }
500 :
501 : // this can't happen unless we have totally broken the font-list manager!
502 0 : NS_NOTREACHED("no face found in simple font family!");
503 0 : return nsnull;
504 : }
505 :
506 : // This is a large/rich font family, so we do full style- and weight-matching:
507 : // first collect a list of weights that are the best match for the requested
508 : // font-stretch and font-style, then pick the best weight match among those
509 : // available.
510 :
511 0 : gfxFontEntry *weightList[10] = { 0 };
512 0 : bool foundWeights = FindWeightsForStyle(weightList, wantItalic, aFontStyle.stretch);
513 0 : if (!foundWeights) {
514 0 : return nsnull;
515 : }
516 :
517 : // First find a match for the best weight
518 0 : PRInt8 matchBaseWeight = 0;
519 0 : PRInt8 i = baseWeight;
520 :
521 : // Need to special case when normal face doesn't exist but medium does.
522 : // In that case, use medium otherwise weights < 400
523 0 : if (baseWeight == 4 && !weightList[4]) {
524 0 : i = 5; // medium
525 : }
526 :
527 : // Loop through weights, since one exists loop will terminate
528 0 : PRInt8 direction = (baseWeight > 5) ? 1 : -1;
529 0 : for (; ; i += direction) {
530 0 : if (weightList[i]) {
531 0 : matchBaseWeight = i;
532 : break;
533 : }
534 :
535 : // If we've reached one side without finding a font,
536 : // start over and go the other direction until we find a match
537 0 : if (i == 1 || i == 9) {
538 0 : i = baseWeight;
539 0 : direction = -direction;
540 : }
541 : }
542 :
543 0 : NS_ASSERTION(matchBaseWeight != 0,
544 : "weight mapping should always find at least one font in a family");
545 :
546 0 : gfxFontEntry *matchFE = weightList[matchBaseWeight];
547 :
548 0 : NS_ASSERTION(matchFE,
549 : "weight mapping should always find at least one font in a family");
550 :
551 0 : if (!matchFE->IsBold() && baseWeight >= 6)
552 : {
553 0 : aNeedsSyntheticBold = true;
554 : }
555 :
556 0 : return matchFE;
557 : }
558 :
559 : void
560 0 : gfxFontFamily::CheckForSimpleFamily()
561 : {
562 0 : PRUint32 count = mAvailableFonts.Length();
563 0 : if (count > 4 || count == 0) {
564 0 : return; // can't be "simple" if there are >4 faces;
565 : // if none then the family is unusable anyway
566 : }
567 :
568 0 : if (count == 1) {
569 0 : mIsSimpleFamily = true;
570 0 : return;
571 : }
572 :
573 0 : PRInt16 firstStretch = mAvailableFonts[0]->Stretch();
574 :
575 0 : gfxFontEntry *faces[4] = { 0 };
576 0 : for (PRUint8 i = 0; i < count; ++i) {
577 0 : gfxFontEntry *fe = mAvailableFonts[i];
578 0 : if (fe->Stretch() != firstStretch) {
579 0 : return; // font-stretch doesn't match, don't treat as simple family
580 : }
581 0 : PRUint8 faceIndex = (fe->IsItalic() ? kItalicMask : 0) |
582 0 : (fe->Weight() >= 600 ? kBoldMask : 0);
583 0 : if (faces[faceIndex]) {
584 0 : return; // two faces resolve to the same slot; family isn't "simple"
585 : }
586 0 : faces[faceIndex] = fe;
587 : }
588 :
589 : // we have successfully slotted the available faces into the standard
590 : // 4-face framework
591 0 : mAvailableFonts.SetLength(4);
592 0 : for (PRUint8 i = 0; i < 4; ++i) {
593 0 : if (mAvailableFonts[i].get() != faces[i]) {
594 0 : mAvailableFonts[i].swap(faces[i]);
595 : }
596 : }
597 :
598 0 : mIsSimpleFamily = true;
599 : }
600 :
601 : static inline PRUint32
602 0 : StyleDistance(gfxFontEntry *aFontEntry,
603 : bool anItalic, PRInt16 aStretch)
604 : {
605 : // Compute a measure of the "distance" between the requested style
606 : // and the given fontEntry,
607 : // considering italicness and font-stretch but not weight.
608 :
609 0 : PRInt32 distance = 0;
610 0 : if (aStretch != aFontEntry->mStretch) {
611 : // stretch values are in the range -4 .. +4
612 : // if aStretch is positive, we prefer more-positive values;
613 : // if zero or negative, prefer more-negative
614 0 : if (aStretch > 0) {
615 0 : distance = (aFontEntry->mStretch - aStretch) * 2;
616 : } else {
617 0 : distance = (aStretch - aFontEntry->mStretch) * 2;
618 : }
619 : // if the computed "distance" here is negative, it means that
620 : // aFontEntry lies in the "non-preferred" direction from aStretch,
621 : // so we treat that as larger than any preferred-direction distance
622 : // (max possible is 8) by adding an extra 10 to the absolute value
623 0 : if (distance < 0) {
624 0 : distance = -distance + 10;
625 : }
626 : }
627 0 : if (aFontEntry->IsItalic() != anItalic) {
628 0 : distance += 1;
629 : }
630 0 : return PRUint32(distance);
631 : }
632 :
633 : bool
634 0 : gfxFontFamily::FindWeightsForStyle(gfxFontEntry* aFontsForWeights[],
635 : bool anItalic, PRInt16 aStretch)
636 : {
637 0 : PRUint32 foundWeights = 0;
638 0 : PRUint32 bestMatchDistance = 0xffffffff;
639 :
640 0 : for (PRUint32 i = 0; i < mAvailableFonts.Length(); i++) {
641 : // this is not called for "simple" families, and therefore it does not
642 : // need to check the mAvailableFonts entries for NULL
643 0 : gfxFontEntry *fe = mAvailableFonts[i];
644 0 : PRUint32 distance = StyleDistance(fe, anItalic, aStretch);
645 0 : if (distance <= bestMatchDistance) {
646 0 : PRInt8 wt = fe->mWeight / 100;
647 0 : NS_ASSERTION(wt >= 1 && wt < 10, "invalid weight in fontEntry");
648 0 : if (!aFontsForWeights[wt]) {
649 : // record this as a possible candidate for weight matching
650 0 : aFontsForWeights[wt] = fe;
651 0 : ++foundWeights;
652 : } else {
653 : PRUint32 prevDistance =
654 0 : StyleDistance(aFontsForWeights[wt], anItalic, aStretch);
655 0 : if (prevDistance >= distance) {
656 : // replacing a weight we already found,
657 : // so don't increment foundWeights
658 0 : aFontsForWeights[wt] = fe;
659 : }
660 : }
661 0 : bestMatchDistance = distance;
662 : }
663 : }
664 :
665 0 : NS_ASSERTION(foundWeights > 0, "Font family containing no faces?");
666 :
667 0 : if (foundWeights == 1) {
668 : // no need to cull entries if we only found one weight
669 0 : return true;
670 : }
671 :
672 : // we might have recorded some faces that were a partial style match, but later found
673 : // others that were closer; in this case, we need to cull the poorer matches from the
674 : // weight list we'll return
675 0 : for (PRUint32 i = 0; i < 10; ++i) {
676 0 : if (aFontsForWeights[i] &&
677 0 : StyleDistance(aFontsForWeights[i], anItalic, aStretch) > bestMatchDistance)
678 : {
679 0 : aFontsForWeights[i] = 0;
680 : }
681 : }
682 :
683 0 : return (foundWeights > 0);
684 : }
685 :
686 :
687 0 : void gfxFontFamily::LocalizedName(nsAString& aLocalizedName)
688 : {
689 : // just return the primary name; subclasses should override
690 0 : aLocalizedName = mName;
691 0 : }
692 :
693 : // metric for how close a given font matches a style
694 : static PRInt32
695 0 : CalcStyleMatch(gfxFontEntry *aFontEntry, const gfxFontStyle *aStyle)
696 : {
697 0 : PRInt32 rank = 0;
698 0 : if (aStyle) {
699 : // italics
700 : bool wantItalic =
701 0 : ((aStyle->style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)) != 0);
702 0 : if (aFontEntry->IsItalic() == wantItalic) {
703 0 : rank += 10;
704 : }
705 :
706 : // measure of closeness of weight to the desired value
707 0 : rank += 9 - abs(aFontEntry->Weight() / 100 - aStyle->ComputeWeight());
708 : } else {
709 : // if no font to match, prefer non-bold, non-italic fonts
710 0 : if (!aFontEntry->IsItalic()) {
711 0 : rank += 3;
712 : }
713 0 : if (!aFontEntry->IsBold()) {
714 0 : rank += 2;
715 : }
716 : }
717 :
718 0 : return rank;
719 : }
720 :
721 : #define RANK_MATCHED_CMAP 20
722 :
723 : void
724 0 : gfxFontFamily::FindFontForChar(GlobalFontMatch *aMatchData)
725 : {
726 0 : if (mCharacterMapInitialized && !TestCharacterMap(aMatchData->mCh)) {
727 : // none of the faces in the family support the required char,
728 : // so bail out immediately
729 0 : return;
730 : }
731 :
732 : bool needsBold;
733 0 : gfxFontStyle normal;
734 : gfxFontEntry *fe = FindFontForStyle(
735 : (aMatchData->mStyle == nsnull) ? *aMatchData->mStyle : normal,
736 0 : needsBold);
737 :
738 0 : if (fe && !fe->SkipDuringSystemFallback()) {
739 0 : PRInt32 rank = 0;
740 :
741 0 : if (fe->TestCharacterMap(aMatchData->mCh)) {
742 0 : rank += RANK_MATCHED_CMAP;
743 0 : aMatchData->mCount++;
744 : #ifdef PR_LOGGING
745 0 : PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textrun);
746 :
747 0 : if (NS_UNLIKELY(log)) {
748 0 : PRUint32 charRange = gfxFontUtils::CharRangeBit(aMatchData->mCh);
749 0 : PRUint32 unicodeRange = FindCharUnicodeRange(aMatchData->mCh);
750 0 : PRUint32 script = GetScriptCode(aMatchData->mCh);
751 0 : PR_LOG(log, PR_LOG_DEBUG,\
752 : ("(textrun-systemfallback-fonts) char: u+%6.6x "
753 : "char-range: %d unicode-range: %d script: %d match: [%s]\n",
754 : aMatchData->mCh,
755 : charRange, unicodeRange, script,
756 : NS_ConvertUTF16toUTF8(fe->Name()).get()));
757 : }
758 : #endif
759 : }
760 :
761 0 : aMatchData->mCmapsTested++;
762 0 : if (rank == 0) {
763 : return;
764 : }
765 :
766 : // omitting from original windows code -- family name, lang group, pitch
767 : // not available in current FontEntry implementation
768 0 : rank += CalcStyleMatch(fe, aMatchData->mStyle);
769 :
770 : // xxx - add whether AAT font with morphing info for specific lang groups
771 :
772 0 : if (rank > aMatchData->mMatchRank
773 : || (rank == aMatchData->mMatchRank &&
774 0 : Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0))
775 : {
776 0 : aMatchData->mBestMatch = fe;
777 0 : aMatchData->mMatchRank = rank;
778 : }
779 : }
780 : }
781 :
782 : void
783 0 : gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch *aMatchData)
784 : {
785 0 : PRUint32 i, numFonts = mAvailableFonts.Length();
786 0 : for (i = 0; i < numFonts; i++) {
787 0 : gfxFontEntry *fe = mAvailableFonts[i];
788 0 : if (fe && fe->TestCharacterMap(aMatchData->mCh)) {
789 0 : PRInt32 rank = RANK_MATCHED_CMAP;
790 0 : rank += CalcStyleMatch(fe, aMatchData->mStyle);
791 0 : if (rank > aMatchData->mMatchRank
792 : || (rank == aMatchData->mMatchRank &&
793 0 : Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0))
794 : {
795 0 : aMatchData->mBestMatch = fe;
796 0 : aMatchData->mMatchRank = rank;
797 : }
798 : }
799 : }
800 0 : }
801 :
802 : // returns true if other names were found, false otherwise
803 : bool
804 0 : gfxFontFamily::ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList,
805 : FallibleTArray<PRUint8>& aNameTable,
806 : bool useFullName)
807 : {
808 0 : const PRUint8 *nameData = aNameTable.Elements();
809 0 : PRUint32 dataLength = aNameTable.Length();
810 : const gfxFontUtils::NameHeader *nameHeader =
811 0 : reinterpret_cast<const gfxFontUtils::NameHeader*>(nameData);
812 :
813 0 : PRUint32 nameCount = nameHeader->count;
814 0 : if (nameCount * sizeof(gfxFontUtils::NameRecord) > dataLength) {
815 0 : NS_WARNING("invalid font (name records)");
816 0 : return false;
817 : }
818 :
819 : const gfxFontUtils::NameRecord *nameRecord =
820 0 : reinterpret_cast<const gfxFontUtils::NameRecord*>(nameData + sizeof(gfxFontUtils::NameHeader));
821 0 : PRUint32 stringsBase = PRUint32(nameHeader->stringOffset);
822 :
823 0 : bool foundNames = false;
824 0 : for (PRUint32 i = 0; i < nameCount; i++, nameRecord++) {
825 0 : PRUint32 nameLen = nameRecord->length;
826 0 : PRUint32 nameOff = nameRecord->offset; // offset from base of string storage
827 :
828 0 : if (stringsBase + nameOff + nameLen > dataLength) {
829 0 : NS_WARNING("invalid font (name table strings)");
830 0 : return false;
831 : }
832 :
833 0 : PRUint16 nameID = nameRecord->nameID;
834 0 : if ((useFullName && nameID == gfxFontUtils::NAME_ID_FULL) ||
835 0 : (!useFullName && (nameID == gfxFontUtils::NAME_ID_FAMILY ||
836 : nameID == gfxFontUtils::NAME_ID_PREFERRED_FAMILY))) {
837 0 : nsAutoString otherFamilyName;
838 0 : bool ok = gfxFontUtils::DecodeFontName(nameData + stringsBase + nameOff,
839 : nameLen,
840 : PRUint32(nameRecord->platformID),
841 : PRUint32(nameRecord->encodingID),
842 : PRUint32(nameRecord->languageID),
843 0 : otherFamilyName);
844 : // add if not same as canonical family name
845 0 : if (ok && otherFamilyName != mName) {
846 0 : aPlatformFontList->AddOtherFamilyName(this, otherFamilyName);
847 0 : foundNames = true;
848 : }
849 : }
850 : }
851 :
852 0 : return foundNames;
853 : }
854 :
855 :
856 : void
857 0 : gfxFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
858 : {
859 0 : if (mOtherFamilyNamesInitialized)
860 0 : return;
861 0 : mOtherFamilyNamesInitialized = true;
862 :
863 0 : FindStyleVariations();
864 :
865 : // read in other family names for the first face in the list
866 0 : PRUint32 i, numFonts = mAvailableFonts.Length();
867 0 : const PRUint32 kNAME = TRUETYPE_TAG('n','a','m','e');
868 0 : AutoFallibleTArray<PRUint8,8192> buffer;
869 :
870 0 : for (i = 0; i < numFonts; ++i) {
871 0 : gfxFontEntry *fe = mAvailableFonts[i];
872 0 : if (!fe)
873 0 : continue;
874 :
875 0 : if (fe->GetFontTable(kNAME, buffer) != NS_OK)
876 0 : continue;
877 :
878 : mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList,
879 0 : buffer);
880 0 : break;
881 : }
882 :
883 : // read in other names for the first face in the list with the assumption
884 : // that if extra names don't exist in that face then they don't exist in
885 : // other faces for the same font
886 0 : if (!mHasOtherFamilyNames)
887 : return;
888 :
889 : // read in names for all faces, needed to catch cases where fonts have
890 : // family names for individual weights (e.g. Hiragino Kaku Gothic Pro W6)
891 0 : for ( ; i < numFonts; i++) {
892 0 : gfxFontEntry *fe = mAvailableFonts[i];
893 0 : if (!fe)
894 0 : continue;
895 :
896 0 : if (fe->GetFontTable(kNAME, buffer) != NS_OK)
897 0 : continue;
898 :
899 0 : ReadOtherFamilyNamesForFace(aPlatformFontList, buffer);
900 : }
901 : }
902 :
903 : void
904 0 : gfxFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList,
905 : bool aNeedFullnamePostscriptNames)
906 : {
907 : // if all needed names have already been read, skip
908 0 : if (mOtherFamilyNamesInitialized &&
909 0 : (mFaceNamesInitialized || !aNeedFullnamePostscriptNames))
910 0 : return;
911 :
912 0 : FindStyleVariations();
913 :
914 0 : PRUint32 i, numFonts = mAvailableFonts.Length();
915 0 : const PRUint32 kNAME = TRUETYPE_TAG('n','a','m','e');
916 0 : AutoFallibleTArray<PRUint8,8192> buffer;
917 0 : nsAutoString fullname, psname;
918 :
919 0 : bool firstTime = true, readAllFaces = false;
920 0 : for (i = 0; i < numFonts; ++i) {
921 0 : gfxFontEntry *fe = mAvailableFonts[i];
922 0 : if (!fe)
923 0 : continue;
924 :
925 0 : if (fe->GetFontTable(kNAME, buffer) != NS_OK)
926 0 : continue;
927 :
928 0 : if (aNeedFullnamePostscriptNames) {
929 0 : if (gfxFontUtils::ReadCanonicalName(
930 0 : buffer, gfxFontUtils::NAME_ID_FULL, fullname) == NS_OK)
931 : {
932 0 : aPlatformFontList->AddFullname(fe, fullname);
933 : }
934 :
935 0 : if (gfxFontUtils::ReadCanonicalName(
936 0 : buffer, gfxFontUtils::NAME_ID_POSTSCRIPT, psname) == NS_OK)
937 : {
938 0 : aPlatformFontList->AddPostscriptName(fe, psname);
939 : }
940 : }
941 :
942 0 : if (!mOtherFamilyNamesInitialized && (firstTime || readAllFaces)) {
943 : bool foundOtherName = ReadOtherFamilyNamesForFace(aPlatformFontList,
944 0 : buffer);
945 :
946 : // if the first face has a different name, scan all faces, otherwise
947 : // assume the family doesn't have other names
948 0 : if (firstTime && foundOtherName) {
949 0 : mHasOtherFamilyNames = true;
950 0 : readAllFaces = true;
951 : }
952 0 : firstTime = false;
953 : }
954 :
955 : // if not reading in any more names, skip other faces
956 0 : if (!readAllFaces && !aNeedFullnamePostscriptNames)
957 0 : break;
958 : }
959 :
960 0 : mFaceNamesInitialized = true;
961 0 : mOtherFamilyNamesInitialized = true;
962 : }
963 :
964 :
965 : gfxFontEntry*
966 0 : gfxFontFamily::FindFont(const nsAString& aPostscriptName)
967 : {
968 : // find the font using a simple linear search
969 0 : PRUint32 numFonts = mAvailableFonts.Length();
970 0 : for (PRUint32 i = 0; i < numFonts; i++) {
971 0 : gfxFontEntry *fe = mAvailableFonts[i].get();
972 0 : if (fe && fe->Name() == aPostscriptName)
973 0 : return fe;
974 : }
975 0 : return nsnull;
976 : }
977 :
978 : /*
979 : * gfxFontCache - global cache of gfxFont instances.
980 : * Expires unused fonts after a short interval;
981 : * notifies fonts to age their cached shaped-word records;
982 : * observes memory-pressure notification and tells fonts to clear their
983 : * shaped-word caches to free up memory.
984 : */
985 :
986 : // Observer for the memory-pressure notification, to trigger
987 : // flushing of the shaped-word caches
988 : class MemoryPressureObserver : public nsIObserver,
989 : public nsSupportsWeakReference
990 6 : {
991 : public:
992 : NS_DECL_ISUPPORTS
993 : NS_DECL_NSIOBSERVER
994 : };
995 :
996 9 : NS_IMPL_ISUPPORTS2(MemoryPressureObserver, nsIObserver, nsISupportsWeakReference)
997 :
998 : NS_IMETHODIMP
999 0 : MemoryPressureObserver::Observe(nsISupports *aSubject,
1000 : const char *aTopic,
1001 : const PRUnichar *someData)
1002 : {
1003 0 : if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
1004 0 : gfxFontCache *fontCache = gfxFontCache::GetCache();
1005 0 : if (fontCache) {
1006 0 : fontCache->FlushShapedWordCaches();
1007 : }
1008 : }
1009 0 : return NS_OK;
1010 : }
1011 :
1012 : nsresult
1013 3 : gfxFontCache::Init()
1014 : {
1015 3 : NS_ASSERTION(!gGlobalCache, "Where did this come from?");
1016 3 : gGlobalCache = new gfxFontCache();
1017 3 : return gGlobalCache ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
1018 : }
1019 :
1020 : void
1021 4 : gfxFontCache::Shutdown()
1022 : {
1023 4 : delete gGlobalCache;
1024 4 : gGlobalCache = nsnull;
1025 :
1026 : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
1027 : printf("Textrun storage high water mark=%d\n", gTextRunStorageHighWaterMark);
1028 : printf("Total number of fonts=%d\n", gFontCount);
1029 : printf("Total glyph extents allocated=%d (size %d)\n", gGlyphExtentsCount,
1030 : int(gGlyphExtentsCount*sizeof(gfxGlyphExtents)));
1031 : printf("Total glyph extents width-storage size allocated=%d\n", gGlyphExtentsWidthsTotalSize);
1032 : printf("Number of simple glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerSimple);
1033 : printf("Number of tight glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerTight);
1034 : printf("Number of tight glyph extents lazily requested=%d\n", gGlyphExtentsSetupLazyTight);
1035 : printf("Number of simple glyph extent setups that fell back to tight=%d\n", gGlyphExtentsSetupFallBackToTight);
1036 : #endif
1037 4 : }
1038 :
1039 3 : gfxFontCache::gfxFontCache()
1040 3 : : nsExpirationTracker<gfxFont,3>(FONT_TIMEOUT_SECONDS * 1000)
1041 : {
1042 3 : mFonts.Init();
1043 :
1044 6 : nsCOMPtr<nsIObserverService> obs = GetObserverService();
1045 3 : if (obs) {
1046 6 : obs->AddObserver(new MemoryPressureObserver, "memory-pressure", false);
1047 : }
1048 :
1049 : #if 0 // disabled due to crashiness, see bug 717175
1050 : mWordCacheExpirationTimer = do_CreateInstance("@mozilla.org/timer;1");
1051 : if (mWordCacheExpirationTimer) {
1052 : mWordCacheExpirationTimer->
1053 : InitWithFuncCallback(WordCacheExpirationTimerCallback, this,
1054 : SHAPED_WORD_TIMEOUT_SECONDS * 1000,
1055 : nsITimer::TYPE_REPEATING_SLACK);
1056 : }
1057 : #endif
1058 3 : }
1059 :
1060 6 : gfxFontCache::~gfxFontCache()
1061 : {
1062 3 : if (mWordCacheExpirationTimer) {
1063 0 : mWordCacheExpirationTimer->Cancel();
1064 0 : mWordCacheExpirationTimer = nsnull;
1065 : }
1066 :
1067 : // Expire everything that has a zero refcount, so we don't leak them.
1068 3 : AgeAllGenerations();
1069 : // All fonts should be gone.
1070 3 : NS_WARN_IF_FALSE(mFonts.Count() == 0,
1071 : "Fonts still alive while shutting down gfxFontCache");
1072 : // Note that we have to delete everything through the expiration
1073 : // tracker, since there might be fonts not in the hashtable but in
1074 : // the tracker.
1075 3 : }
1076 :
1077 : bool
1078 0 : gfxFontCache::HashEntry::KeyEquals(const KeyTypePointer aKey) const
1079 : {
1080 0 : return aKey->mFontEntry == mFont->GetFontEntry() &&
1081 0 : aKey->mStyle->Equals(*mFont->GetStyle());
1082 : }
1083 :
1084 : already_AddRefed<gfxFont>
1085 0 : gfxFontCache::Lookup(const gfxFontEntry *aFontEntry,
1086 : const gfxFontStyle *aStyle)
1087 : {
1088 0 : Key key(aFontEntry, aStyle);
1089 0 : HashEntry *entry = mFonts.GetEntry(key);
1090 :
1091 0 : Telemetry::Accumulate(Telemetry::FONT_CACHE_HIT, entry != nsnull);
1092 0 : if (!entry)
1093 0 : return nsnull;
1094 :
1095 0 : gfxFont *font = entry->mFont;
1096 0 : NS_ADDREF(font);
1097 0 : return font;
1098 : }
1099 :
1100 : void
1101 0 : gfxFontCache::AddNew(gfxFont *aFont)
1102 : {
1103 0 : Key key(aFont->GetFontEntry(), aFont->GetStyle());
1104 0 : HashEntry *entry = mFonts.PutEntry(key);
1105 0 : if (!entry)
1106 0 : return;
1107 0 : gfxFont *oldFont = entry->mFont;
1108 0 : entry->mFont = aFont;
1109 : // If someone's asked us to replace an existing font entry, then that's a
1110 : // bit weird, but let it happen, and expire the old font if it's not used.
1111 0 : if (oldFont && oldFont->GetExpirationState()->IsTracked()) {
1112 : // if oldFont == aFont, recount should be > 0,
1113 : // so we shouldn't be here.
1114 0 : NS_ASSERTION(aFont != oldFont, "new font is tracked for expiry!");
1115 0 : NotifyExpired(oldFont);
1116 : }
1117 : }
1118 :
1119 : void
1120 0 : gfxFontCache::NotifyReleased(gfxFont *aFont)
1121 : {
1122 0 : nsresult rv = AddObject(aFont);
1123 0 : if (NS_FAILED(rv)) {
1124 : // We couldn't track it for some reason. Kill it now.
1125 0 : DestroyFont(aFont);
1126 : }
1127 : // Note that we might have fonts that aren't in the hashtable, perhaps because
1128 : // of OOM adding to the hashtable or because someone did an AddNew where
1129 : // we already had a font. These fonts are added to the expiration tracker
1130 : // anyway, even though Lookup can't resurrect them. Eventually they will
1131 : // expire and be deleted.
1132 0 : }
1133 :
1134 : void
1135 0 : gfxFontCache::NotifyExpired(gfxFont *aFont)
1136 : {
1137 0 : aFont->ClearCachedWords();
1138 0 : RemoveObject(aFont);
1139 0 : DestroyFont(aFont);
1140 0 : }
1141 :
1142 : void
1143 0 : gfxFontCache::DestroyFont(gfxFont *aFont)
1144 : {
1145 0 : Key key(aFont->GetFontEntry(), aFont->GetStyle());
1146 0 : HashEntry *entry = mFonts.GetEntry(key);
1147 0 : if (entry && entry->mFont == aFont)
1148 0 : mFonts.RemoveEntry(key);
1149 0 : NS_ASSERTION(aFont->GetRefCount() == 0,
1150 : "Destroying with non-zero ref count!");
1151 0 : delete aFont;
1152 0 : }
1153 :
1154 : /*static*/
1155 : PLDHashOperator
1156 0 : gfxFontCache::AgeCachedWordsForFont(HashEntry* aHashEntry, void* aUserData)
1157 : {
1158 0 : aHashEntry->mFont->AgeCachedWords();
1159 0 : return PL_DHASH_NEXT;
1160 : }
1161 :
1162 : /*static*/
1163 : void
1164 0 : gfxFontCache::WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache)
1165 : {
1166 0 : gfxFontCache* cache = static_cast<gfxFontCache*>(aCache);
1167 0 : cache->mFonts.EnumerateEntries(AgeCachedWordsForFont, nsnull);
1168 0 : }
1169 :
1170 : /*static*/
1171 : PLDHashOperator
1172 0 : gfxFontCache::ClearCachedWordsForFont(HashEntry* aHashEntry, void* aUserData)
1173 : {
1174 0 : aHashEntry->mFont->ClearCachedWords();
1175 0 : return PL_DHASH_NEXT;
1176 : }
1177 :
1178 : void
1179 0 : gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther, bool aOtherIsOnLeft)
1180 : {
1181 0 : mAscent = NS_MAX(mAscent, aOther.mAscent);
1182 0 : mDescent = NS_MAX(mDescent, aOther.mDescent);
1183 0 : if (aOtherIsOnLeft) {
1184 : mBoundingBox =
1185 0 : (mBoundingBox + gfxPoint(aOther.mAdvanceWidth, 0)).Union(aOther.mBoundingBox);
1186 : } else {
1187 : mBoundingBox =
1188 0 : mBoundingBox.Union(aOther.mBoundingBox + gfxPoint(mAdvanceWidth, 0));
1189 : }
1190 0 : mAdvanceWidth += aOther.mAdvanceWidth;
1191 0 : }
1192 :
1193 0 : gfxFont::gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
1194 : AntialiasOption anAAOption, cairo_scaled_font_t *aScaledFont) :
1195 : mScaledFont(aScaledFont),
1196 : mFontEntry(aFontEntry), mIsValid(true),
1197 : mApplySyntheticBold(false),
1198 : mStyle(*aFontStyle),
1199 : mAdjustedSize(0.0),
1200 : mFUnitsConvFactor(0.0f),
1201 0 : mAntialiasOption(anAAOption)
1202 : {
1203 : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
1204 : ++gFontCount;
1205 : #endif
1206 0 : }
1207 :
1208 0 : gfxFont::~gfxFont()
1209 : {
1210 : PRUint32 i;
1211 : // We destroy the contents of mGlyphExtentsArray explicitly instead of
1212 : // using nsAutoPtr because VC++ can't deal with nsTArrays of nsAutoPtrs
1213 : // of classes that lack a proper copy constructor
1214 0 : for (i = 0; i < mGlyphExtentsArray.Length(); ++i) {
1215 0 : delete mGlyphExtentsArray[i];
1216 : }
1217 0 : }
1218 :
1219 : /*static*/
1220 : PLDHashOperator
1221 0 : gfxFont::AgeCacheEntry(CacheHashEntry *aEntry, void *aUserData)
1222 : {
1223 0 : if (!aEntry->mShapedWord) {
1224 0 : NS_ASSERTION(aEntry->mShapedWord, "cache entry has no gfxShapedWord!");
1225 0 : return PL_DHASH_REMOVE;
1226 : }
1227 0 : if (aEntry->mShapedWord->IncrementAge() == kShapedWordCacheMaxAge) {
1228 0 : return PL_DHASH_REMOVE;
1229 : }
1230 0 : return PL_DHASH_NEXT;
1231 : }
1232 :
1233 : hb_blob_t *
1234 0 : gfxFont::GetFontTable(PRUint32 aTag) {
1235 : hb_blob_t *blob;
1236 0 : if (mFontEntry->GetExistingFontTable(aTag, &blob))
1237 0 : return blob;
1238 :
1239 0 : FallibleTArray<PRUint8> buffer;
1240 0 : bool haveTable = NS_SUCCEEDED(mFontEntry->GetFontTable(aTag, buffer));
1241 :
1242 : return mFontEntry->ShareFontTableAndGetBlob(aTag,
1243 0 : haveTable ? &buffer : nsnull);
1244 : }
1245 :
1246 : /**
1247 : * A helper function in case we need to do any rounding or other
1248 : * processing here.
1249 : */
1250 : #define ToDeviceUnits(aAppUnits, aDevUnitsPerAppUnit) \
1251 : (double(aAppUnits)*double(aDevUnitsPerAppUnit))
1252 :
1253 : struct GlyphBuffer {
1254 : #define GLYPH_BUFFER_SIZE (2048/sizeof(cairo_glyph_t))
1255 : cairo_glyph_t mGlyphBuffer[GLYPH_BUFFER_SIZE];
1256 : unsigned int mNumGlyphs;
1257 :
1258 0 : GlyphBuffer()
1259 0 : : mNumGlyphs(0) { }
1260 :
1261 0 : cairo_glyph_t *AppendGlyph() {
1262 0 : return &mGlyphBuffer[mNumGlyphs++];
1263 : }
1264 :
1265 0 : void Flush(cairo_t *aCR, cairo_pattern_t *aStrokePattern,
1266 : gfxFont::DrawMode aDrawMode, bool aReverse,
1267 : bool aFinish = false) {
1268 : // Ensure there's enough room for a glyph to be added to the buffer
1269 0 : if (!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE) {
1270 0 : return;
1271 : }
1272 :
1273 0 : if (aReverse) {
1274 0 : for (PRUint32 i = 0; i < mNumGlyphs/2; ++i) {
1275 0 : cairo_glyph_t tmp = mGlyphBuffer[i];
1276 0 : mGlyphBuffer[i] = mGlyphBuffer[mNumGlyphs - 1 - i];
1277 0 : mGlyphBuffer[mNumGlyphs - 1 - i] = tmp;
1278 : }
1279 : }
1280 :
1281 0 : if (aDrawMode == gfxFont::GLYPH_PATH) {
1282 0 : cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs);
1283 : } else {
1284 0 : if (aDrawMode & gfxFont::GLYPH_FILL) {
1285 0 : cairo_show_glyphs(aCR, mGlyphBuffer, mNumGlyphs);
1286 : }
1287 :
1288 0 : if (aDrawMode & gfxFont::GLYPH_STROKE) {
1289 0 : if (aStrokePattern) {
1290 0 : cairo_save(aCR);
1291 0 : cairo_set_source(aCR, aStrokePattern);
1292 : }
1293 :
1294 0 : cairo_new_path(aCR);
1295 0 : cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs);
1296 0 : cairo_stroke(aCR);
1297 :
1298 0 : if (aStrokePattern) {
1299 0 : cairo_restore(aCR);
1300 : }
1301 : }
1302 : }
1303 :
1304 0 : mNumGlyphs = 0;
1305 : }
1306 : #undef GLYPH_BUFFER_SIZE
1307 : };
1308 :
1309 : struct GlyphBufferAzure {
1310 : #define GLYPH_BUFFER_SIZE (2048/sizeof(Glyph))
1311 : Glyph mGlyphBuffer[GLYPH_BUFFER_SIZE];
1312 : unsigned int mNumGlyphs;
1313 :
1314 0 : GlyphBufferAzure()
1315 0 : : mNumGlyphs(0) { }
1316 :
1317 0 : Glyph *AppendGlyph() {
1318 0 : return &mGlyphBuffer[mNumGlyphs++];
1319 : }
1320 :
1321 0 : void Flush(DrawTarget *aDT, Pattern &aPattern, ScaledFont *aFont,
1322 : gfxFont::DrawMode aDrawMode, bool aReverse, bool aFinish = false)
1323 : {
1324 : // Ensure there's enough room for a glyph to be added to the buffer
1325 0 : if (!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE || !mNumGlyphs) {
1326 0 : return;
1327 : }
1328 :
1329 0 : if (aReverse) {
1330 0 : Glyph *begin = &mGlyphBuffer[0];
1331 0 : Glyph *end = &mGlyphBuffer[mNumGlyphs];
1332 0 : std::reverse(begin, end);
1333 : }
1334 :
1335 0 : NS_ASSERTION(aDrawMode != gfxFont::GLYPH_FILL, "Not supported yet.");
1336 :
1337 : gfx::GlyphBuffer buf;
1338 0 : buf.mGlyphs = mGlyphBuffer;
1339 0 : buf.mNumGlyphs = mNumGlyphs;
1340 :
1341 0 : aDT->FillGlyphs(aFont, buf, aPattern);
1342 :
1343 0 : mNumGlyphs = 0;
1344 : }
1345 : #undef GLYPH_BUFFER_SIZE
1346 : };
1347 :
1348 : // Bug 674909. When synthetic bolding text by drawing twice, need to
1349 : // render using a pixel offset in device pixels, otherwise text
1350 : // doesn't appear bolded, it appears as if a bad text shadow exists
1351 : // when a non-identity transform exists. Use an offset factor so that
1352 : // the second draw occurs at a constant offset in device pixels.
1353 :
1354 : double
1355 0 : gfxFont::CalcXScale(gfxContext *aContext)
1356 : {
1357 : // determine magnitude of a 1px x offset in device space
1358 0 : gfxSize t = aContext->UserToDevice(gfxSize(1.0, 0.0));
1359 0 : if (t.width == 1.0 && t.height == 0.0) {
1360 : // short-circuit the most common case to avoid sqrt() and division
1361 0 : return 1.0;
1362 : }
1363 :
1364 0 : double m = sqrt(t.width * t.width + t.height * t.height);
1365 :
1366 0 : NS_ASSERTION(m != 0.0, "degenerate transform while synthetic bolding");
1367 0 : if (m == 0.0) {
1368 0 : return 0.0; // effectively disables offset
1369 : }
1370 :
1371 : // scale factor so that offsets are 1px in device pixels
1372 0 : return 1.0 / m;
1373 : }
1374 :
1375 : void
1376 0 : gfxFont::Draw(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
1377 : gfxContext *aContext, DrawMode aDrawMode, gfxPoint *aPt,
1378 : Spacing *aSpacing, gfxPattern *aStrokePattern)
1379 : {
1380 0 : NS_ASSERTION(aDrawMode <= gfxFont::GLYPH_PATH, "GLYPH_PATH cannot be used with GLYPH_FILL or GLYPH_STROKE");
1381 :
1382 : // We have to multiply the stroke matrix by the context matrix as cairo
1383 : // multiplies by the inverse of the context matrix when the pattern is set
1384 0 : gfxMatrix strokeMatrix;
1385 0 : if (aStrokePattern) {
1386 0 : strokeMatrix = aStrokePattern->GetMatrix();
1387 0 : aStrokePattern->SetMatrix(aContext->CurrentMatrix().Multiply(strokeMatrix));
1388 : }
1389 :
1390 0 : if (aStart >= aEnd)
1391 0 : return;
1392 :
1393 0 : const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
1394 0 : const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
1395 0 : const double devUnitsPerAppUnit = 1.0/double(appUnitsPerDevUnit);
1396 0 : bool isRTL = aTextRun->IsRightToLeft();
1397 0 : double direction = aTextRun->GetDirection();
1398 :
1399 : // synthetic-bold strikes are each offset one device pixel in run direction
1400 : // (these values are only needed if IsSyntheticBold() is true)
1401 0 : double synBoldOnePixelOffset = 0;
1402 0 : PRInt32 strikes = 0;
1403 0 : if (IsSyntheticBold()) {
1404 0 : double xscale = CalcXScale(aContext);
1405 0 : synBoldOnePixelOffset = direction * xscale;
1406 : // use as many strikes as needed for the the increased advance
1407 0 : strikes = NS_lroundf(GetSyntheticBoldOffset() / xscale);
1408 : }
1409 :
1410 : PRUint32 i;
1411 : // Current position in appunits
1412 0 : double x = aPt->x;
1413 0 : double y = aPt->y;
1414 :
1415 0 : cairo_t *cr = aContext->GetCairo();
1416 0 : RefPtr<DrawTarget> dt = aContext->GetDrawTarget();
1417 0 : cairo_pattern_t *strokePattern = nsnull;
1418 0 : if (aStrokePattern) {
1419 0 : strokePattern = aStrokePattern->CairoPattern();
1420 : }
1421 :
1422 0 : RefPtr<ScaledFont> scaledFont;
1423 :
1424 0 : gfxRGBA color;
1425 0 : ColorPattern colPat(Color(0, 0, 0, 0));
1426 :
1427 0 : if (aContext->IsCairo()) {
1428 0 : bool success = SetupCairoFont(aContext);
1429 0 : if (NS_UNLIKELY(!success))
1430 : return;
1431 :
1432 0 : ::GlyphBuffer glyphs;
1433 : cairo_glyph_t *glyph;
1434 :
1435 0 : if (aSpacing) {
1436 0 : x += direction*aSpacing[0].mBefore;
1437 : }
1438 0 : for (i = aStart; i < aEnd; ++i) {
1439 0 : const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
1440 0 : if (glyphData->IsSimpleGlyph()) {
1441 0 : glyph = glyphs.AppendGlyph();
1442 0 : glyph->index = glyphData->GetSimpleGlyph();
1443 0 : double advance = glyphData->GetSimpleAdvance();
1444 : // Perhaps we should put a scale in the cairo context instead of
1445 : // doing this scaling here...
1446 : // Multiplying by the reciprocal may introduce tiny error here,
1447 : // but we assume cairo is going to round coordinates at some stage
1448 : // and this is faster
1449 : double glyphX;
1450 0 : if (isRTL) {
1451 0 : x -= advance;
1452 0 : glyphX = x;
1453 : } else {
1454 0 : glyphX = x;
1455 0 : x += advance;
1456 : }
1457 0 : glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
1458 0 : glyph->y = ToDeviceUnits(y, devUnitsPerAppUnit);
1459 0 : glyphs.Flush(cr, strokePattern, aDrawMode, isRTL);
1460 :
1461 : // synthetic bolding by multi-striking with 1-pixel offsets
1462 : // at least once, more if there's room (large font sizes)
1463 0 : if (IsSyntheticBold()) {
1464 0 : double strikeOffset = synBoldOnePixelOffset;
1465 0 : PRInt32 strikeCount = strikes;
1466 0 : do {
1467 : cairo_glyph_t *doubleglyph;
1468 0 : doubleglyph = glyphs.AppendGlyph();
1469 0 : doubleglyph->index = glyph->index;
1470 : doubleglyph->x =
1471 : ToDeviceUnits(glyphX + strikeOffset * appUnitsPerDevUnit,
1472 0 : devUnitsPerAppUnit);
1473 0 : doubleglyph->y = glyph->y;
1474 0 : strikeOffset += synBoldOnePixelOffset;
1475 0 : glyphs.Flush(cr, strokePattern, aDrawMode, isRTL);
1476 : } while (--strikeCount > 0);
1477 : }
1478 : } else {
1479 0 : PRUint32 glyphCount = glyphData->GetGlyphCount();
1480 0 : if (glyphCount > 0) {
1481 : const gfxTextRun::DetailedGlyph *details =
1482 0 : aTextRun->GetDetailedGlyphs(i);
1483 0 : NS_ASSERTION(details, "detailedGlyph should not be missing!");
1484 0 : for (PRUint32 j = 0; j < glyphCount; ++j, ++details) {
1485 0 : double advance = details->mAdvance;
1486 0 : if (glyphData->IsMissing()) {
1487 : // default ignorable characters will have zero advance width.
1488 : // we don't have to draw the hexbox for them
1489 0 : if (aDrawMode != gfxFont::GLYPH_PATH && advance > 0) {
1490 0 : double glyphX = x;
1491 0 : if (isRTL) {
1492 0 : glyphX -= advance;
1493 : }
1494 : gfxPoint pt(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
1495 0 : ToDeviceUnits(y, devUnitsPerAppUnit));
1496 0 : gfxFloat advanceDevUnits = ToDeviceUnits(advance, devUnitsPerAppUnit);
1497 0 : gfxFloat height = GetMetrics().maxAscent;
1498 0 : gfxRect glyphRect(pt.x, pt.y - height, advanceDevUnits, height);
1499 : gfxFontMissingGlyphs::DrawMissingGlyph(aContext,
1500 : glyphRect,
1501 0 : details->mGlyphID);
1502 : }
1503 : } else {
1504 0 : glyph = glyphs.AppendGlyph();
1505 0 : glyph->index = details->mGlyphID;
1506 0 : double glyphX = x + details->mXOffset;
1507 0 : if (isRTL) {
1508 0 : glyphX -= advance;
1509 : }
1510 0 : glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
1511 0 : glyph->y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit);
1512 0 : glyphs.Flush(cr, strokePattern, aDrawMode, isRTL);
1513 :
1514 0 : if (IsSyntheticBold()) {
1515 0 : double strikeOffset = synBoldOnePixelOffset;
1516 0 : PRInt32 strikeCount = strikes;
1517 0 : do {
1518 : cairo_glyph_t *doubleglyph;
1519 0 : doubleglyph = glyphs.AppendGlyph();
1520 0 : doubleglyph->index = glyph->index;
1521 : doubleglyph->x =
1522 : ToDeviceUnits(glyphX + strikeOffset *
1523 : appUnitsPerDevUnit,
1524 0 : devUnitsPerAppUnit);
1525 0 : doubleglyph->y = glyph->y;
1526 0 : strikeOffset += synBoldOnePixelOffset;
1527 0 : glyphs.Flush(cr, strokePattern, aDrawMode, isRTL);
1528 : } while (--strikeCount > 0);
1529 : }
1530 : }
1531 0 : x += direction*advance;
1532 : }
1533 : }
1534 : }
1535 :
1536 0 : if (aSpacing) {
1537 0 : double space = aSpacing[i - aStart].mAfter;
1538 0 : if (i + 1 < aEnd) {
1539 0 : space += aSpacing[i + 1 - aStart].mBefore;
1540 : }
1541 0 : x += direction*space;
1542 : }
1543 : }
1544 :
1545 0 : if (gfxFontTestStore::CurrentStore()) {
1546 : /* This assumes that the tests won't have anything that results
1547 : * in more than GLYPH_BUFFER_SIZE glyphs. Do this before we
1548 : * flush, since that'll blow away the num_glyphs.
1549 : */
1550 0 : gfxFontTestStore::CurrentStore()->AddItem(GetName(),
1551 : glyphs.mGlyphBuffer,
1552 0 : glyphs.mNumGlyphs);
1553 : }
1554 :
1555 : // draw any remaining glyphs
1556 0 : glyphs.Flush(cr, strokePattern, aDrawMode, isRTL, true);
1557 :
1558 : } else {
1559 0 : if (aDrawMode == gfxFont::GLYPH_PATH) {
1560 : // This should never be reached with azure!
1561 0 : NS_ERROR("Attempt at drawing to a Path to an Azure gfxContext.");
1562 : return;
1563 : }
1564 :
1565 : scaledFont =
1566 0 : gfxPlatform::GetPlatform()->GetScaledFontForFont(this);
1567 :
1568 0 : if (!scaledFont || !aContext->GetDeviceColor(color)) {
1569 : return;
1570 : }
1571 :
1572 0 : colPat.mColor = ToColor(color);
1573 :
1574 0 : GlyphBufferAzure glyphs;
1575 : Glyph *glyph;
1576 :
1577 0 : Matrix mat, matInv;
1578 0 : Matrix oldMat = dt->GetTransform();
1579 :
1580 0 : if (mScaledFont) {
1581 : cairo_matrix_t matrix;
1582 0 : cairo_scaled_font_get_font_matrix(mScaledFont, &matrix);
1583 0 : if (matrix.xy != 0) {
1584 : // If this matrix applies a skew, which can happen when drawing
1585 : // oblique fonts, we will set the DrawTarget matrix to apply the
1586 : // skew. We'll need to move the glyphs by the inverse of the skew to
1587 : // get the glyphs positioned correctly in the new device space
1588 : // though, since the font matrix should only be applied to drawing
1589 : // the glyphs, and not to their position.
1590 0 : mat = ToMatrix(*reinterpret_cast<gfxMatrix*>(&matrix));
1591 :
1592 0 : mat._11 = mat._22 = 1.0;
1593 0 : mat._21 /= mAdjustedSize;
1594 :
1595 0 : dt->SetTransform(mat * oldMat);
1596 :
1597 0 : matInv = mat;
1598 0 : matInv.Invert();
1599 : }
1600 : }
1601 :
1602 0 : if (aSpacing) {
1603 0 : x += direction*aSpacing[0].mBefore;
1604 : }
1605 0 : for (i = aStart; i < aEnd; ++i) {
1606 0 : const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
1607 0 : if (glyphData->IsSimpleGlyph()) {
1608 0 : glyph = glyphs.AppendGlyph();
1609 0 : glyph->mIndex = glyphData->GetSimpleGlyph();
1610 0 : double advance = glyphData->GetSimpleAdvance();
1611 : // Perhaps we should put a scale in the cairo context instead of
1612 : // doing this scaling here...
1613 : // Multiplying by the reciprocal may introduce tiny error here,
1614 : // but we assume cairo is going to round coordinates at some stage
1615 : // and this is faster
1616 : double glyphX;
1617 0 : if (isRTL) {
1618 0 : x -= advance;
1619 0 : glyphX = x;
1620 : } else {
1621 0 : glyphX = x;
1622 0 : x += advance;
1623 : }
1624 0 : glyph->mPosition.x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
1625 0 : glyph->mPosition.y = ToDeviceUnits(y, devUnitsPerAppUnit);
1626 0 : glyph->mPosition = matInv * glyph->mPosition;
1627 0 : glyphs.Flush(dt, colPat, scaledFont, aDrawMode, isRTL);
1628 :
1629 : // synthetic bolding by multi-striking with 1-pixel offsets
1630 : // at least once, more if there's room (large font sizes)
1631 0 : if (IsSyntheticBold()) {
1632 0 : double strikeOffset = synBoldOnePixelOffset;
1633 0 : PRInt32 strikeCount = strikes;
1634 0 : do {
1635 : Glyph *doubleglyph;
1636 0 : doubleglyph = glyphs.AppendGlyph();
1637 0 : doubleglyph->mIndex = glyph->mIndex;
1638 : doubleglyph->mPosition.x =
1639 : ToDeviceUnits(glyphX + strikeOffset * appUnitsPerDevUnit,
1640 0 : devUnitsPerAppUnit);
1641 0 : doubleglyph->mPosition.y = glyph->mPosition.y;
1642 0 : doubleglyph->mPosition = matInv * doubleglyph->mPosition;
1643 0 : strikeOffset += synBoldOnePixelOffset;
1644 0 : glyphs.Flush(dt, colPat, scaledFont, aDrawMode, isRTL);
1645 : } while (--strikeCount > 0);
1646 : }
1647 : } else {
1648 0 : PRUint32 glyphCount = glyphData->GetGlyphCount();
1649 0 : if (glyphCount > 0) {
1650 : const gfxTextRun::DetailedGlyph *details =
1651 0 : aTextRun->GetDetailedGlyphs(i);
1652 0 : NS_ASSERTION(details, "detailedGlyph should not be missing!");
1653 0 : for (PRUint32 j = 0; j < glyphCount; ++j, ++details) {
1654 0 : double advance = details->mAdvance;
1655 0 : if (glyphData->IsMissing()) {
1656 : // default ignorable characters will have zero advance width.
1657 : // we don't have to draw the hexbox for them
1658 0 : if (aDrawMode != gfxFont::GLYPH_PATH && advance > 0) {
1659 0 : double glyphX = x;
1660 0 : if (isRTL) {
1661 0 : glyphX -= advance;
1662 : }
1663 : gfxPoint pt(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
1664 0 : ToDeviceUnits(y, devUnitsPerAppUnit));
1665 0 : gfxFloat advanceDevUnits = ToDeviceUnits(advance, devUnitsPerAppUnit);
1666 0 : gfxFloat height = GetMetrics().maxAscent;
1667 0 : gfxRect glyphRect(pt.x, pt.y - height, advanceDevUnits, height);
1668 : gfxFontMissingGlyphs::DrawMissingGlyph(aContext,
1669 : glyphRect,
1670 0 : details->mGlyphID);
1671 : }
1672 : } else {
1673 0 : glyph = glyphs.AppendGlyph();
1674 0 : glyph->mIndex = details->mGlyphID;
1675 0 : double glyphX = x + details->mXOffset;
1676 0 : if (isRTL) {
1677 0 : glyphX -= advance;
1678 : }
1679 0 : glyph->mPosition.x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
1680 0 : glyph->mPosition.y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit);
1681 0 : glyph->mPosition = matInv * glyph->mPosition;
1682 0 : glyphs.Flush(dt, colPat, scaledFont, aDrawMode, isRTL);
1683 :
1684 0 : if (IsSyntheticBold()) {
1685 0 : double strikeOffset = synBoldOnePixelOffset;
1686 0 : PRInt32 strikeCount = strikes;
1687 0 : do {
1688 : Glyph *doubleglyph;
1689 0 : doubleglyph = glyphs.AppendGlyph();
1690 0 : doubleglyph->mIndex = glyph->mIndex;
1691 : doubleglyph->mPosition.x =
1692 : ToDeviceUnits(glyphX + strikeOffset *
1693 : appUnitsPerDevUnit,
1694 0 : devUnitsPerAppUnit);
1695 0 : doubleglyph->mPosition.y = glyph->mPosition.y;
1696 0 : strikeOffset += synBoldOnePixelOffset;
1697 0 : doubleglyph->mPosition = matInv * doubleglyph->mPosition;
1698 0 : glyphs.Flush(dt, colPat, scaledFont, aDrawMode, isRTL);
1699 : } while (--strikeCount > 0);
1700 : }
1701 : }
1702 0 : x += direction*advance;
1703 : }
1704 : }
1705 : }
1706 :
1707 0 : if (aSpacing) {
1708 0 : double space = aSpacing[i - aStart].mAfter;
1709 0 : if (i + 1 < aEnd) {
1710 0 : space += aSpacing[i + 1 - aStart].mBefore;
1711 : }
1712 0 : x += direction*space;
1713 : }
1714 : }
1715 :
1716 0 : glyphs.Flush(dt, colPat, scaledFont, aDrawMode, isRTL, true);
1717 :
1718 0 : dt->SetTransform(oldMat);
1719 : }
1720 :
1721 : // Restore matrix for stroke pattern
1722 0 : if (aStrokePattern) {
1723 0 : aStrokePattern->SetMatrix(strokeMatrix);
1724 : }
1725 :
1726 0 : *aPt = gfxPoint(x, y);
1727 : }
1728 :
1729 : static void
1730 0 : UnionRange(gfxFloat aX, gfxFloat* aDestMin, gfxFloat* aDestMax)
1731 : {
1732 0 : *aDestMin = NS_MIN(*aDestMin, aX);
1733 0 : *aDestMax = NS_MAX(*aDestMax, aX);
1734 0 : }
1735 :
1736 : // We get precise glyph extents if the textrun creator requested them, or
1737 : // if the font is a user font --- in which case the author may be relying
1738 : // on overflowing glyphs.
1739 : static bool
1740 0 : NeedsGlyphExtents(gfxFont *aFont, gfxTextRun *aTextRun)
1741 : {
1742 0 : return (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX) ||
1743 0 : aFont->GetFontEntry()->IsUserFont();
1744 : }
1745 :
1746 : static bool
1747 0 : NeedsGlyphExtents(gfxTextRun *aTextRun)
1748 : {
1749 0 : if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX)
1750 0 : return true;
1751 : PRUint32 numRuns;
1752 0 : const gfxTextRun::GlyphRun *glyphRuns = aTextRun->GetGlyphRuns(&numRuns);
1753 0 : for (PRUint32 i = 0; i < numRuns; ++i) {
1754 0 : if (glyphRuns[i].mFont->GetFontEntry()->IsUserFont())
1755 0 : return true;
1756 : }
1757 0 : return false;
1758 : }
1759 :
1760 : gfxFont::RunMetrics
1761 0 : gfxFont::Measure(gfxTextRun *aTextRun,
1762 : PRUint32 aStart, PRUint32 aEnd,
1763 : BoundingBoxType aBoundingBoxType,
1764 : gfxContext *aRefContext,
1765 : Spacing *aSpacing)
1766 : {
1767 : // If aBoundingBoxType is TIGHT_HINTED_OUTLINE_EXTENTS
1768 : // and the underlying cairo font may be antialiased,
1769 : // we need to create a copy in order to avoid getting cached extents.
1770 : // This is only used by MathML layout at present.
1771 0 : if (aBoundingBoxType == TIGHT_HINTED_OUTLINE_EXTENTS &&
1772 : mAntialiasOption != kAntialiasNone) {
1773 0 : if (!mNonAAFont) {
1774 0 : mNonAAFont = CopyWithAntialiasOption(kAntialiasNone);
1775 : }
1776 : // if font subclass doesn't implement CopyWithAntialiasOption(),
1777 : // it will return null and we'll proceed to use the existing font
1778 0 : if (mNonAAFont) {
1779 0 : return mNonAAFont->Measure(aTextRun, aStart, aEnd,
1780 : TIGHT_HINTED_OUTLINE_EXTENTS,
1781 0 : aRefContext, aSpacing);
1782 : }
1783 : }
1784 :
1785 0 : const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
1786 : // Current position in appunits
1787 0 : const gfxFont::Metrics& fontMetrics = GetMetrics();
1788 :
1789 0 : RunMetrics metrics;
1790 0 : metrics.mAscent = fontMetrics.maxAscent*appUnitsPerDevUnit;
1791 0 : metrics.mDescent = fontMetrics.maxDescent*appUnitsPerDevUnit;
1792 0 : if (aStart == aEnd) {
1793 : // exit now before we look at aSpacing[0], which is undefined
1794 0 : metrics.mBoundingBox = gfxRect(0, -metrics.mAscent, 0, metrics.mAscent + metrics.mDescent);
1795 0 : return metrics;
1796 : }
1797 :
1798 0 : gfxFloat advanceMin = 0, advanceMax = 0;
1799 0 : const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
1800 0 : bool isRTL = aTextRun->IsRightToLeft();
1801 0 : double direction = aTextRun->GetDirection();
1802 0 : bool needsGlyphExtents = NeedsGlyphExtents(this, aTextRun);
1803 : gfxGlyphExtents *extents =
1804 : (aBoundingBoxType == LOOSE_INK_EXTENTS &&
1805 : !needsGlyphExtents &&
1806 0 : !aTextRun->HasDetailedGlyphs()) ? nsnull
1807 0 : : GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit());
1808 0 : double x = 0;
1809 0 : if (aSpacing) {
1810 0 : x += direction*aSpacing[0].mBefore;
1811 : }
1812 : PRUint32 i;
1813 0 : for (i = aStart; i < aEnd; ++i) {
1814 0 : const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
1815 0 : if (glyphData->IsSimpleGlyph()) {
1816 0 : double advance = glyphData->GetSimpleAdvance();
1817 : // Only get the real glyph horizontal extent if we were asked
1818 : // for the tight bounding box or we're in quality mode
1819 0 : if ((aBoundingBoxType != LOOSE_INK_EXTENTS || needsGlyphExtents) &&
1820 : extents) {
1821 0 : PRUint32 glyphIndex = glyphData->GetSimpleGlyph();
1822 0 : PRUint16 extentsWidth = extents->GetContainedGlyphWidthAppUnits(glyphIndex);
1823 0 : if (extentsWidth != gfxGlyphExtents::INVALID_WIDTH &&
1824 : aBoundingBoxType == LOOSE_INK_EXTENTS) {
1825 0 : UnionRange(x, &advanceMin, &advanceMax);
1826 0 : UnionRange(x + direction*extentsWidth, &advanceMin, &advanceMax);
1827 : } else {
1828 0 : gfxRect glyphRect;
1829 0 : if (!extents->GetTightGlyphExtentsAppUnits(this,
1830 0 : aRefContext, glyphIndex, &glyphRect)) {
1831 : glyphRect = gfxRect(0, metrics.mBoundingBox.Y(),
1832 0 : advance, metrics.mBoundingBox.Height());
1833 : }
1834 0 : if (isRTL) {
1835 0 : glyphRect -= gfxPoint(advance, 0);
1836 : }
1837 0 : glyphRect += gfxPoint(x, 0);
1838 0 : metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
1839 : }
1840 : }
1841 0 : x += direction*advance;
1842 : } else {
1843 0 : PRUint32 glyphCount = glyphData->GetGlyphCount();
1844 0 : if (glyphCount > 0) {
1845 : const gfxTextRun::DetailedGlyph *details =
1846 0 : aTextRun->GetDetailedGlyphs(i);
1847 0 : NS_ASSERTION(details != nsnull,
1848 : "detaiedGlyph record should not be missing!");
1849 : PRUint32 j;
1850 0 : for (j = 0; j < glyphCount; ++j, ++details) {
1851 0 : PRUint32 glyphIndex = details->mGlyphID;
1852 0 : gfxPoint glyphPt(x + details->mXOffset, details->mYOffset);
1853 0 : double advance = details->mAdvance;
1854 0 : gfxRect glyphRect;
1855 0 : if (glyphData->IsMissing() || !extents ||
1856 : !extents->GetTightGlyphExtentsAppUnits(this,
1857 0 : aRefContext, glyphIndex, &glyphRect)) {
1858 : // We might have failed to get glyph extents due to
1859 : // OOM or something
1860 : glyphRect = gfxRect(0, -metrics.mAscent,
1861 0 : advance, metrics.mAscent + metrics.mDescent);
1862 : }
1863 0 : if (isRTL) {
1864 0 : glyphRect -= gfxPoint(advance, 0);
1865 : }
1866 0 : glyphRect += gfxPoint(x, 0);
1867 0 : metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
1868 0 : x += direction*advance;
1869 : }
1870 : }
1871 : }
1872 : // Every other glyph type is ignored
1873 0 : if (aSpacing) {
1874 0 : double space = aSpacing[i - aStart].mAfter;
1875 0 : if (i + 1 < aEnd) {
1876 0 : space += aSpacing[i + 1 - aStart].mBefore;
1877 : }
1878 0 : x += direction*space;
1879 : }
1880 : }
1881 :
1882 0 : if (aBoundingBoxType == LOOSE_INK_EXTENTS) {
1883 0 : UnionRange(x, &advanceMin, &advanceMax);
1884 : gfxRect fontBox(advanceMin, -metrics.mAscent,
1885 0 : advanceMax - advanceMin, metrics.mAscent + metrics.mDescent);
1886 0 : metrics.mBoundingBox = metrics.mBoundingBox.Union(fontBox);
1887 : }
1888 0 : if (isRTL) {
1889 0 : metrics.mBoundingBox -= gfxPoint(x, 0);
1890 : }
1891 :
1892 0 : metrics.mAdvanceWidth = x*direction;
1893 0 : return metrics;
1894 : }
1895 :
1896 : #define MAX_SHAPING_LENGTH 32760 // slightly less than 32K, trying to avoid
1897 : // over-stressing platform shapers
1898 :
1899 : #define BACKTRACK_LIMIT 1024 // If we can't find a space or a cluster start
1900 : // within 1K chars, just chop arbitrarily.
1901 : // Limiting backtrack here avoids pathological
1902 : // behavior on long runs with no whitespace.
1903 :
1904 : static bool
1905 0 : IsBoundarySpace(PRUnichar aChar, PRUnichar aNextChar)
1906 : {
1907 0 : return (aChar == ' ' || aChar == 0x00A0) && !IsClusterExtender(aNextChar);
1908 : }
1909 :
1910 : static inline PRUint32
1911 0 : HashMix(PRUint32 aHash, PRUnichar aCh)
1912 : {
1913 0 : return (aHash >> 28) ^ (aHash << 4) ^ aCh;
1914 : }
1915 :
1916 : template<typename T>
1917 : gfxShapedWord*
1918 0 : gfxFont::GetShapedWord(gfxContext *aContext,
1919 : const T *aText,
1920 : PRUint32 aLength,
1921 : PRUint32 aHash,
1922 : PRInt32 aRunScript,
1923 : PRInt32 aAppUnitsPerDevUnit,
1924 : PRUint32 aFlags)
1925 : {
1926 : // if the cache is getting too big, flush it and start over
1927 0 : if (mWordCache.Count() > 10000) {
1928 0 : NS_WARNING("flushing shaped-word cache");
1929 0 : ClearCachedWords();
1930 : }
1931 :
1932 : // if there's a cached entry for this word, just return it
1933 : CacheHashKey key(aText, aLength, aHash,
1934 : aRunScript,
1935 : aAppUnitsPerDevUnit,
1936 0 : aFlags);
1937 :
1938 0 : CacheHashEntry *entry = mWordCache.PutEntry(key);
1939 0 : gfxShapedWord *sw = entry->mShapedWord;
1940 0 : Telemetry::Accumulate(Telemetry::WORD_CACHE_LOOKUP_LEN, aLength);
1941 0 : Telemetry::Accumulate(Telemetry::WORD_CACHE_LOOKUP_SCRIPT, aRunScript);
1942 :
1943 0 : if (sw) {
1944 0 : sw->ResetAge();
1945 0 : Telemetry::Accumulate(Telemetry::WORD_CACHE_HIT_LEN, aLength);
1946 0 : Telemetry::Accumulate(Telemetry::WORD_CACHE_HIT_SCRIPT, aRunScript);
1947 0 : return sw;
1948 : }
1949 :
1950 0 : sw = entry->mShapedWord = gfxShapedWord::Create(aText, aLength,
1951 : aRunScript,
1952 : aAppUnitsPerDevUnit,
1953 : aFlags);
1954 0 : NS_ASSERTION(sw != nsnull,
1955 : "failed to create gfxShapedWord - expect missing text");
1956 0 : if (!sw) {
1957 0 : return nsnull;
1958 : }
1959 :
1960 : bool ok;
1961 : if (sizeof(T) == sizeof(PRUnichar)) {
1962 0 : ok = ShapeWord(aContext, sw, (const PRUnichar*)aText);
1963 : } else {
1964 0 : nsAutoString utf16;
1965 0 : AppendASCIItoUTF16(nsDependentCSubstring((const char*)aText, aLength),
1966 : utf16);
1967 0 : ok = ShapeWord(aContext, sw, utf16.BeginReading());
1968 : }
1969 0 : NS_WARN_IF_FALSE(ok, "failed to shape word - expect garbled text");
1970 :
1971 0 : for (PRUint32 i = 0; i < aLength; ++i) {
1972 0 : if (aText[i] == ' ') {
1973 0 : sw->SetIsSpace(i);
1974 0 : } else if (i > 0 &&
1975 : NS_IS_HIGH_SURROGATE(aText[i - 1]) &&
1976 : NS_IS_LOW_SURROGATE(aText[i])) {
1977 0 : sw->SetIsLowSurrogate(i);
1978 : }
1979 : }
1980 :
1981 0 : return sw;
1982 : }
1983 :
1984 : bool
1985 0 : gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
1986 : {
1987 0 : const gfxShapedWord *sw = mShapedWord;
1988 0 : if (!sw) {
1989 0 : return false;
1990 : }
1991 0 : if (sw->Length() != aKey->mLength ||
1992 0 : sw->Flags() != aKey->mFlags ||
1993 0 : sw->AppUnitsPerDevUnit() != aKey->mAppUnitsPerDevUnit ||
1994 0 : sw->Script() != aKey->mScript) {
1995 0 : return false;
1996 : }
1997 0 : if (sw->TextIs8Bit()) {
1998 0 : if (aKey->mTextIs8Bit) {
1999 0 : return (0 == memcmp(sw->Text8Bit(), aKey->mText.mSingle,
2000 0 : aKey->mLength * sizeof(PRUint8)));
2001 : }
2002 : // The key has 16-bit text, even though all the characters are < 256,
2003 : // so the TEXT_IS_8BIT flag was set and the cached ShapedWord we're
2004 : // comparing with will have 8-bit text.
2005 0 : const PRUint8 *s1 = sw->Text8Bit();
2006 0 : const PRUnichar *s2 = aKey->mText.mDouble;
2007 0 : const PRUnichar *s2end = s2 + aKey->mLength;
2008 0 : while (s2 < s2end) {
2009 0 : if (*s1++ != *s2++) {
2010 0 : return false;
2011 : }
2012 : }
2013 0 : return true;
2014 : }
2015 0 : NS_ASSERTION((aKey->mFlags & gfxTextRunFactory::TEXT_IS_8BIT) == 0 &&
2016 : !aKey->mTextIs8Bit, "didn't expect 8-bit text here");
2017 0 : return (0 == memcmp(sw->TextUnicode(), aKey->mText.mDouble,
2018 0 : aKey->mLength * sizeof(PRUnichar)));
2019 : }
2020 :
2021 : bool
2022 0 : gfxFont::ShapeWord(gfxContext *aContext,
2023 : gfxShapedWord *aShapedWord,
2024 : const PRUnichar *aText,
2025 : bool aPreferPlatformShaping)
2026 : {
2027 0 : bool ok = false;
2028 :
2029 : #ifdef MOZ_GRAPHITE
2030 0 : if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
2031 0 : ok = mGraphiteShaper->ShapeWord(aContext, aShapedWord, aText);
2032 : }
2033 : #endif
2034 :
2035 0 : if (!ok && mHarfBuzzShaper && !aPreferPlatformShaping) {
2036 0 : if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aShapedWord->Script())) {
2037 0 : ok = mHarfBuzzShaper->ShapeWord(aContext, aShapedWord, aText);
2038 : }
2039 : }
2040 :
2041 0 : if (!ok) {
2042 0 : if (!mPlatformShaper) {
2043 0 : CreatePlatformShaper();
2044 0 : NS_ASSERTION(mPlatformShaper, "no platform shaper available!");
2045 : }
2046 0 : if (mPlatformShaper) {
2047 0 : ok = mPlatformShaper->ShapeWord(aContext, aShapedWord, aText);
2048 : }
2049 : }
2050 :
2051 0 : if (ok && IsSyntheticBold()) {
2052 : float synBoldOffset =
2053 0 : GetSyntheticBoldOffset() * CalcXScale(aContext);
2054 0 : aShapedWord->AdjustAdvancesForSyntheticBold(synBoldOffset);
2055 : }
2056 :
2057 0 : return ok;
2058 : }
2059 :
2060 : template<typename T>
2061 : bool
2062 0 : gfxFont::SplitAndInitTextRun(gfxContext *aContext,
2063 : gfxTextRun *aTextRun,
2064 : const T *aString,
2065 : PRUint32 aRunStart,
2066 : PRUint32 aRunLength,
2067 : PRInt32 aRunScript)
2068 : {
2069 0 : if (aRunLength == 0) {
2070 0 : return true;
2071 : }
2072 :
2073 0 : InitWordCache();
2074 :
2075 : // the only flags we care about for ShapedWord construction/caching
2076 : PRUint32 flags = aTextRun->GetFlags() &
2077 : (gfxTextRunFactory::TEXT_IS_RTL |
2078 0 : gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES);
2079 : if (sizeof(T) == sizeof(PRUint8)) {
2080 0 : flags |= gfxTextRunFactory::TEXT_IS_8BIT;
2081 : }
2082 :
2083 0 : const T *text = aString + aRunStart;
2084 0 : PRUint32 wordStart = 0;
2085 0 : PRUint32 hash = 0;
2086 0 : bool wordIs8Bit = true;
2087 0 : PRInt32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
2088 :
2089 0 : T nextCh = text[0];
2090 0 : for (PRUint32 i = 0; i <= aRunLength; ++i) {
2091 0 : T ch = nextCh;
2092 0 : nextCh = (i < aRunLength - 1) ? text[i + 1] : '\n';
2093 0 : bool boundary = IsBoundarySpace(ch, nextCh);
2094 0 : bool invalid = !boundary && gfxFontGroup::IsInvalidChar(ch);
2095 0 : PRUint32 length = i - wordStart;
2096 :
2097 : // break into separate ShapedWords when we hit an invalid char,
2098 : // or a boundary space (always handled individually),
2099 : // or the first non-space after a space
2100 0 : bool breakHere = boundary || invalid;
2101 :
2102 0 : if (!breakHere) {
2103 : // if we're approaching the max ShapedWord length, break anyway...
2104 : if (sizeof(T) == sizeof(PRUint8)) {
2105 : // in 8-bit text, no clusters or surrogates to worry about
2106 0 : if (length >= gfxShapedWord::kMaxLength) {
2107 0 : breakHere = true;
2108 : }
2109 : } else {
2110 : // try to avoid breaking before combining mark or low surrogate
2111 0 : if (length >= gfxShapedWord::kMaxLength - 15) {
2112 0 : if (!NS_IS_LOW_SURROGATE(ch)) {
2113 0 : if (!IsClusterExtender(ch)) {
2114 0 : breakHere = true;
2115 : }
2116 : }
2117 0 : if (!breakHere && length >= gfxShapedWord::kMaxLength - 3) {
2118 0 : if (!NS_IS_LOW_SURROGATE(ch)) {
2119 0 : breakHere = true;
2120 : }
2121 : }
2122 0 : if (!breakHere && length >= gfxShapedWord::kMaxLength) {
2123 0 : breakHere = true;
2124 : }
2125 : }
2126 : }
2127 : }
2128 :
2129 0 : if (!breakHere) {
2130 0 : if (ch >= 0x100) {
2131 0 : wordIs8Bit = false;
2132 : }
2133 : // include this character in the hash, and move on to next
2134 0 : hash = HashMix(hash, ch);
2135 0 : continue;
2136 : }
2137 :
2138 : // We've decided to break here (i.e. we're at the end of a "word",
2139 : // or the word is becoming excessively long): shape the word and
2140 : // add it to the textrun
2141 0 : if (length > 0) {
2142 0 : PRUint32 wordFlags = flags;
2143 : // in the 8-bit version of this method, TEXT_IS_8BIT was
2144 : // already set as part of |flags|, so no need for a per-word
2145 : // adjustment here
2146 : if (sizeof(T) == sizeof(PRUnichar)) {
2147 0 : if (wordIs8Bit) {
2148 0 : wordFlags |= gfxTextRunFactory::TEXT_IS_8BIT;
2149 : }
2150 : }
2151 : gfxShapedWord *sw = GetShapedWord(aContext,
2152 : text + wordStart, length,
2153 : hash, aRunScript,
2154 : appUnitsPerDevUnit,
2155 0 : wordFlags);
2156 0 : if (sw) {
2157 0 : aTextRun->CopyGlyphDataFrom(sw, aRunStart + wordStart);
2158 : } else {
2159 0 : return false; // failed, presumably out of memory?
2160 : }
2161 : }
2162 :
2163 0 : if (boundary) {
2164 : // word was terminated by a space: add that to the textrun
2165 0 : if (!aTextRun->SetSpaceGlyphIfSimple(this, aContext,
2166 : aRunStart + i, ch))
2167 : {
2168 : static const PRUint8 space = ' ';
2169 : gfxShapedWord *sw =
2170 : GetShapedWord(aContext,
2171 : &space, 1,
2172 : HashMix(0, ' '), aRunScript,
2173 : appUnitsPerDevUnit,
2174 0 : flags | gfxTextRunFactory::TEXT_IS_8BIT);
2175 0 : if (sw) {
2176 0 : aTextRun->CopyGlyphDataFrom(sw, aRunStart + i);
2177 : } else {
2178 0 : return false;
2179 : }
2180 : }
2181 0 : hash = 0;
2182 0 : wordStart = i + 1;
2183 0 : wordIs8Bit = true;
2184 0 : continue;
2185 : }
2186 :
2187 0 : if (i == aRunLength) {
2188 0 : break;
2189 : }
2190 :
2191 0 : if (invalid) {
2192 : // word was terminated by an invalid char: skip it,
2193 : // but record where TAB or NEWLINE occur
2194 0 : if (ch == '\t') {
2195 0 : aTextRun->SetIsTab(aRunStart + i);
2196 0 : } else if (ch == '\n') {
2197 0 : aTextRun->SetIsNewline(aRunStart + i);
2198 : }
2199 0 : hash = 0;
2200 0 : wordStart = i + 1;
2201 0 : wordIs8Bit = true;
2202 0 : continue;
2203 : }
2204 :
2205 : // word was forcibly broken, so current char will begin next word
2206 0 : hash = HashMix(0, ch);
2207 0 : wordStart = i;
2208 0 : wordIs8Bit = (ch < 0x100);
2209 : }
2210 :
2211 0 : return true;
2212 : }
2213 :
2214 : gfxGlyphExtents *
2215 0 : gfxFont::GetOrCreateGlyphExtents(PRUint32 aAppUnitsPerDevUnit) {
2216 : PRUint32 i;
2217 0 : for (i = 0; i < mGlyphExtentsArray.Length(); ++i) {
2218 0 : if (mGlyphExtentsArray[i]->GetAppUnitsPerDevUnit() == aAppUnitsPerDevUnit)
2219 0 : return mGlyphExtentsArray[i];
2220 : }
2221 0 : gfxGlyphExtents *glyphExtents = new gfxGlyphExtents(aAppUnitsPerDevUnit);
2222 0 : if (glyphExtents) {
2223 0 : mGlyphExtentsArray.AppendElement(glyphExtents);
2224 : // Initialize the extents of a space glyph, assuming that spaces don't
2225 : // render anything!
2226 0 : glyphExtents->SetContainedGlyphWidthAppUnits(GetSpaceGlyph(), 0);
2227 : }
2228 0 : return glyphExtents;
2229 : }
2230 :
2231 : void
2232 0 : gfxFont::SetupGlyphExtents(gfxContext *aContext, PRUint32 aGlyphID, bool aNeedTight,
2233 : gfxGlyphExtents *aExtents)
2234 : {
2235 0 : gfxMatrix matrix = aContext->CurrentMatrix();
2236 0 : aContext->IdentityMatrix();
2237 : cairo_glyph_t glyph;
2238 0 : glyph.index = aGlyphID;
2239 0 : glyph.x = 0;
2240 0 : glyph.y = 0;
2241 : cairo_text_extents_t extents;
2242 0 : cairo_glyph_extents(aContext->GetCairo(), &glyph, 1, &extents);
2243 0 : aContext->SetMatrix(matrix);
2244 :
2245 0 : const Metrics& fontMetrics = GetMetrics();
2246 0 : PRUint32 appUnitsPerDevUnit = aExtents->GetAppUnitsPerDevUnit();
2247 0 : if (!aNeedTight && extents.x_bearing >= 0 &&
2248 : extents.y_bearing >= -fontMetrics.maxAscent &&
2249 : extents.height + extents.y_bearing <= fontMetrics.maxDescent) {
2250 : PRUint32 appUnitsWidth =
2251 0 : PRUint32(ceil((extents.x_bearing + extents.width)*appUnitsPerDevUnit));
2252 0 : if (appUnitsWidth < gfxGlyphExtents::INVALID_WIDTH) {
2253 0 : aExtents->SetContainedGlyphWidthAppUnits(aGlyphID, PRUint16(appUnitsWidth));
2254 0 : return;
2255 : }
2256 : }
2257 : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
2258 : if (!aNeedTight) {
2259 : ++gGlyphExtentsSetupFallBackToTight;
2260 : }
2261 : #endif
2262 :
2263 0 : double d2a = appUnitsPerDevUnit;
2264 : gfxRect bounds(extents.x_bearing*d2a, extents.y_bearing*d2a,
2265 0 : extents.width*d2a, extents.height*d2a);
2266 0 : aExtents->SetTightGlyphExtents(aGlyphID, bounds);
2267 : }
2268 :
2269 : // Try to initialize font metrics by reading sfnt tables directly;
2270 : // set mIsValid=TRUE and return TRUE on success.
2271 : // Return FALSE if the gfxFontEntry subclass does not
2272 : // implement GetFontTable(), or for non-sfnt fonts where tables are
2273 : // not available.
2274 : bool
2275 0 : gfxFont::InitMetricsFromSfntTables(Metrics& aMetrics)
2276 : {
2277 0 : mIsValid = false; // font is NOT valid in case of early return
2278 :
2279 0 : const PRUint32 kHeadTableTag = TRUETYPE_TAG('h','e','a','d');
2280 0 : const PRUint32 kHheaTableTag = TRUETYPE_TAG('h','h','e','a');
2281 0 : const PRUint32 kPostTableTag = TRUETYPE_TAG('p','o','s','t');
2282 0 : const PRUint32 kOS_2TableTag = TRUETYPE_TAG('O','S','/','2');
2283 :
2284 0 : if (mFUnitsConvFactor == 0.0) {
2285 : // If the conversion factor from FUnits is not yet set,
2286 : // 'head' table is required; otherwise we cannot read any metrics
2287 : // because we don't know unitsPerEm
2288 0 : AutoFallibleTArray<PRUint8,sizeof(HeadTable)> headData;
2289 0 : if (NS_FAILED(mFontEntry->GetFontTable(kHeadTableTag, headData)) ||
2290 0 : headData.Length() < sizeof(HeadTable)) {
2291 0 : return false; // no 'head' table -> not an sfnt
2292 : }
2293 0 : HeadTable *head = reinterpret_cast<HeadTable*>(headData.Elements());
2294 0 : PRUint32 unitsPerEm = head->unitsPerEm;
2295 0 : if (!unitsPerEm) {
2296 0 : return true; // is an sfnt, but not valid
2297 : }
2298 0 : mFUnitsConvFactor = mAdjustedSize / unitsPerEm;
2299 : }
2300 :
2301 : // 'hhea' table is required to get vertical extents
2302 0 : AutoFallibleTArray<PRUint8,sizeof(HheaTable)> hheaData;
2303 0 : if (NS_FAILED(mFontEntry->GetFontTable(kHheaTableTag, hheaData)) ||
2304 0 : hheaData.Length() < sizeof(HheaTable)) {
2305 0 : return false; // no 'hhea' table -> not an sfnt
2306 : }
2307 0 : HheaTable *hhea = reinterpret_cast<HheaTable*>(hheaData.Elements());
2308 :
2309 : #define SET_UNSIGNED(field,src) aMetrics.field = PRUint16(src) * mFUnitsConvFactor
2310 : #define SET_SIGNED(field,src) aMetrics.field = PRInt16(src) * mFUnitsConvFactor
2311 :
2312 0 : SET_UNSIGNED(maxAdvance, hhea->advanceWidthMax);
2313 0 : SET_SIGNED(maxAscent, hhea->ascender);
2314 0 : SET_SIGNED(maxDescent, -PRInt16(hhea->descender));
2315 0 : SET_SIGNED(externalLeading, hhea->lineGap);
2316 :
2317 : // 'post' table is required for underline metrics
2318 0 : AutoFallibleTArray<PRUint8,sizeof(PostTable)> postData;
2319 0 : if (NS_FAILED(mFontEntry->GetFontTable(kPostTableTag, postData))) {
2320 0 : return true; // no 'post' table -> sfnt is not valid
2321 : }
2322 0 : if (postData.Length() <
2323 : offsetof(PostTable, underlineThickness) + sizeof(PRUint16)) {
2324 0 : return true; // bad post table -> sfnt is not valid
2325 : }
2326 0 : PostTable *post = reinterpret_cast<PostTable*>(postData.Elements());
2327 :
2328 0 : SET_SIGNED(underlineOffset, post->underlinePosition);
2329 0 : SET_UNSIGNED(underlineSize, post->underlineThickness);
2330 :
2331 : // 'OS/2' table is optional, if not found we'll estimate xHeight
2332 : // and aveCharWidth by measuring glyphs
2333 0 : AutoFallibleTArray<PRUint8,sizeof(OS2Table)> os2data;
2334 0 : if (NS_SUCCEEDED(mFontEntry->GetFontTable(kOS_2TableTag, os2data))) {
2335 0 : OS2Table *os2 = reinterpret_cast<OS2Table*>(os2data.Elements());
2336 :
2337 0 : if (os2data.Length() >= offsetof(OS2Table, sxHeight) +
2338 : sizeof(PRInt16) &&
2339 0 : PRUint16(os2->version) >= 2) {
2340 : // version 2 and later includes the x-height field
2341 0 : SET_SIGNED(xHeight, os2->sxHeight);
2342 : // NS_ABS because of negative xHeight seen in Kokonor (Tibetan) font
2343 0 : aMetrics.xHeight = NS_ABS(aMetrics.xHeight);
2344 : }
2345 : // this should always be present
2346 0 : if (os2data.Length() >= offsetof(OS2Table, yStrikeoutPosition) +
2347 : sizeof(PRInt16)) {
2348 0 : SET_SIGNED(aveCharWidth, os2->xAvgCharWidth);
2349 0 : SET_SIGNED(subscriptOffset, os2->ySubscriptYOffset);
2350 0 : SET_SIGNED(superscriptOffset, os2->ySuperscriptYOffset);
2351 0 : SET_SIGNED(strikeoutSize, os2->yStrikeoutSize);
2352 0 : SET_SIGNED(strikeoutOffset, os2->yStrikeoutPosition);
2353 : }
2354 : }
2355 :
2356 0 : mIsValid = true;
2357 :
2358 0 : return true;
2359 : }
2360 :
2361 : static double
2362 0 : RoundToNearestMultiple(double aValue, double aFraction)
2363 : {
2364 0 : return floor(aValue/aFraction + 0.5) * aFraction;
2365 : }
2366 :
2367 0 : void gfxFont::CalculateDerivedMetrics(Metrics& aMetrics)
2368 : {
2369 : aMetrics.maxAscent =
2370 0 : ceil(RoundToNearestMultiple(aMetrics.maxAscent, 1/1024.0));
2371 : aMetrics.maxDescent =
2372 0 : ceil(RoundToNearestMultiple(aMetrics.maxDescent, 1/1024.0));
2373 :
2374 0 : if (aMetrics.xHeight <= 0) {
2375 : // only happens if we couldn't find either font metrics
2376 : // or a char to measure;
2377 : // pick an arbitrary value that's better than zero
2378 0 : aMetrics.xHeight = aMetrics.maxAscent * DEFAULT_XHEIGHT_FACTOR;
2379 : }
2380 :
2381 0 : aMetrics.maxHeight = aMetrics.maxAscent + aMetrics.maxDescent;
2382 :
2383 0 : if (aMetrics.maxHeight - aMetrics.emHeight > 0.0) {
2384 0 : aMetrics.internalLeading = aMetrics.maxHeight - aMetrics.emHeight;
2385 : } else {
2386 0 : aMetrics.internalLeading = 0.0;
2387 : }
2388 :
2389 : aMetrics.emAscent = aMetrics.maxAscent * aMetrics.emHeight
2390 0 : / aMetrics.maxHeight;
2391 0 : aMetrics.emDescent = aMetrics.emHeight - aMetrics.emAscent;
2392 :
2393 0 : if (GetFontEntry()->IsFixedPitch()) {
2394 : // Some Quartz fonts are fixed pitch, but there's some glyph with a bigger
2395 : // advance than the average character width... this forces
2396 : // those fonts to be recognized like fixed pitch fonts by layout.
2397 0 : aMetrics.maxAdvance = aMetrics.aveCharWidth;
2398 : }
2399 :
2400 0 : if (!aMetrics.subscriptOffset) {
2401 0 : aMetrics.subscriptOffset = aMetrics.xHeight;
2402 : }
2403 0 : if (!aMetrics.superscriptOffset) {
2404 0 : aMetrics.superscriptOffset = aMetrics.xHeight;
2405 : }
2406 :
2407 0 : if (!aMetrics.strikeoutOffset) {
2408 0 : aMetrics.strikeoutOffset = aMetrics.xHeight * 0.5;
2409 : }
2410 0 : if (!aMetrics.strikeoutSize) {
2411 0 : aMetrics.strikeoutSize = aMetrics.underlineSize;
2412 : }
2413 0 : }
2414 :
2415 : void
2416 0 : gfxFont::SanitizeMetrics(gfxFont::Metrics *aMetrics, bool aIsBadUnderlineFont)
2417 : {
2418 : // Even if this font size is zero, this font is created with non-zero size.
2419 : // However, for layout and others, we should return the metrics of zero size font.
2420 0 : if (mStyle.size == 0.0) {
2421 0 : memset(aMetrics, 0, sizeof(gfxFont::Metrics));
2422 0 : return;
2423 : }
2424 :
2425 : // MS (P)Gothic and MS (P)Mincho are not having suitable values in their super script offset.
2426 : // If the values are not suitable, we should use x-height instead of them.
2427 : // See https://bugzilla.mozilla.org/show_bug.cgi?id=353632
2428 0 : if (aMetrics->superscriptOffset <= 0 ||
2429 : aMetrics->superscriptOffset >= aMetrics->maxAscent) {
2430 0 : aMetrics->superscriptOffset = aMetrics->xHeight;
2431 : }
2432 : // And also checking the case of sub script offset. The old gfx for win has checked this too.
2433 0 : if (aMetrics->subscriptOffset <= 0 ||
2434 : aMetrics->subscriptOffset >= aMetrics->maxAscent) {
2435 0 : aMetrics->subscriptOffset = aMetrics->xHeight;
2436 : }
2437 :
2438 0 : aMetrics->underlineSize = NS_MAX(1.0, aMetrics->underlineSize);
2439 0 : aMetrics->strikeoutSize = NS_MAX(1.0, aMetrics->strikeoutSize);
2440 :
2441 0 : aMetrics->underlineOffset = NS_MIN(aMetrics->underlineOffset, -1.0);
2442 :
2443 0 : if (aMetrics->maxAscent < 1.0) {
2444 : // We cannot draw strikeout line and overline in the ascent...
2445 0 : aMetrics->underlineSize = 0;
2446 0 : aMetrics->underlineOffset = 0;
2447 0 : aMetrics->strikeoutSize = 0;
2448 0 : aMetrics->strikeoutOffset = 0;
2449 0 : return;
2450 : }
2451 :
2452 : /**
2453 : * Some CJK fonts have bad underline offset. Therefore, if this is such font,
2454 : * we need to lower the underline offset to bottom of *em* descent.
2455 : * However, if this is system font, we should not do this for the rendering compatibility with
2456 : * another application's UI on the platform.
2457 : * XXX Should not use this hack if the font size is too small?
2458 : * Such text cannot be read, this might be used for tight CSS rendering? (E.g., Acid2)
2459 : */
2460 0 : if (!mStyle.systemFont && aIsBadUnderlineFont) {
2461 : // First, we need 2 pixels between baseline and underline at least. Because many CJK characters
2462 : // put their glyphs on the baseline, so, 1 pixel is too close for CJK characters.
2463 0 : aMetrics->underlineOffset = NS_MIN(aMetrics->underlineOffset, -2.0);
2464 :
2465 : // Next, we put the underline to bottom of below of the descent space.
2466 0 : if (aMetrics->internalLeading + aMetrics->externalLeading > aMetrics->underlineSize) {
2467 0 : aMetrics->underlineOffset = NS_MIN(aMetrics->underlineOffset, -aMetrics->emDescent);
2468 : } else {
2469 : aMetrics->underlineOffset = NS_MIN(aMetrics->underlineOffset,
2470 0 : aMetrics->underlineSize - aMetrics->emDescent);
2471 : }
2472 : }
2473 : // If underline positioned is too far from the text, descent position is preferred so that underline
2474 : // will stay within the boundary.
2475 0 : else if (aMetrics->underlineSize - aMetrics->underlineOffset > aMetrics->maxDescent) {
2476 0 : if (aMetrics->underlineSize > aMetrics->maxDescent)
2477 0 : aMetrics->underlineSize = NS_MAX(aMetrics->maxDescent, 1.0);
2478 : // The max underlineOffset is 1px (the min underlineSize is 1px, and min maxDescent is 0px.)
2479 0 : aMetrics->underlineOffset = aMetrics->underlineSize - aMetrics->maxDescent;
2480 : }
2481 :
2482 : // If strikeout line is overflowed from the ascent, the line should be resized and moved for
2483 : // that being in the ascent space.
2484 : // Note that the strikeoutOffset is *middle* of the strikeout line position.
2485 0 : gfxFloat halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
2486 0 : if (halfOfStrikeoutSize + aMetrics->strikeoutOffset > aMetrics->maxAscent) {
2487 0 : if (aMetrics->strikeoutSize > aMetrics->maxAscent) {
2488 0 : aMetrics->strikeoutSize = NS_MAX(aMetrics->maxAscent, 1.0);
2489 0 : halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
2490 : }
2491 0 : gfxFloat ascent = floor(aMetrics->maxAscent + 0.5);
2492 0 : aMetrics->strikeoutOffset = NS_MAX(halfOfStrikeoutSize, ascent / 2.0);
2493 : }
2494 :
2495 : // If overline is larger than the ascent, the line should be resized.
2496 0 : if (aMetrics->underlineSize > aMetrics->maxAscent) {
2497 0 : aMetrics->underlineSize = aMetrics->maxAscent;
2498 : }
2499 : }
2500 :
2501 : gfxFloat
2502 0 : gfxFont::SynthesizeSpaceWidth(PRUint32 aCh)
2503 : {
2504 : // return an appropriate width for various Unicode space characters
2505 : // that we "fake" if they're not actually present in the font;
2506 : // returns negative value if the char is not a known space.
2507 0 : switch (aCh) {
2508 : case 0x2000: // en quad
2509 0 : case 0x2002: return GetAdjustedSize() / 2; // en space
2510 : case 0x2001: // em quad
2511 0 : case 0x2003: return GetAdjustedSize(); // em space
2512 0 : case 0x2004: return GetAdjustedSize() / 3; // three-per-em space
2513 0 : case 0x2005: return GetAdjustedSize() / 4; // four-per-em space
2514 0 : case 0x2006: return GetAdjustedSize() / 6; // six-per-em space
2515 0 : case 0x2007: return GetMetrics().zeroOrAveCharWidth; // figure space
2516 0 : case 0x2008: return GetMetrics().spaceWidth; // punctuation space
2517 0 : case 0x2009: return GetAdjustedSize() / 5; // thin space
2518 0 : case 0x200a: return GetAdjustedSize() / 10; // hair space
2519 0 : case 0x202f: return GetAdjustedSize() / 5; // narrow no-break space
2520 0 : default: return -1.0;
2521 : }
2522 : }
2523 :
2524 0 : gfxGlyphExtents::~gfxGlyphExtents()
2525 : {
2526 : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
2527 : gGlyphExtentsWidthsTotalSize += mContainedGlyphWidths.ComputeSize();
2528 : gGlyphExtentsCount++;
2529 : #endif
2530 0 : MOZ_COUNT_DTOR(gfxGlyphExtents);
2531 0 : }
2532 :
2533 : bool
2534 0 : gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont *aFont,
2535 : gfxContext *aContext, PRUint32 aGlyphID, gfxRect *aExtents)
2536 : {
2537 0 : HashEntry *entry = mTightGlyphExtents.GetEntry(aGlyphID);
2538 0 : if (!entry) {
2539 0 : if (!aContext) {
2540 0 : NS_WARNING("Could not get glyph extents (no aContext)");
2541 0 : return false;
2542 : }
2543 :
2544 0 : aFont->SetupCairoFont(aContext);
2545 : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
2546 : ++gGlyphExtentsSetupLazyTight;
2547 : #endif
2548 0 : aFont->SetupGlyphExtents(aContext, aGlyphID, true, this);
2549 0 : entry = mTightGlyphExtents.GetEntry(aGlyphID);
2550 0 : if (!entry) {
2551 0 : NS_WARNING("Could not get glyph extents");
2552 0 : return false;
2553 : }
2554 : }
2555 :
2556 0 : *aExtents = gfxRect(entry->x, entry->y, entry->width, entry->height);
2557 0 : return true;
2558 : }
2559 :
2560 0 : gfxGlyphExtents::GlyphWidths::~GlyphWidths()
2561 : {
2562 : PRUint32 i;
2563 0 : for (i = 0; i < mBlocks.Length(); ++i) {
2564 0 : PtrBits bits = mBlocks[i];
2565 0 : if (bits && !(bits & 0x1)) {
2566 0 : delete[] reinterpret_cast<PRUint16 *>(bits);
2567 : }
2568 : }
2569 0 : }
2570 :
2571 : #ifdef DEBUG
2572 : PRUint32
2573 0 : gfxGlyphExtents::GlyphWidths::ComputeSize()
2574 : {
2575 : PRUint32 i;
2576 0 : PRUint32 size = mBlocks.Capacity()*sizeof(PtrBits);
2577 0 : for (i = 0; i < mBlocks.Length(); ++i) {
2578 0 : PtrBits bits = mBlocks[i];
2579 0 : if (bits && !(bits & 0x1)) {
2580 0 : size += BLOCK_SIZE*sizeof(PRUint16);
2581 : }
2582 : }
2583 0 : return size;
2584 : }
2585 : #endif
2586 :
2587 : void
2588 0 : gfxGlyphExtents::GlyphWidths::Set(PRUint32 aGlyphID, PRUint16 aWidth)
2589 : {
2590 0 : PRUint32 block = aGlyphID >> BLOCK_SIZE_BITS;
2591 0 : PRUint32 len = mBlocks.Length();
2592 0 : if (block >= len) {
2593 0 : PtrBits *elems = mBlocks.AppendElements(block + 1 - len);
2594 0 : if (!elems)
2595 0 : return;
2596 0 : memset(elems, 0, sizeof(PtrBits)*(block + 1 - len));
2597 : }
2598 :
2599 0 : PtrBits bits = mBlocks[block];
2600 0 : PRUint32 glyphOffset = aGlyphID & (BLOCK_SIZE - 1);
2601 0 : if (!bits) {
2602 0 : mBlocks[block] = MakeSingle(glyphOffset, aWidth);
2603 0 : return;
2604 : }
2605 :
2606 : PRUint16 *newBlock;
2607 0 : if (bits & 0x1) {
2608 : // Expand the block to a real block. We could avoid this by checking
2609 : // glyphOffset == GetGlyphOffset(bits), but that never happens so don't bother
2610 0 : newBlock = new PRUint16[BLOCK_SIZE];
2611 0 : if (!newBlock)
2612 0 : return;
2613 : PRUint32 i;
2614 0 : for (i = 0; i < BLOCK_SIZE; ++i) {
2615 0 : newBlock[i] = INVALID_WIDTH;
2616 : }
2617 0 : newBlock[GetGlyphOffset(bits)] = GetWidth(bits);
2618 0 : mBlocks[block] = reinterpret_cast<PtrBits>(newBlock);
2619 : } else {
2620 0 : newBlock = reinterpret_cast<PRUint16 *>(bits);
2621 : }
2622 0 : newBlock[glyphOffset] = aWidth;
2623 : }
2624 :
2625 : void
2626 0 : gfxGlyphExtents::SetTightGlyphExtents(PRUint32 aGlyphID, const gfxRect& aExtentsAppUnits)
2627 : {
2628 0 : HashEntry *entry = mTightGlyphExtents.PutEntry(aGlyphID);
2629 0 : if (!entry)
2630 0 : return;
2631 0 : entry->x = aExtentsAppUnits.X();
2632 0 : entry->y = aExtentsAppUnits.Y();
2633 0 : entry->width = aExtentsAppUnits.Width();
2634 0 : entry->height = aExtentsAppUnits.Height();
2635 : }
2636 :
2637 0 : gfxFontGroup::gfxFontGroup(const nsAString& aFamilies, const gfxFontStyle *aStyle, gfxUserFontSet *aUserFontSet)
2638 0 : : mFamilies(aFamilies), mStyle(*aStyle), mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET)
2639 : {
2640 0 : mUserFontSet = nsnull;
2641 0 : SetUserFontSet(aUserFontSet);
2642 :
2643 0 : mSkipDrawing = false;
2644 :
2645 0 : mPageLang = gfxPlatform::GetFontPrefLangFor(mStyle.language);
2646 0 : BuildFontList();
2647 0 : }
2648 :
2649 : void
2650 0 : gfxFontGroup::BuildFontList()
2651 : {
2652 : // "#if" to be removed once all platforms are moved to gfxPlatformFontList interface
2653 : // and subclasses of gfxFontGroup eliminated
2654 : #if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID)
2655 : ForEachFont(FindPlatformFont, this);
2656 :
2657 : if (mFonts.Length() == 0) {
2658 : bool needsBold;
2659 : gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
2660 : gfxFontEntry *defaultFont = pfl->GetDefaultFont(&mStyle, needsBold);
2661 : NS_ASSERTION(defaultFont, "invalid default font returned by GetDefaultFont");
2662 :
2663 : if (defaultFont) {
2664 : nsRefPtr<gfxFont> font = defaultFont->FindOrMakeFont(&mStyle,
2665 : needsBold);
2666 : if (font) {
2667 : mFonts.AppendElement(font);
2668 : }
2669 : }
2670 :
2671 : if (mFonts.Length() == 0) {
2672 : // Try for a "font of last resort...."
2673 : // Because an empty font list would be Really Bad for later code
2674 : // that assumes it will be able to get valid metrics for layout,
2675 : // just look for the first usable font and put in the list.
2676 : // (see bug 554544)
2677 : nsAutoTArray<nsRefPtr<gfxFontFamily>,200> families;
2678 : pfl->GetFontFamilyList(families);
2679 : for (PRUint32 i = 0; i < families.Length(); ++i) {
2680 : gfxFontEntry *fe = families[i]->FindFontForStyle(mStyle,
2681 : needsBold);
2682 : if (fe) {
2683 : nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle,
2684 : needsBold);
2685 : if (font) {
2686 : mFonts.AppendElement(font);
2687 : break;
2688 : }
2689 : }
2690 : }
2691 : }
2692 :
2693 : if (mFonts.Length() == 0) {
2694 : // an empty font list at this point is fatal; we're not going to
2695 : // be able to do even the most basic layout operations
2696 : char msg[256]; // CHECK buffer length if revising message below
2697 : sprintf(msg, "unable to find a usable font (%.220s)",
2698 : NS_ConvertUTF16toUTF8(mFamilies).get());
2699 : NS_RUNTIMEABORT(msg);
2700 : }
2701 : }
2702 :
2703 : if (!mStyle.systemFont) {
2704 : for (PRUint32 i = 0; i < mFonts.Length(); ++i) {
2705 : gfxFont* font = mFonts[i];
2706 : if (font->GetFontEntry()->mIsBadUnderlineFont) {
2707 : gfxFloat first = mFonts[0]->GetMetrics().underlineOffset;
2708 : gfxFloat bad = font->GetMetrics().underlineOffset;
2709 : mUnderlineOffset = NS_MIN(first, bad);
2710 : break;
2711 : }
2712 : }
2713 : }
2714 : #endif
2715 0 : }
2716 :
2717 : bool
2718 0 : gfxFontGroup::FindPlatformFont(const nsAString& aName,
2719 : const nsACString& aGenericName,
2720 : bool aUseFontSet,
2721 : void *aClosure)
2722 : {
2723 0 : gfxFontGroup *fontGroup = static_cast<gfxFontGroup*>(aClosure);
2724 0 : const gfxFontStyle *fontStyle = fontGroup->GetStyle();
2725 :
2726 : bool needsBold;
2727 0 : gfxFontEntry *fe = nsnull;
2728 :
2729 0 : bool foundFamily = false;
2730 0 : if (aUseFontSet) {
2731 : // First, look up in the user font set...
2732 : // If the fontSet matches the family, we must not look for a platform
2733 : // font of the same name, even if we fail to actually get a fontEntry
2734 : // here; we'll fall back to the next name in the CSS font-family list.
2735 0 : gfxUserFontSet *fs = fontGroup->GetUserFontSet();
2736 0 : if (fs) {
2737 : // If the fontSet matches the family, but the font has not yet finished
2738 : // loading (nor has its load timeout fired), the fontGroup should wait
2739 : // for the download, and not actually draw its text yet.
2740 0 : bool waitForUserFont = false;
2741 : fe = fs->FindFontEntry(aName, *fontStyle, foundFamily,
2742 0 : needsBold, waitForUserFont);
2743 0 : if (!fe && waitForUserFont) {
2744 0 : fontGroup->mSkipDrawing = true;
2745 : }
2746 : }
2747 : }
2748 :
2749 : // Not known in the user font set ==> check system fonts
2750 0 : if (!foundFamily) {
2751 : fe = gfxPlatformFontList::PlatformFontList()->
2752 0 : FindFontForFamily(aName, fontStyle, needsBold);
2753 : }
2754 :
2755 : // add to the font group, unless it's already there
2756 0 : if (fe && !fontGroup->HasFont(fe)) {
2757 0 : nsRefPtr<gfxFont> font = fe->FindOrMakeFont(fontStyle, needsBold);
2758 0 : if (font) {
2759 0 : fontGroup->mFonts.AppendElement(font);
2760 : }
2761 : }
2762 :
2763 0 : return true;
2764 : }
2765 :
2766 : bool
2767 0 : gfxFontGroup::HasFont(const gfxFontEntry *aFontEntry)
2768 : {
2769 0 : for (PRUint32 i = 0; i < mFonts.Length(); ++i) {
2770 0 : if (mFonts.ElementAt(i)->GetFontEntry() == aFontEntry)
2771 0 : return true;
2772 : }
2773 0 : return false;
2774 : }
2775 :
2776 0 : gfxFontGroup::~gfxFontGroup() {
2777 0 : mFonts.Clear();
2778 0 : SetUserFontSet(nsnull);
2779 0 : }
2780 :
2781 : gfxFontGroup *
2782 0 : gfxFontGroup::Copy(const gfxFontStyle *aStyle)
2783 : {
2784 0 : return new gfxFontGroup(mFamilies, aStyle, mUserFontSet);
2785 : }
2786 :
2787 : bool
2788 0 : gfxFontGroup::IsInvalidChar(PRUint8 ch)
2789 : {
2790 0 : return ((ch & 0x7f) < 0x20);
2791 : }
2792 :
2793 : bool
2794 0 : gfxFontGroup::IsInvalidChar(PRUnichar ch)
2795 : {
2796 : // All printable 7-bit ASCII values are OK
2797 0 : if (ch >= ' ' && ch < 0x80) {
2798 0 : return false;
2799 : }
2800 : // No point in sending non-printing control chars through font shaping
2801 0 : if (ch <= 0x9f) {
2802 0 : return true;
2803 : }
2804 : return ((ch & 0xFF00) == 0x2000 /* Unicode control character */ &&
2805 : (ch == 0x200B/*ZWSP*/ || ch == 0x2028/*LSEP*/ || ch == 0x2029/*PSEP*/ ||
2806 0 : IS_BIDI_CONTROL_CHAR(ch)));
2807 : }
2808 :
2809 : bool
2810 0 : gfxFontGroup::ForEachFont(FontCreationCallback fc,
2811 : void *closure)
2812 : {
2813 : return ForEachFontInternal(mFamilies, mStyle.language,
2814 0 : true, true, true, fc, closure);
2815 : }
2816 :
2817 : bool
2818 0 : gfxFontGroup::ForEachFont(const nsAString& aFamilies,
2819 : nsIAtom *aLanguage,
2820 : FontCreationCallback fc,
2821 : void *closure)
2822 : {
2823 : return ForEachFontInternal(aFamilies, aLanguage,
2824 0 : false, true, true, fc, closure);
2825 : }
2826 :
2827 0 : struct ResolveData {
2828 0 : ResolveData(gfxFontGroup::FontCreationCallback aCallback,
2829 : nsACString& aGenericFamily,
2830 : bool aUseFontSet,
2831 : void *aClosure) :
2832 : mCallback(aCallback),
2833 : mGenericFamily(aGenericFamily),
2834 : mUseFontSet(aUseFontSet),
2835 0 : mClosure(aClosure) {
2836 0 : }
2837 : gfxFontGroup::FontCreationCallback mCallback;
2838 : nsCString mGenericFamily;
2839 : bool mUseFontSet;
2840 : void *mClosure;
2841 : };
2842 :
2843 : bool
2844 0 : gfxFontGroup::ForEachFontInternal(const nsAString& aFamilies,
2845 : nsIAtom *aLanguage,
2846 : bool aResolveGeneric,
2847 : bool aResolveFontName,
2848 : bool aUseFontSet,
2849 : FontCreationCallback fc,
2850 : void *closure)
2851 : {
2852 0 : const PRUnichar kSingleQuote = PRUnichar('\'');
2853 0 : const PRUnichar kDoubleQuote = PRUnichar('\"');
2854 0 : const PRUnichar kComma = PRUnichar(',');
2855 :
2856 0 : nsIAtom *groupAtom = nsnull;
2857 0 : nsCAutoString groupString;
2858 0 : if (aLanguage) {
2859 0 : if (!gLangService) {
2860 0 : CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
2861 : }
2862 0 : if (gLangService) {
2863 : nsresult rv;
2864 0 : groupAtom = gLangService->GetLanguageGroup(aLanguage, &rv);
2865 : }
2866 : }
2867 0 : if (!groupAtom) {
2868 0 : groupAtom = gfxAtoms::x_unicode;
2869 : }
2870 0 : groupAtom->ToUTF8String(groupString);
2871 :
2872 0 : nsPromiseFlatString families(aFamilies);
2873 : const PRUnichar *p, *p_end;
2874 0 : families.BeginReading(p);
2875 0 : families.EndReading(p_end);
2876 0 : nsAutoString family;
2877 0 : nsCAutoString lcFamily;
2878 0 : nsAutoString genericFamily;
2879 :
2880 0 : while (p < p_end) {
2881 0 : while (nsCRT::IsAsciiSpace(*p) || *p == kComma)
2882 0 : if (++p == p_end)
2883 0 : return true;
2884 :
2885 : bool generic;
2886 0 : if (*p == kSingleQuote || *p == kDoubleQuote) {
2887 : // quoted font family
2888 0 : PRUnichar quoteMark = *p;
2889 0 : if (++p == p_end)
2890 0 : return true;
2891 0 : const PRUnichar *nameStart = p;
2892 :
2893 : // XXX What about CSS character escapes?
2894 0 : while (*p != quoteMark)
2895 0 : if (++p == p_end)
2896 0 : return true;
2897 :
2898 0 : family = Substring(nameStart, p);
2899 0 : generic = false;
2900 0 : genericFamily.SetIsVoid(true);
2901 :
2902 0 : while (++p != p_end && *p != kComma)
2903 0 : /* nothing */ ;
2904 :
2905 : } else {
2906 : // unquoted font family
2907 0 : const PRUnichar *nameStart = p;
2908 0 : while (++p != p_end && *p != kComma)
2909 : /* nothing */ ;
2910 :
2911 0 : family = Substring(nameStart, p);
2912 0 : family.CompressWhitespace(false, true);
2913 :
2914 0 : if (aResolveGeneric &&
2915 0 : (family.LowerCaseEqualsLiteral("serif") ||
2916 0 : family.LowerCaseEqualsLiteral("sans-serif") ||
2917 0 : family.LowerCaseEqualsLiteral("monospace") ||
2918 0 : family.LowerCaseEqualsLiteral("cursive") ||
2919 0 : family.LowerCaseEqualsLiteral("fantasy")))
2920 : {
2921 0 : generic = true;
2922 :
2923 0 : ToLowerCase(NS_LossyConvertUTF16toASCII(family), lcFamily);
2924 :
2925 0 : nsCAutoString prefName("font.name.");
2926 0 : prefName.Append(lcFamily);
2927 0 : prefName.AppendLiteral(".");
2928 0 : prefName.Append(groupString);
2929 :
2930 0 : nsAdoptingString value = Preferences::GetString(prefName.get());
2931 0 : if (value) {
2932 0 : CopyASCIItoUTF16(lcFamily, genericFamily);
2933 0 : family = value;
2934 : }
2935 : } else {
2936 0 : generic = false;
2937 0 : genericFamily.SetIsVoid(true);
2938 : }
2939 : }
2940 :
2941 0 : NS_LossyConvertUTF16toASCII gf(genericFamily);
2942 0 : if (generic) {
2943 : ForEachFontInternal(family, groupAtom, false,
2944 : aResolveFontName, false,
2945 0 : fc, closure);
2946 0 : } else if (!family.IsEmpty()) {
2947 0 : if (aResolveFontName) {
2948 0 : ResolveData data(fc, gf, aUseFontSet, closure);
2949 0 : bool aborted = false, needsBold;
2950 0 : nsresult rv = NS_OK;
2951 0 : bool foundFamily = false;
2952 0 : bool waitForUserFont = false;
2953 0 : if (aUseFontSet && mUserFontSet &&
2954 : mUserFontSet->FindFontEntry(family, mStyle, foundFamily,
2955 0 : needsBold, waitForUserFont))
2956 : {
2957 0 : gfxFontGroup::FontResolverProc(family, &data);
2958 : } else {
2959 0 : if (waitForUserFont) {
2960 0 : mSkipDrawing = true;
2961 : }
2962 0 : if (!foundFamily) {
2963 0 : gfxPlatform *pf = gfxPlatform::GetPlatform();
2964 : rv = pf->ResolveFontName(family,
2965 : gfxFontGroup::FontResolverProc,
2966 0 : &data, aborted);
2967 : }
2968 : }
2969 0 : if (NS_FAILED(rv) || aborted)
2970 0 : return false;
2971 : }
2972 : else {
2973 0 : if (!fc(family, gf, aUseFontSet, closure))
2974 0 : return false;
2975 : }
2976 : }
2977 :
2978 0 : if (generic && aResolveGeneric) {
2979 0 : nsCAutoString prefName("font.name-list.");
2980 0 : prefName.Append(lcFamily);
2981 0 : prefName.AppendLiteral(".");
2982 0 : prefName.Append(groupString);
2983 0 : nsAdoptingString value = Preferences::GetString(prefName.get());
2984 0 : if (value) {
2985 : ForEachFontInternal(value, groupAtom, false,
2986 : aResolveFontName, false,
2987 0 : fc, closure);
2988 : }
2989 : }
2990 :
2991 0 : ++p; // may advance past p_end
2992 : }
2993 :
2994 0 : return true;
2995 : }
2996 :
2997 : bool
2998 0 : gfxFontGroup::FontResolverProc(const nsAString& aName, void *aClosure)
2999 : {
3000 0 : ResolveData *data = reinterpret_cast<ResolveData*>(aClosure);
3001 : return (data->mCallback)(aName, data->mGenericFamily, data->mUseFontSet,
3002 0 : data->mClosure);
3003 : }
3004 :
3005 : gfxTextRun *
3006 0 : gfxFontGroup::MakeEmptyTextRun(const Parameters *aParams, PRUint32 aFlags)
3007 : {
3008 0 : aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
3009 0 : return gfxTextRun::Create(aParams, nsnull, 0, this, aFlags);
3010 : }
3011 :
3012 : gfxTextRun *
3013 0 : gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, PRUint32 aFlags)
3014 : {
3015 0 : aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
3016 : static const PRUint8 space = ' ';
3017 :
3018 0 : gfxTextRun *textRun = gfxTextRun::Create(aParams, &space, 1, this, aFlags);
3019 0 : if (!textRun) {
3020 0 : return nsnull;
3021 : }
3022 :
3023 0 : gfxFont *font = GetFontAt(0);
3024 0 : if (NS_UNLIKELY(GetStyle()->size == 0)) {
3025 : // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
3026 : // them, and always create at least size 1 fonts, i.e. they still
3027 : // render something for size 0 fonts.
3028 0 : textRun->AddGlyphRun(font, gfxTextRange::kFontGroup, 0, false);
3029 : }
3030 : else {
3031 0 : textRun->SetSpaceGlyph(font, aParams->mContext, 0);
3032 : }
3033 :
3034 : // Note that the gfxGlyphExtents glyph bounds storage for the font will
3035 : // always contain an entry for the font's space glyph, so we don't have
3036 : // to call FetchGlyphExtents here.
3037 0 : return textRun;
3038 : }
3039 :
3040 : gfxTextRun *
3041 0 : gfxFontGroup::MakeBlankTextRun(const void* aText, PRUint32 aLength,
3042 : const Parameters *aParams, PRUint32 aFlags)
3043 : {
3044 : gfxTextRun *textRun =
3045 0 : gfxTextRun::Create(aParams, aText, aLength, this, aFlags);
3046 0 : if (!textRun) {
3047 0 : return nsnull;
3048 : }
3049 :
3050 0 : textRun->AddGlyphRun(GetFontAt(0), gfxTextRange::kFontGroup, 0, false);
3051 0 : return textRun;
3052 : }
3053 :
3054 : gfxTextRun *
3055 0 : gfxFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
3056 : const Parameters *aParams, PRUint32 aFlags)
3057 : {
3058 0 : if (aLength == 0) {
3059 0 : return MakeEmptyTextRun(aParams, aFlags);
3060 : }
3061 0 : if (aLength == 1 && aString[0] == ' ') {
3062 0 : return MakeSpaceTextRun(aParams, aFlags);
3063 : }
3064 :
3065 0 : aFlags |= TEXT_IS_8BIT;
3066 :
3067 0 : if (GetStyle()->size == 0) {
3068 : // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
3069 : // them, and always create at least size 1 fonts, i.e. they still
3070 : // render something for size 0 fonts.
3071 0 : return MakeBlankTextRun(aString, aLength, aParams, aFlags);
3072 : }
3073 :
3074 : gfxTextRun *textRun = gfxTextRun::Create(aParams, aString, aLength,
3075 0 : this, aFlags);
3076 0 : if (!textRun) {
3077 0 : return nsnull;
3078 : }
3079 :
3080 0 : InitTextRun(aParams->mContext, textRun, aString, aLength);
3081 :
3082 0 : textRun->FetchGlyphExtents(aParams->mContext);
3083 :
3084 0 : return textRun;
3085 : }
3086 :
3087 : gfxTextRun *
3088 0 : gfxFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
3089 : const Parameters *aParams, PRUint32 aFlags)
3090 : {
3091 0 : if (aLength == 0) {
3092 0 : return MakeEmptyTextRun(aParams, aFlags);
3093 : }
3094 0 : if (aLength == 1 && aString[0] == ' ') {
3095 0 : return MakeSpaceTextRun(aParams, aFlags);
3096 : }
3097 0 : if (GetStyle()->size == 0) {
3098 0 : return MakeBlankTextRun(aString, aLength, aParams, aFlags);
3099 : }
3100 :
3101 : gfxTextRun *textRun = gfxTextRun::Create(aParams, aString, aLength,
3102 0 : this, aFlags);
3103 0 : if (!textRun) {
3104 0 : return nsnull;
3105 : }
3106 :
3107 0 : InitTextRun(aParams->mContext, textRun, aString, aLength);
3108 :
3109 0 : textRun->FetchGlyphExtents(aParams->mContext);
3110 :
3111 0 : return textRun;
3112 : }
3113 :
3114 : template<typename T>
3115 : void
3116 : gfxFontGroup::InitTextRun(gfxContext *aContext,
3117 : gfxTextRun *aTextRun,
3118 : const T *aString,
3119 : PRUint32 aLength)
3120 : {
3121 : // we need to do numeral processing even on 8-bit text,
3122 : // in case we're converting Western to Hindi/Arabic digits
3123 0 : PRInt32 numOption = gfxPlatform::GetPlatform()->GetBidiNumeralOption();
3124 0 : nsAutoArrayPtr<PRUnichar> transformedString;
3125 0 : if (numOption != IBMBIDI_NUMERAL_NOMINAL) {
3126 : // scan the string for numerals that may need to be transformed;
3127 : // if we find any, we'll make a local copy here and use that for
3128 : // font matching and glyph generation/shaping
3129 : bool prevIsArabic =
3130 0 : (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_INCOMING_ARABICCHAR) != 0;
3131 0 : for (PRUint32 i = 0; i < aLength; ++i) {
3132 0 : PRUnichar origCh = aString[i];
3133 0 : PRUnichar newCh = HandleNumberInChar(origCh, prevIsArabic, numOption);
3134 0 : if (newCh != origCh) {
3135 0 : if (!transformedString) {
3136 0 : transformedString = new PRUnichar[aLength];
3137 : if (sizeof(T) == sizeof(PRUnichar)) {
3138 0 : memcpy(transformedString.get(), aString, i * sizeof(PRUnichar));
3139 : } else {
3140 0 : for (PRUint32 j = 0; j < i; ++j) {
3141 0 : transformedString[j] = aString[j];
3142 : }
3143 : }
3144 : }
3145 : }
3146 0 : if (transformedString) {
3147 0 : transformedString[i] = newCh;
3148 : }
3149 0 : prevIsArabic = IS_ARABIC_CHAR(newCh);
3150 : }
3151 : }
3152 :
3153 0 : if (sizeof(T) == sizeof(PRUint8) && !transformedString) {
3154 : // the text is still purely 8-bit; bypass the script-run itemizer
3155 : // and treat it as a single Latin run
3156 0 : InitScriptRun(aContext, aTextRun, aString,
3157 : 0, aLength, MOZ_SCRIPT_LATIN);
3158 : } else {
3159 : const PRUnichar *textPtr;
3160 0 : if (transformedString) {
3161 0 : textPtr = transformedString.get();
3162 : } else {
3163 : // typecast to avoid compilation error for the 8-bit version,
3164 : // even though this is dead code in that case
3165 0 : textPtr = reinterpret_cast<const PRUnichar*>(aString);
3166 : }
3167 :
3168 : // split into script runs so that script can potentially influence
3169 : // the font matching process below
3170 0 : gfxScriptItemizer scriptRuns(textPtr, aLength);
3171 :
3172 : #ifdef PR_LOGGING
3173 : PRLogModuleInfo *log = (mStyle.systemFont ?
3174 : gfxPlatform::GetLog(eGfxLog_textrunui) :
3175 0 : gfxPlatform::GetLog(eGfxLog_textrun));
3176 : #endif
3177 :
3178 0 : PRUint32 runStart = 0, runLimit = aLength;
3179 0 : PRInt32 runScript = MOZ_SCRIPT_LATIN;
3180 0 : while (scriptRuns.Next(runStart, runLimit, runScript)) {
3181 :
3182 : #ifdef PR_LOGGING
3183 0 : if (NS_UNLIKELY(log)) {
3184 0 : nsCAutoString lang;
3185 0 : mStyle.language->ToUTF8String(lang);
3186 0 : PRUint32 runLen = runLimit - runStart;
3187 0 : PR_LOG(log, PR_LOG_WARNING,\
3188 : ("(%s) fontgroup: [%s] lang: %s script: %d len %d "
3189 : "weight: %d width: %d style: %s "
3190 : "TEXTRUN [%s] ENDTEXTRUN\n",
3191 : (mStyle.systemFont ? "textrunui" : "textrun"),
3192 : NS_ConvertUTF16toUTF8(mFamilies).get(),
3193 : lang.get(), runScript, runLen,
3194 : PRUint32(mStyle.weight), PRUint32(mStyle.stretch),
3195 : (mStyle.style & FONT_STYLE_ITALIC ? "italic" :
3196 : (mStyle.style & FONT_STYLE_OBLIQUE ? "oblique" :
3197 : "normal")),
3198 : NS_ConvertUTF16toUTF8(textPtr + runStart, runLen).get()));
3199 : }
3200 : #endif
3201 :
3202 0 : InitScriptRun(aContext, aTextRun, textPtr,
3203 : runStart, runLimit, runScript);
3204 : }
3205 : }
3206 :
3207 0 : if (sizeof(T) == sizeof(PRUnichar) && aLength > 0) {
3208 0 : gfxTextRun::CompressedGlyph *glyph = aTextRun->GetCharacterGlyphs();
3209 0 : if (!glyph->IsSimpleGlyph()) {
3210 0 : glyph->SetClusterStart(true);
3211 : }
3212 : }
3213 :
3214 : // It's possible for CoreText to omit glyph runs if it decides they contain
3215 : // only invisibles (e.g., U+FEFF, see reftest 474417-1). In this case, we
3216 : // need to eliminate them from the glyph run array to avoid drawing "partial
3217 : // ligatures" with the wrong font.
3218 : // We don't do this during InitScriptRun (or gfxFont::InitTextRun) because
3219 : // it will iterate back over all glyphruns in the textrun, which leads to
3220 : // pathologically-bad perf in the case where a textrun contains many script
3221 : // changes (see bug 680402) - we'd end up re-sanitizing all the earlier runs
3222 : // every time a new script subrun is processed.
3223 0 : aTextRun->SanitizeGlyphRuns();
3224 :
3225 0 : aTextRun->SortGlyphRuns();
3226 0 : }
3227 :
3228 : template<typename T>
3229 : void
3230 0 : gfxFontGroup::InitScriptRun(gfxContext *aContext,
3231 : gfxTextRun *aTextRun,
3232 : const T *aString,
3233 : PRUint32 aScriptRunStart,
3234 : PRUint32 aScriptRunEnd,
3235 : PRInt32 aRunScript)
3236 : {
3237 0 : gfxFont *mainFont = GetFontAt(0);
3238 :
3239 0 : PRUint32 runStart = aScriptRunStart;
3240 0 : nsAutoTArray<gfxTextRange,3> fontRanges;
3241 0 : ComputeRanges(fontRanges, aString + aScriptRunStart,
3242 : aScriptRunEnd - aScriptRunStart, aRunScript);
3243 0 : PRUint32 numRanges = fontRanges.Length();
3244 :
3245 0 : for (PRUint32 r = 0; r < numRanges; r++) {
3246 0 : const gfxTextRange& range = fontRanges[r];
3247 0 : PRUint32 matchedLength = range.Length();
3248 0 : gfxFont *matchedFont = (range.font ? range.font.get() : nsnull);
3249 :
3250 : // create the glyph run for this range
3251 0 : if (matchedFont) {
3252 0 : aTextRun->AddGlyphRun(matchedFont, range.matchType,
3253 : runStart, (matchedLength > 0));
3254 : // do glyph layout and record the resulting positioned glyphs
3255 0 : if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun, aString,
3256 : runStart, matchedLength,
3257 : aRunScript)) {
3258 : // glyph layout failed! treat as missing glyphs
3259 0 : matchedFont = nsnull;
3260 : }
3261 : } else {
3262 0 : aTextRun->AddGlyphRun(mainFont, gfxTextRange::kFontGroup,
3263 : runStart, (matchedLength > 0));
3264 : }
3265 :
3266 0 : if (!matchedFont) {
3267 : // for PRUnichar text, we need to set cluster boundaries so that
3268 : // surrogate pairs, combining characters, etc behave properly,
3269 : // even if we don't have glyphs for them
3270 : if (sizeof(T) == sizeof(PRUnichar)) {
3271 0 : gfxShapedWord::SetupClusterBoundaries(aTextRun->GetCharacterGlyphs() + runStart,
3272 : reinterpret_cast<const PRUnichar*>(aString) + runStart,
3273 : matchedLength);
3274 : }
3275 :
3276 : // various "missing" characters may need special handling,
3277 : // so we check for them here
3278 0 : PRUint32 runLimit = runStart + matchedLength;
3279 0 : for (PRUint32 index = runStart; index < runLimit; index++) {
3280 0 : T ch = aString[index];
3281 :
3282 : // tab and newline are not to be displayed as hexboxes,
3283 : // but do need to be recorded in the textrun
3284 0 : if (ch == '\n') {
3285 0 : aTextRun->SetIsNewline(index);
3286 0 : continue;
3287 : }
3288 0 : if (ch == '\t') {
3289 0 : aTextRun->SetIsTab(index);
3290 0 : continue;
3291 : }
3292 :
3293 : // for 16-bit textruns only, check for surrogate pairs and
3294 : // special Unicode spaces; omit these checks in 8-bit runs
3295 : if (sizeof(T) == sizeof(PRUnichar)) {
3296 0 : if (NS_IS_HIGH_SURROGATE(ch) &&
3297 : index + 1 < aScriptRunEnd &&
3298 : NS_IS_LOW_SURROGATE(aString[index + 1]))
3299 : {
3300 0 : aTextRun->SetMissingGlyph(index,
3301 : SURROGATE_TO_UCS4(ch,
3302 : aString[index + 1]));
3303 0 : index++;
3304 0 : aTextRun->SetIsLowSurrogate(index);
3305 0 : continue;
3306 : }
3307 :
3308 : // check if this is a known Unicode whitespace character that
3309 : // we can render using the space glyph with a custom width
3310 0 : gfxFloat wid = mainFont->SynthesizeSpaceWidth(ch);
3311 0 : if (wid >= 0.0) {
3312 : nscoord advance =
3313 0 : aTextRun->GetAppUnitsPerDevUnit() * floor(wid + 0.5);
3314 0 : gfxTextRun::CompressedGlyph g;
3315 0 : if (gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance)) {
3316 0 : aTextRun->SetSimpleGlyph(index,
3317 : g.SetSimpleGlyph(advance,
3318 : mainFont->GetSpaceGlyph()));
3319 : } else {
3320 : gfxTextRun::DetailedGlyph detailedGlyph;
3321 0 : detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph();
3322 0 : detailedGlyph.mAdvance = advance;
3323 0 : detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
3324 0 : g.SetComplex(true, true, 1);
3325 0 : aTextRun->SetGlyphs(index,
3326 : g, &detailedGlyph);
3327 : }
3328 0 : continue;
3329 : }
3330 : }
3331 :
3332 0 : if (IsInvalidChar(ch)) {
3333 : // invalid chars are left as zero-width/invisible
3334 0 : continue;
3335 : }
3336 :
3337 : // record char code so we can draw a box with the Unicode value
3338 0 : aTextRun->SetMissingGlyph(index, ch);
3339 : }
3340 : }
3341 :
3342 0 : runStart += matchedLength;
3343 : }
3344 0 : }
3345 :
3346 : already_AddRefed<gfxFont>
3347 0 : gfxFontGroup::FindFontForChar(PRUint32 aCh, PRUint32 aPrevCh,
3348 : PRInt32 aRunScript, gfxFont *aPrevMatchedFont,
3349 : PRUint8 *aMatchType)
3350 : {
3351 0 : nsRefPtr<gfxFont> selectedFont;
3352 :
3353 0 : if (aPrevMatchedFont) {
3354 : // Don't switch fonts for control characters, regardless of
3355 : // whether they are present in the current font, as they won't
3356 : // actually be rendered (see bug 716229)
3357 0 : PRUint8 category = GetGeneralCategory(aCh);
3358 0 : if (category == HB_UNICODE_GENERAL_CATEGORY_CONTROL) {
3359 0 : selectedFont = aPrevMatchedFont;
3360 0 : return selectedFont.forget();
3361 : }
3362 :
3363 : // if this character is a join-control or the previous is a join-causer,
3364 : // use the same font as the previous range if we can
3365 0 : if (gfxFontUtils::IsJoinControl(aCh) ||
3366 0 : gfxFontUtils::IsJoinCauser(aPrevCh)) {
3367 0 : if (aPrevMatchedFont->HasCharacter(aCh)) {
3368 0 : selectedFont = aPrevMatchedFont;
3369 0 : return selectedFont.forget();
3370 : }
3371 : }
3372 : }
3373 :
3374 : // if this character is a variation selector,
3375 : // use the previous font regardless of whether it supports VS or not.
3376 : // otherwise the text run will be divided.
3377 0 : if (gfxFontUtils::IsVarSelector(aCh)) {
3378 0 : if (aPrevMatchedFont) {
3379 0 : selectedFont = aPrevMatchedFont;
3380 0 : return selectedFont.forget();
3381 : }
3382 : // VS alone. it's meaningless to search different fonts
3383 0 : return nsnull;
3384 : }
3385 :
3386 : // 1. check fonts in the font group
3387 0 : for (PRUint32 i = 0; i < FontListLength(); i++) {
3388 0 : nsRefPtr<gfxFont> font = GetFontAt(i);
3389 0 : if (font->HasCharacter(aCh)) {
3390 0 : *aMatchType = gfxTextRange::kFontGroup;
3391 0 : return font.forget();
3392 : }
3393 :
3394 : // check other faces of the family
3395 0 : gfxFontFamily *family = font->GetFontEntry()->Family();
3396 0 : if (family && family->TestCharacterMap(aCh)) {
3397 0 : GlobalFontMatch matchData(aCh, aRunScript, &mStyle);
3398 0 : family->SearchAllFontsForChar(&matchData);
3399 0 : gfxFontEntry *fe = matchData.mBestMatch;
3400 0 : if (fe) {
3401 : bool needsBold =
3402 0 : font->GetStyle()->weight >= 600 && !fe->IsBold();
3403 : selectedFont =
3404 0 : fe->FindOrMakeFont(font->GetStyle(), needsBold);
3405 0 : if (selectedFont) {
3406 0 : return selectedFont.forget();
3407 : }
3408 : }
3409 : }
3410 : }
3411 :
3412 : // if character is in Private Use Area, don't do matching against pref or system fonts
3413 0 : if ((aCh >= 0xE000 && aCh <= 0xF8FF) || (aCh >= 0xF0000 && aCh <= 0x10FFFD))
3414 0 : return nsnull;
3415 :
3416 : // 2. search pref fonts
3417 0 : if ((selectedFont = WhichPrefFontSupportsChar(aCh))) {
3418 0 : *aMatchType = gfxTextRange::kPrefsFallback;
3419 0 : return selectedFont.forget();
3420 : }
3421 :
3422 : // 3. use fallback fonts
3423 : // -- before searching for something else check the font used for the previous character
3424 0 : if (!selectedFont && aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
3425 0 : *aMatchType = gfxTextRange::kSystemFallback;
3426 0 : selectedFont = aPrevMatchedFont;
3427 0 : return selectedFont.forget();
3428 : }
3429 :
3430 : // never fall back for characters from unknown scripts
3431 0 : if (aRunScript == HB_SCRIPT_UNKNOWN) {
3432 0 : return nsnull;
3433 : }
3434 :
3435 : // for known "space" characters, don't do a full system-fallback search;
3436 : // we'll synthesize appropriate-width spaces instead of missing-glyph boxes
3437 0 : if (GetGeneralCategory(aCh) ==
3438 : HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR &&
3439 0 : GetFontAt(0)->SynthesizeSpaceWidth(aCh) >= 0.0)
3440 : {
3441 0 : return nsnull;
3442 : }
3443 :
3444 : // -- otherwise look for other stuff
3445 0 : if (!selectedFont) {
3446 0 : *aMatchType = gfxTextRange::kSystemFallback;
3447 0 : selectedFont = WhichSystemFontSupportsChar(aCh, aRunScript);
3448 0 : return selectedFont.forget();
3449 : }
3450 :
3451 0 : return nsnull;
3452 : }
3453 :
3454 : template<typename T>
3455 0 : void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
3456 : const T *aString, PRUint32 aLength,
3457 : PRInt32 aRunScript)
3458 : {
3459 0 : aRanges.Clear();
3460 :
3461 0 : if (aLength == 0) {
3462 0 : return;
3463 : }
3464 :
3465 0 : PRUint32 prevCh = 0;
3466 0 : PRUint8 matchType = 0;
3467 :
3468 : // initialize prevFont to the group's primary font, so that this will be
3469 : // used for string-initial control chars, etc rather than risk hitting font
3470 : // fallback for these (bug 716229)
3471 0 : gfxFont *prevFont = GetFontAt(0);
3472 :
3473 0 : for (PRUint32 i = 0; i < aLength; i++) {
3474 :
3475 0 : const PRUint32 origI = i; // save off in case we increase for surrogate
3476 :
3477 : // set up current ch
3478 0 : PRUint32 ch = aString[i];
3479 :
3480 : // in 16-bit case only, check for surrogate pair
3481 : if (sizeof(T) == sizeof(PRUnichar)) {
3482 0 : if ((i + 1 < aLength) && NS_IS_HIGH_SURROGATE(ch) &&
3483 : NS_IS_LOW_SURROGATE(aString[i + 1])) {
3484 0 : i++;
3485 0 : ch = SURROGATE_TO_UCS4(ch, aString[i]);
3486 : }
3487 : }
3488 :
3489 0 : if (ch == 0xa0) {
3490 0 : ch = ' ';
3491 : }
3492 :
3493 : // find the font for this char
3494 : nsRefPtr<gfxFont> font =
3495 0 : FindFontForChar(ch, prevCh, aRunScript, prevFont, &matchType);
3496 :
3497 0 : prevCh = ch;
3498 :
3499 0 : if (aRanges.Length() == 0) {
3500 : // first char ==> make a new range
3501 0 : aRanges.AppendElement(gfxTextRange(0, 1, font, matchType));
3502 0 : prevFont = font;
3503 : } else {
3504 : // if font has changed, make a new range
3505 0 : gfxTextRange& prevRange = aRanges[aRanges.Length() - 1];
3506 0 : if (prevRange.font != font || prevRange.matchType != matchType) {
3507 : // close out the previous range
3508 0 : prevRange.end = origI;
3509 0 : aRanges.AppendElement(gfxTextRange(origI, i + 1,
3510 : font, matchType));
3511 :
3512 : // update prevFont for the next match, *unless* we switched
3513 : // fonts on a ZWJ, in which case propagating the changed font
3514 : // is probably not a good idea (see bug 619511)
3515 0 : if (sizeof(T) == sizeof(PRUint8) ||
3516 : !gfxFontUtils::IsJoinCauser(ch))
3517 : {
3518 0 : prevFont = font;
3519 : }
3520 : }
3521 : }
3522 : }
3523 0 : aRanges[aRanges.Length() - 1].end = aLength;
3524 : }
3525 :
3526 : gfxUserFontSet*
3527 0 : gfxFontGroup::GetUserFontSet()
3528 : {
3529 0 : return mUserFontSet;
3530 : }
3531 :
3532 : void
3533 0 : gfxFontGroup::SetUserFontSet(gfxUserFontSet *aUserFontSet)
3534 : {
3535 0 : NS_IF_RELEASE(mUserFontSet);
3536 0 : mUserFontSet = aUserFontSet;
3537 0 : NS_IF_ADDREF(mUserFontSet);
3538 0 : mCurrGeneration = GetGeneration();
3539 0 : }
3540 :
3541 : PRUint64
3542 0 : gfxFontGroup::GetGeneration()
3543 : {
3544 0 : if (!mUserFontSet)
3545 0 : return 0;
3546 0 : return mUserFontSet->GetGeneration();
3547 : }
3548 :
3549 : void
3550 0 : gfxFontGroup::UpdateFontList()
3551 : {
3552 0 : if (mUserFontSet && mCurrGeneration != GetGeneration()) {
3553 : // xxx - can probably improve this to detect when all fonts were found, so no need to update list
3554 0 : mFonts.Clear();
3555 0 : mUnderlineOffset = UNDERLINE_OFFSET_NOT_SET;
3556 0 : mSkipDrawing = false;
3557 :
3558 : // bug 548184 - need to clean up FT2, OS/2 platform code to use BuildFontList
3559 : #if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID)
3560 : BuildFontList();
3561 : #else
3562 0 : ForEachFont(FindPlatformFont, this);
3563 : #endif
3564 0 : mCurrGeneration = GetGeneration();
3565 : }
3566 0 : }
3567 :
3568 : struct PrefFontCallbackData {
3569 0 : PrefFontCallbackData(nsTArray<nsRefPtr<gfxFontFamily> >& aFamiliesArray)
3570 0 : : mPrefFamilies(aFamiliesArray)
3571 0 : {}
3572 :
3573 : nsTArray<nsRefPtr<gfxFontFamily> >& mPrefFamilies;
3574 :
3575 0 : static bool AddFontFamilyEntry(eFontPrefLang aLang, const nsAString& aName, void *aClosure)
3576 : {
3577 0 : PrefFontCallbackData *prefFontData = static_cast<PrefFontCallbackData*>(aClosure);
3578 :
3579 0 : gfxFontFamily *family = gfxPlatformFontList::PlatformFontList()->FindFamily(aName);
3580 0 : if (family) {
3581 0 : prefFontData->mPrefFamilies.AppendElement(family);
3582 : }
3583 0 : return true;
3584 : }
3585 : };
3586 :
3587 : already_AddRefed<gfxFont>
3588 0 : gfxFontGroup::WhichPrefFontSupportsChar(PRUint32 aCh)
3589 : {
3590 : gfxFont *font;
3591 :
3592 : // get the pref font list if it hasn't been set up already
3593 0 : PRUint32 unicodeRange = FindCharUnicodeRange(aCh);
3594 0 : eFontPrefLang charLang = gfxPlatform::GetPlatform()->GetFontPrefLangFor(unicodeRange);
3595 :
3596 : // if the last pref font was the first family in the pref list, no need to recheck through a list of families
3597 0 : if (mLastPrefFont && charLang == mLastPrefLang &&
3598 0 : mLastPrefFirstFont && mLastPrefFont->HasCharacter(aCh)) {
3599 0 : font = mLastPrefFont;
3600 0 : NS_ADDREF(font);
3601 0 : return font;
3602 : }
3603 :
3604 : // based on char lang and page lang, set up list of pref lang fonts to check
3605 : eFontPrefLang prefLangs[kMaxLenPrefLangList];
3606 0 : PRUint32 i, numLangs = 0;
3607 :
3608 0 : gfxPlatform::GetPlatform()->GetLangPrefs(prefLangs, numLangs, charLang, mPageLang);
3609 :
3610 0 : for (i = 0; i < numLangs; i++) {
3611 0 : nsAutoTArray<nsRefPtr<gfxFontFamily>, 5> families;
3612 0 : eFontPrefLang currentLang = prefLangs[i];
3613 :
3614 0 : gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList();
3615 :
3616 : // get the pref families for a single pref lang
3617 0 : if (!fontList->GetPrefFontFamilyEntries(currentLang, &families)) {
3618 0 : eFontPrefLang prefLangsToSearch[1] = { currentLang };
3619 0 : PrefFontCallbackData prefFontData(families);
3620 : gfxPlatform::ForEachPrefFont(prefLangsToSearch, 1, PrefFontCallbackData::AddFontFamilyEntry,
3621 0 : &prefFontData);
3622 0 : fontList->SetPrefFontFamilyEntries(currentLang, families);
3623 : }
3624 :
3625 : // find the first pref font that includes the character
3626 : PRUint32 j, numPrefs;
3627 0 : numPrefs = families.Length();
3628 0 : for (j = 0; j < numPrefs; j++) {
3629 : // look up the appropriate face
3630 0 : gfxFontFamily *family = families[j];
3631 0 : if (!family) continue;
3632 :
3633 : // if a pref font is used, it's likely to be used again in the same text run.
3634 : // the style doesn't change so the face lookup can be cached rather than calling
3635 : // FindOrMakeFont repeatedly. speeds up FindFontForChar lookup times for subsequent
3636 : // pref font lookups
3637 0 : if (family == mLastPrefFamily && mLastPrefFont->HasCharacter(aCh)) {
3638 0 : font = mLastPrefFont;
3639 0 : NS_ADDREF(font);
3640 0 : return font;
3641 : }
3642 :
3643 : bool needsBold;
3644 0 : gfxFontEntry *fe = family->FindFontForStyle(mStyle, needsBold);
3645 : // if ch in cmap, create and return a gfxFont
3646 0 : if (fe && fe->TestCharacterMap(aCh)) {
3647 0 : nsRefPtr<gfxFont> prefFont = fe->FindOrMakeFont(&mStyle, needsBold);
3648 0 : if (!prefFont) continue;
3649 0 : mLastPrefFamily = family;
3650 0 : mLastPrefFont = prefFont;
3651 0 : mLastPrefLang = charLang;
3652 0 : mLastPrefFirstFont = (i == 0 && j == 0);
3653 0 : return prefFont.forget();
3654 : }
3655 :
3656 : }
3657 : }
3658 :
3659 0 : return nsnull;
3660 : }
3661 :
3662 : already_AddRefed<gfxFont>
3663 0 : gfxFontGroup::WhichSystemFontSupportsChar(PRUint32 aCh, PRInt32 aRunScript)
3664 : {
3665 : gfxFontEntry *fe =
3666 0 : gfxPlatformFontList::PlatformFontList()->
3667 0 : SystemFindFontForChar(aCh, aRunScript, &mStyle);
3668 0 : if (fe) {
3669 : // ignore bolder considerations in system fallback case...
3670 0 : nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, false);
3671 0 : return font.forget();
3672 : }
3673 :
3674 0 : return nsnull;
3675 : }
3676 :
3677 : /*static*/ void
3678 4 : gfxFontGroup::Shutdown()
3679 : {
3680 4 : NS_IF_RELEASE(gLangService);
3681 4 : }
3682 :
3683 : nsILanguageAtomService* gfxFontGroup::gLangService = nsnull;
3684 :
3685 :
3686 : #define DEFAULT_PIXEL_FONT_SIZE 16.0f
3687 :
3688 : /*static*/ void
3689 0 : gfxFontStyle::ParseFontFeatureSettings(const nsString& aFeatureString,
3690 : nsTArray<gfxFontFeature>& aFeatures)
3691 : {
3692 0 : aFeatures.Clear();
3693 0 : PRUint32 offset = 0;
3694 0 : while (offset < aFeatureString.Length()) {
3695 : // skip whitespace
3696 0 : while (offset < aFeatureString.Length() &&
3697 0 : nsCRT::IsAsciiSpace(aFeatureString[offset])) {
3698 0 : ++offset;
3699 : }
3700 0 : PRInt32 limit = aFeatureString.FindChar(',', offset);
3701 0 : if (limit < 0) {
3702 0 : limit = aFeatureString.Length();
3703 : }
3704 : // check that we have enough text for a 4-char tag,
3705 : // the '=' sign, and at least one digit
3706 0 : if (offset + 6 <= PRUint32(limit) &&
3707 0 : aFeatureString[offset+4] == '=') {
3708 : gfxFontFeature setting;
3709 : setting.mTag =
3710 0 : ((aFeatureString[offset] & 0xff) << 24) +
3711 0 : ((aFeatureString[offset+1] & 0xff) << 16) +
3712 0 : ((aFeatureString[offset+2] & 0xff) << 8) +
3713 0 : (aFeatureString[offset+3] & 0xff);
3714 0 : nsString valString;
3715 0 : aFeatureString.Mid(valString, offset+5, limit-offset-5);
3716 : PRInt32 rv;
3717 0 : setting.mValue = valString.ToInteger(&rv);
3718 0 : if (rv == NS_OK) {
3719 : PRUint32 i;
3720 : // could optimize this based on the fact that the features array
3721 : // is sorted, but it's unlikely to be more than a few entries
3722 0 : for (i = 0; i < aFeatures.Length(); i++) {
3723 0 : if (aFeatures[i].mTag == setting.mTag) {
3724 0 : aFeatures[i].mValue = setting.mValue;
3725 0 : break;
3726 : }
3727 : }
3728 0 : if (i == aFeatures.Length()) {
3729 : // we keep the features array sorted so that we can
3730 : // use nsTArray<>::Equals() to compare feature lists
3731 0 : aFeatures.InsertElementSorted(setting);
3732 : }
3733 : }
3734 : }
3735 0 : offset = limit + 1;
3736 : }
3737 0 : }
3738 :
3739 : /*static*/ PRUint32
3740 0 : gfxFontStyle::ParseFontLanguageOverride(const nsString& aLangTag)
3741 : {
3742 0 : if (!aLangTag.Length() || aLangTag.Length() > 4) {
3743 0 : return NO_FONT_LANGUAGE_OVERRIDE;
3744 : }
3745 0 : PRUint32 index, result = 0;
3746 0 : for (index = 0; index < aLangTag.Length(); ++index) {
3747 0 : PRUnichar ch = aLangTag[index];
3748 0 : if (!nsCRT::IsAscii(ch)) { // valid tags are pure ASCII
3749 0 : return NO_FONT_LANGUAGE_OVERRIDE;
3750 : }
3751 0 : result = (result << 8) + ch;
3752 : }
3753 0 : while (index++ < 4) {
3754 0 : result = (result << 8) + 0x20;
3755 : }
3756 0 : return result;
3757 : }
3758 :
3759 0 : gfxFontStyle::gfxFontStyle() :
3760 : style(FONT_STYLE_NORMAL), systemFont(true), printerFont(false),
3761 : weight(FONT_WEIGHT_NORMAL),
3762 : stretch(NS_FONT_STRETCH_NORMAL), size(DEFAULT_PIXEL_FONT_SIZE),
3763 : sizeAdjust(0.0f),
3764 : language(gfxAtoms::x_western),
3765 0 : languageOverride(NO_FONT_LANGUAGE_OVERRIDE)
3766 : {
3767 0 : }
3768 :
3769 0 : gfxFontStyle::gfxFontStyle(PRUint8 aStyle, PRUint16 aWeight, PRInt16 aStretch,
3770 : gfxFloat aSize, nsIAtom *aLanguage,
3771 : float aSizeAdjust, bool aSystemFont,
3772 : bool aPrinterFont,
3773 : const nsString& aFeatureSettings,
3774 : const nsString& aLanguageOverride):
3775 : style(aStyle), systemFont(aSystemFont), printerFont(aPrinterFont),
3776 : weight(aWeight), stretch(aStretch),
3777 : size(aSize), sizeAdjust(aSizeAdjust),
3778 : language(aLanguage),
3779 0 : languageOverride(ParseFontLanguageOverride(aLanguageOverride))
3780 : {
3781 0 : ParseFontFeatureSettings(aFeatureSettings, featureSettings);
3782 :
3783 0 : if (weight > 900)
3784 0 : weight = 900;
3785 0 : if (weight < 100)
3786 0 : weight = 100;
3787 :
3788 0 : if (size >= FONT_MAX_SIZE) {
3789 0 : size = FONT_MAX_SIZE;
3790 0 : sizeAdjust = 0.0;
3791 0 : } else if (size < 0.0) {
3792 0 : NS_WARNING("negative font size");
3793 0 : size = 0.0;
3794 : }
3795 :
3796 0 : if (!language) {
3797 0 : NS_WARNING("null language");
3798 0 : language = gfxAtoms::x_western;
3799 : }
3800 0 : }
3801 :
3802 0 : gfxFontStyle::gfxFontStyle(const gfxFontStyle& aStyle) :
3803 : style(aStyle.style), systemFont(aStyle.systemFont), printerFont(aStyle.printerFont),
3804 : weight(aStyle.weight),
3805 : stretch(aStyle.stretch), size(aStyle.size),
3806 : sizeAdjust(aStyle.sizeAdjust),
3807 : language(aStyle.language),
3808 0 : languageOverride(aStyle.languageOverride)
3809 : {
3810 0 : featureSettings.AppendElements(aStyle.featureSettings);
3811 0 : }
3812 :
3813 : PRInt8
3814 0 : gfxFontStyle::ComputeWeight() const
3815 : {
3816 0 : PRInt8 baseWeight = (weight + 50) / 100;
3817 :
3818 0 : if (baseWeight < 0)
3819 0 : baseWeight = 0;
3820 0 : if (baseWeight > 9)
3821 0 : baseWeight = 9;
3822 :
3823 0 : return baseWeight;
3824 : }
3825 :
3826 : // This is not a member function of gfxShapedWord because it is also used
3827 : // by gfxFontGroup on missing-glyph runs, where we don't actually "shape"
3828 : // anything but still need to set cluster info.
3829 : /*static*/ void
3830 0 : gfxShapedWord::SetupClusterBoundaries(CompressedGlyph *aGlyphs,
3831 : const PRUnichar *aString, PRUint32 aLength)
3832 : {
3833 0 : gfxTextRun::CompressedGlyph extendCluster;
3834 0 : extendCluster.SetComplex(false, true, 0);
3835 :
3836 0 : ClusterIterator iter(aString, aLength);
3837 :
3838 : // the ClusterIterator won't be able to tell us if the string
3839 : // _begins_ with a cluster-extender, so we handle that here
3840 0 : if (aLength && IsClusterExtender(*aString)) {
3841 0 : *aGlyphs = extendCluster;
3842 : }
3843 :
3844 0 : while (!iter.AtEnd()) {
3845 : // advance iter to the next cluster-start (or end of text)
3846 0 : iter.Next();
3847 : // step past the first char of the cluster
3848 0 : aString++;
3849 0 : aGlyphs++;
3850 : // mark all the rest as cluster-continuations
3851 0 : while (aString < iter) {
3852 0 : *aGlyphs++ = extendCluster;
3853 0 : aString++;
3854 : }
3855 : }
3856 0 : }
3857 :
3858 : gfxShapedWord::DetailedGlyph *
3859 0 : gfxShapedWord::AllocateDetailedGlyphs(PRUint32 aIndex, PRUint32 aCount)
3860 : {
3861 0 : NS_ASSERTION(aIndex < Length(), "Index out of range");
3862 :
3863 0 : if (!mDetailedGlyphs) {
3864 0 : mDetailedGlyphs = new DetailedGlyphStore();
3865 : }
3866 :
3867 0 : DetailedGlyph *details = mDetailedGlyphs->Allocate(aIndex, aCount);
3868 0 : if (!details) {
3869 0 : mCharacterGlyphs[aIndex].SetMissing(0);
3870 0 : return nsnull;
3871 : }
3872 :
3873 0 : return details;
3874 : }
3875 :
3876 : void
3877 0 : gfxShapedWord::SetGlyphs(PRUint32 aIndex, CompressedGlyph aGlyph,
3878 : const DetailedGlyph *aGlyphs)
3879 : {
3880 0 : NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
3881 0 : NS_ASSERTION(aIndex > 0 || aGlyph.IsLigatureGroupStart(),
3882 : "First character can't be a ligature continuation!");
3883 :
3884 0 : PRUint32 glyphCount = aGlyph.GetGlyphCount();
3885 0 : if (glyphCount > 0) {
3886 0 : DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
3887 0 : if (!details) {
3888 0 : return;
3889 : }
3890 0 : memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
3891 : }
3892 0 : mCharacterGlyphs[aIndex] = aGlyph;
3893 : }
3894 :
3895 : #include "ignorable.x-ccmap"
3896 : DEFINE_X_CCMAP(gIgnorableCCMapExt, const);
3897 :
3898 : static inline bool
3899 0 : IsDefaultIgnorable(PRUint32 aChar)
3900 : {
3901 0 : return CCMAP_HAS_CHAR_EXT(gIgnorableCCMapExt, aChar);
3902 : }
3903 :
3904 : void
3905 0 : gfxShapedWord::SetMissingGlyph(PRUint32 aIndex, PRUint32 aChar, gfxFont *aFont)
3906 : {
3907 0 : DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
3908 0 : if (!details) {
3909 0 : return;
3910 : }
3911 :
3912 0 : details->mGlyphID = aChar;
3913 0 : if (IsDefaultIgnorable(aChar)) {
3914 : // Setting advance width to zero will prevent drawing the hexbox
3915 0 : details->mAdvance = 0;
3916 : } else {
3917 0 : gfxFloat width = NS_MAX(aFont->GetMetrics().aveCharWidth,
3918 0 : gfxFontMissingGlyphs::GetDesiredMinWidth(aChar));
3919 0 : details->mAdvance = PRUint32(width * mAppUnitsPerDevUnit);
3920 : }
3921 0 : details->mXOffset = 0;
3922 0 : details->mYOffset = 0;
3923 0 : mCharacterGlyphs[aIndex].SetMissing(1);
3924 : }
3925 :
3926 : bool
3927 0 : gfxShapedWord::FilterIfIgnorable(PRUint32 aIndex)
3928 : {
3929 0 : PRUint32 ch = GetCharAt(aIndex);
3930 0 : if (IsDefaultIgnorable(ch)) {
3931 0 : DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
3932 0 : if (details) {
3933 0 : details->mGlyphID = ch;
3934 0 : details->mAdvance = 0;
3935 0 : details->mXOffset = 0;
3936 0 : details->mYOffset = 0;
3937 0 : mCharacterGlyphs[aIndex].SetMissing(1);
3938 0 : return true;
3939 : }
3940 : }
3941 0 : return false;
3942 : }
3943 :
3944 : void
3945 0 : gfxShapedWord::AdjustAdvancesForSyntheticBold(float aSynBoldOffset)
3946 : {
3947 0 : PRUint32 synAppUnitOffset = aSynBoldOffset * mAppUnitsPerDevUnit;
3948 0 : for (PRUint32 i = 0; i < Length(); ++i) {
3949 0 : CompressedGlyph *glyphData = &mCharacterGlyphs[i];
3950 0 : if (glyphData->IsSimpleGlyph()) {
3951 : // simple glyphs ==> just add the advance
3952 0 : PRInt32 advance = glyphData->GetSimpleAdvance() + synAppUnitOffset;
3953 0 : if (CompressedGlyph::IsSimpleAdvance(advance)) {
3954 0 : glyphData->SetSimpleGlyph(advance, glyphData->GetSimpleGlyph());
3955 : } else {
3956 : // rare case, tested by making this the default
3957 0 : PRUint32 glyphIndex = glyphData->GetSimpleGlyph();
3958 0 : glyphData->SetComplex(true, true, 1);
3959 0 : DetailedGlyph detail = {glyphIndex, advance, 0, 0};
3960 0 : SetGlyphs(i, *glyphData, &detail);
3961 : }
3962 : } else {
3963 : // complex glyphs ==> add offset at cluster/ligature boundaries
3964 0 : PRUint32 detailedLength = glyphData->GetGlyphCount();
3965 0 : if (detailedLength) {
3966 0 : DetailedGlyph *details = GetDetailedGlyphs(i);
3967 0 : if (!details) {
3968 0 : continue;
3969 : }
3970 0 : if (IsRightToLeft()) {
3971 0 : details[0].mAdvance += synAppUnitOffset;
3972 : } else {
3973 0 : details[detailedLength - 1].mAdvance += synAppUnitOffset;
3974 : }
3975 : }
3976 : }
3977 : }
3978 0 : }
3979 :
3980 : bool
3981 0 : gfxTextRun::GlyphRunIterator::NextRun() {
3982 0 : if (mNextIndex >= mTextRun->mGlyphRuns.Length())
3983 0 : return false;
3984 0 : mGlyphRun = &mTextRun->mGlyphRuns[mNextIndex];
3985 0 : if (mGlyphRun->mCharacterOffset >= mEndOffset)
3986 0 : return false;
3987 :
3988 0 : mStringStart = NS_MAX(mStartOffset, mGlyphRun->mCharacterOffset);
3989 0 : PRUint32 last = mNextIndex + 1 < mTextRun->mGlyphRuns.Length()
3990 0 : ? mTextRun->mGlyphRuns[mNextIndex + 1].mCharacterOffset : mTextRun->mCharacterCount;
3991 0 : mStringEnd = NS_MIN(mEndOffset, last);
3992 :
3993 0 : ++mNextIndex;
3994 0 : return true;
3995 : }
3996 :
3997 : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
3998 : static void
3999 : AccountStorageForTextRun(gfxTextRun *aTextRun, PRInt32 aSign)
4000 : {
4001 : // Ignores detailed glyphs... we don't know when those have been constructed
4002 : // Also ignores gfxSkipChars dynamic storage (which won't be anything
4003 : // for preformatted text)
4004 : // Also ignores GlyphRun array, again because it hasn't been constructed
4005 : // by the time this gets called. If there's only one glyphrun that's stored
4006 : // directly in the textrun anyway so no additional overhead.
4007 : PRUint32 length = aTextRun->GetLength();
4008 : PRInt32 bytes = length * sizeof(gfxTextRun::CompressedGlyph);
4009 : bytes += sizeof(gfxTextRun);
4010 : gTextRunStorage += bytes*aSign;
4011 : gTextRunStorageHighWaterMark = NS_MAX(gTextRunStorageHighWaterMark, gTextRunStorage);
4012 : }
4013 : #endif
4014 :
4015 : // Helper for textRun creation to preallocate storage for glyph records;
4016 : // this function returns a pointer to the newly-allocated glyph storage.
4017 : // Returns nsnull if allocation fails.
4018 : void *
4019 0 : gfxTextRun::AllocateStorageForTextRun(size_t aSize, PRUint32 aLength)
4020 : {
4021 : // Allocate the storage we need, returning nsnull on failure rather than
4022 : // throwing an exception (because web content can create huge runs).
4023 0 : void *storage = moz_malloc(aSize + aLength * sizeof(CompressedGlyph));
4024 0 : if (!storage) {
4025 0 : NS_WARNING("failed to allocate storage for text run!");
4026 0 : return nsnull;
4027 : }
4028 :
4029 : // Initialize the glyph storage (beyond aSize) to zero
4030 0 : memset(reinterpret_cast<char*>(storage) + aSize, 0,
4031 0 : aLength * sizeof(CompressedGlyph));
4032 :
4033 0 : return storage;
4034 : }
4035 :
4036 : gfxTextRun *
4037 0 : gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams, const void *aText,
4038 : PRUint32 aLength, gfxFontGroup *aFontGroup, PRUint32 aFlags)
4039 : {
4040 0 : void *storage = AllocateStorageForTextRun(sizeof(gfxTextRun), aLength);
4041 0 : if (!storage) {
4042 0 : return nsnull;
4043 : }
4044 :
4045 0 : return new (storage) gfxTextRun(aParams, aText, aLength, aFontGroup, aFlags);
4046 : }
4047 :
4048 0 : gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams, const void *aText,
4049 : PRUint32 aLength, gfxFontGroup *aFontGroup, PRUint32 aFlags)
4050 : : mUserData(aParams->mUserData),
4051 : mFontGroup(aFontGroup),
4052 : mAppUnitsPerDevUnit(aParams->mAppUnitsPerDevUnit),
4053 0 : mFlags(aFlags), mCharacterCount(aLength)
4054 : {
4055 0 : NS_ASSERTION(mAppUnitsPerDevUnit != 0, "Invalid app unit scale");
4056 0 : MOZ_COUNT_CTOR(gfxTextRun);
4057 0 : NS_ADDREF(mFontGroup);
4058 :
4059 0 : mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>(this + 1);
4060 :
4061 0 : if (aParams->mSkipChars) {
4062 0 : mSkipChars.TakeFrom(aParams->mSkipChars);
4063 : }
4064 :
4065 : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
4066 : AccountStorageForTextRun(this, 1);
4067 : #endif
4068 :
4069 0 : mSkipDrawing = mFontGroup->ShouldSkipDrawing();
4070 0 : }
4071 :
4072 0 : gfxTextRun::~gfxTextRun()
4073 : {
4074 : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
4075 : AccountStorageForTextRun(this, -1);
4076 : #endif
4077 : #ifdef DEBUG
4078 : // Make it easy to detect a dead text run
4079 0 : mFlags = 0xFFFFFFFF;
4080 : #endif
4081 :
4082 0 : NS_RELEASE(mFontGroup);
4083 0 : MOZ_COUNT_DTOR(gfxTextRun);
4084 0 : }
4085 :
4086 : bool
4087 0 : gfxTextRun::SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
4088 : PRUint8 *aBreakBefore,
4089 : gfxContext *aRefContext)
4090 : {
4091 0 : NS_ASSERTION(aStart + aLength <= mCharacterCount, "Overflow");
4092 :
4093 0 : PRUint32 changed = 0;
4094 : PRUint32 i;
4095 0 : CompressedGlyph *charGlyphs = mCharacterGlyphs + aStart;
4096 0 : for (i = 0; i < aLength; ++i) {
4097 0 : PRUint8 canBreak = aBreakBefore[i];
4098 0 : if (canBreak && !charGlyphs[i].IsClusterStart()) {
4099 : // This can happen ... there is no guarantee that our linebreaking rules
4100 : // align with the platform's idea of what constitutes a cluster.
4101 0 : NS_WARNING("Break suggested inside cluster!");
4102 0 : canBreak = CompressedGlyph::FLAG_BREAK_TYPE_NONE;
4103 : }
4104 0 : changed |= charGlyphs[i].SetCanBreakBefore(canBreak);
4105 : }
4106 0 : return changed != 0;
4107 : }
4108 :
4109 : gfxTextRun::LigatureData
4110 0 : gfxTextRun::ComputeLigatureData(PRUint32 aPartStart, PRUint32 aPartEnd,
4111 : PropertyProvider *aProvider)
4112 : {
4113 0 : NS_ASSERTION(aPartStart < aPartEnd, "Computing ligature data for empty range");
4114 0 : NS_ASSERTION(aPartEnd <= mCharacterCount, "Character length overflow");
4115 :
4116 : LigatureData result;
4117 0 : CompressedGlyph *charGlyphs = mCharacterGlyphs;
4118 :
4119 : PRUint32 i;
4120 0 : for (i = aPartStart; !charGlyphs[i].IsLigatureGroupStart(); --i) {
4121 0 : NS_ASSERTION(i > 0, "Ligature at the start of the run??");
4122 : }
4123 0 : result.mLigatureStart = i;
4124 0 : for (i = aPartStart + 1; i < mCharacterCount && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
4125 : }
4126 0 : result.mLigatureEnd = i;
4127 :
4128 : PRInt32 ligatureWidth =
4129 0 : GetAdvanceForGlyphs(result.mLigatureStart, result.mLigatureEnd);
4130 : // Count the number of started clusters we have seen
4131 0 : PRUint32 totalClusterCount = 0;
4132 0 : PRUint32 partClusterIndex = 0;
4133 0 : PRUint32 partClusterCount = 0;
4134 0 : for (i = result.mLigatureStart; i < result.mLigatureEnd; ++i) {
4135 : // Treat the first character of the ligature as the start of a
4136 : // cluster for our purposes of allocating ligature width to its
4137 : // characters.
4138 0 : if (i == result.mLigatureStart || charGlyphs[i].IsClusterStart()) {
4139 0 : ++totalClusterCount;
4140 0 : if (i < aPartStart) {
4141 0 : ++partClusterIndex;
4142 0 : } else if (i < aPartEnd) {
4143 0 : ++partClusterCount;
4144 : }
4145 : }
4146 : }
4147 0 : NS_ASSERTION(totalClusterCount > 0, "Ligature involving no clusters??");
4148 0 : result.mPartAdvance = ligatureWidth*partClusterIndex/totalClusterCount;
4149 0 : result.mPartWidth = ligatureWidth*partClusterCount/totalClusterCount;
4150 :
4151 0 : if (partClusterCount == 0) {
4152 : // nothing to draw
4153 0 : result.mClipBeforePart = result.mClipAfterPart = true;
4154 : } else {
4155 : // Determine whether we should clip before or after this part when
4156 : // drawing its slice of the ligature.
4157 : // We need to clip before the part if any cluster is drawn before
4158 : // this part.
4159 0 : result.mClipBeforePart = partClusterIndex > 0;
4160 : // We need to clip after the part if any cluster is drawn after
4161 : // this part.
4162 0 : result.mClipAfterPart = partClusterIndex + partClusterCount < totalClusterCount;
4163 : }
4164 :
4165 0 : if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) {
4166 : gfxFont::Spacing spacing;
4167 0 : if (aPartStart == result.mLigatureStart) {
4168 0 : aProvider->GetSpacing(aPartStart, 1, &spacing);
4169 0 : result.mPartWidth += spacing.mBefore;
4170 : }
4171 0 : if (aPartEnd == result.mLigatureEnd) {
4172 0 : aProvider->GetSpacing(aPartEnd - 1, 1, &spacing);
4173 0 : result.mPartWidth += spacing.mAfter;
4174 : }
4175 : }
4176 :
4177 : return result;
4178 : }
4179 :
4180 : gfxFloat
4181 0 : gfxTextRun::ComputePartialLigatureWidth(PRUint32 aPartStart, PRUint32 aPartEnd,
4182 : PropertyProvider *aProvider)
4183 : {
4184 0 : if (aPartStart >= aPartEnd)
4185 0 : return 0;
4186 0 : LigatureData data = ComputeLigatureData(aPartStart, aPartEnd, aProvider);
4187 0 : return data.mPartWidth;
4188 : }
4189 :
4190 : PRInt32
4191 0 : gfxTextRun::GetAdvanceForGlyphs(PRUint32 aStart, PRUint32 aEnd)
4192 : {
4193 0 : const CompressedGlyph *glyphData = mCharacterGlyphs + aStart;
4194 0 : PRInt32 advance = 0;
4195 : PRUint32 i;
4196 0 : for (i = aStart; i < aEnd; ++i, ++glyphData) {
4197 0 : if (glyphData->IsSimpleGlyph()) {
4198 0 : advance += glyphData->GetSimpleAdvance();
4199 : } else {
4200 0 : PRUint32 glyphCount = glyphData->GetGlyphCount();
4201 0 : if (glyphCount == 0) {
4202 0 : continue;
4203 : }
4204 0 : const DetailedGlyph *details = GetDetailedGlyphs(i);
4205 0 : if (details) {
4206 : PRUint32 j;
4207 0 : for (j = 0; j < glyphCount; ++j, ++details) {
4208 0 : advance += details->mAdvance;
4209 : }
4210 : }
4211 : }
4212 : }
4213 0 : return advance;
4214 : }
4215 :
4216 : static void
4217 0 : GetAdjustedSpacing(gfxTextRun *aTextRun, PRUint32 aStart, PRUint32 aEnd,
4218 : gfxTextRun::PropertyProvider *aProvider,
4219 : gfxTextRun::PropertyProvider::Spacing *aSpacing)
4220 : {
4221 0 : if (aStart >= aEnd)
4222 0 : return;
4223 :
4224 0 : aProvider->GetSpacing(aStart, aEnd - aStart, aSpacing);
4225 :
4226 : #ifdef DEBUG
4227 : // Check to see if we have spacing inside ligatures
4228 :
4229 0 : const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
4230 : PRUint32 i;
4231 :
4232 0 : for (i = aStart; i < aEnd; ++i) {
4233 0 : if (!charGlyphs[i].IsLigatureGroupStart()) {
4234 0 : NS_ASSERTION(i == aStart || aSpacing[i - aStart].mBefore == 0,
4235 : "Before-spacing inside a ligature!");
4236 0 : NS_ASSERTION(i - 1 <= aStart || aSpacing[i - 1 - aStart].mAfter == 0,
4237 : "After-spacing inside a ligature!");
4238 : }
4239 : }
4240 : #endif
4241 : }
4242 :
4243 : bool
4244 0 : gfxTextRun::GetAdjustedSpacingArray(PRUint32 aStart, PRUint32 aEnd,
4245 : PropertyProvider *aProvider,
4246 : PRUint32 aSpacingStart, PRUint32 aSpacingEnd,
4247 : nsTArray<PropertyProvider::Spacing> *aSpacing)
4248 : {
4249 0 : if (!aProvider || !(mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING))
4250 0 : return false;
4251 0 : if (!aSpacing->AppendElements(aEnd - aStart))
4252 0 : return false;
4253 0 : memset(aSpacing->Elements(), 0, sizeof(gfxFont::Spacing)*(aSpacingStart - aStart));
4254 : GetAdjustedSpacing(this, aSpacingStart, aSpacingEnd, aProvider,
4255 0 : aSpacing->Elements() + aSpacingStart - aStart);
4256 0 : memset(aSpacing->Elements() + aSpacingEnd - aStart, 0, sizeof(gfxFont::Spacing)*(aEnd - aSpacingEnd));
4257 0 : return true;
4258 : }
4259 :
4260 : void
4261 0 : gfxTextRun::ShrinkToLigatureBoundaries(PRUint32 *aStart, PRUint32 *aEnd)
4262 : {
4263 0 : if (*aStart >= *aEnd)
4264 0 : return;
4265 :
4266 0 : CompressedGlyph *charGlyphs = mCharacterGlyphs;
4267 :
4268 0 : while (*aStart < *aEnd && !charGlyphs[*aStart].IsLigatureGroupStart()) {
4269 0 : ++(*aStart);
4270 : }
4271 0 : if (*aEnd < mCharacterCount) {
4272 0 : while (*aEnd > *aStart && !charGlyphs[*aEnd].IsLigatureGroupStart()) {
4273 0 : --(*aEnd);
4274 : }
4275 : }
4276 : }
4277 :
4278 : void
4279 0 : gfxTextRun::DrawGlyphs(gfxFont *aFont, gfxContext *aContext,
4280 : gfxFont::DrawMode aDrawMode, gfxPoint *aPt,
4281 : gfxPattern *aStrokePattern,
4282 : PRUint32 aStart, PRUint32 aEnd,
4283 : PropertyProvider *aProvider,
4284 : PRUint32 aSpacingStart, PRUint32 aSpacingEnd)
4285 : {
4286 0 : nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
4287 : bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
4288 0 : aSpacingStart, aSpacingEnd, &spacingBuffer);
4289 : aFont->Draw(this, aStart, aEnd, aContext, aDrawMode, aPt,
4290 0 : haveSpacing ? spacingBuffer.Elements() : nsnull, aStrokePattern);
4291 0 : }
4292 :
4293 : static void
4294 0 : ClipPartialLigature(gfxTextRun *aTextRun, gfxFloat *aLeft, gfxFloat *aRight,
4295 : gfxFloat aXOrigin, gfxTextRun::LigatureData *aLigature)
4296 : {
4297 0 : if (aLigature->mClipBeforePart) {
4298 0 : if (aTextRun->IsRightToLeft()) {
4299 0 : *aRight = NS_MIN(*aRight, aXOrigin);
4300 : } else {
4301 0 : *aLeft = NS_MAX(*aLeft, aXOrigin);
4302 : }
4303 : }
4304 0 : if (aLigature->mClipAfterPart) {
4305 0 : gfxFloat endEdge = aXOrigin + aTextRun->GetDirection()*aLigature->mPartWidth;
4306 0 : if (aTextRun->IsRightToLeft()) {
4307 0 : *aLeft = NS_MAX(*aLeft, endEdge);
4308 : } else {
4309 0 : *aRight = NS_MIN(*aRight, endEdge);
4310 : }
4311 : }
4312 0 : }
4313 :
4314 : void
4315 0 : gfxTextRun::DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx,
4316 : PRUint32 aStart, PRUint32 aEnd,
4317 : gfxPoint *aPt,
4318 : PropertyProvider *aProvider)
4319 : {
4320 0 : if (aStart >= aEnd)
4321 0 : return;
4322 :
4323 : // Need to preserve the path, otherwise this can break canvas text-on-path;
4324 : // in general it seems like a good thing, as naive callers probably won't
4325 : // expect gfxTextRun::Draw to implicitly destroy the current path.
4326 0 : gfxContextPathAutoSaveRestore savePath(aCtx);
4327 :
4328 : // Draw partial ligature. We hack this by clipping the ligature.
4329 0 : LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider);
4330 0 : gfxRect clipExtents = aCtx->GetClipExtents();
4331 0 : gfxFloat left = clipExtents.X()*mAppUnitsPerDevUnit;
4332 0 : gfxFloat right = clipExtents.XMost()*mAppUnitsPerDevUnit;
4333 0 : ClipPartialLigature(this, &left, &right, aPt->x, &data);
4334 :
4335 0 : aCtx->Save();
4336 0 : aCtx->NewPath();
4337 : // use division here to ensure that when the rect is aligned on multiples
4338 : // of mAppUnitsPerDevUnit, we clip to true device unit boundaries.
4339 : // Also, make sure we snap the rectangle to device pixels.
4340 : aCtx->Rectangle(gfxRect(left/mAppUnitsPerDevUnit,
4341 : clipExtents.Y(),
4342 : (right - left)/mAppUnitsPerDevUnit,
4343 0 : clipExtents.Height()), true);
4344 0 : aCtx->Clip();
4345 0 : gfxFloat direction = GetDirection();
4346 0 : gfxPoint pt(aPt->x - direction*data.mPartAdvance, aPt->y);
4347 : DrawGlyphs(aFont, aCtx, gfxFont::GLYPH_FILL, &pt, nsnull, data.mLigatureStart,
4348 0 : data.mLigatureEnd, aProvider, aStart, aEnd);
4349 0 : aCtx->Restore();
4350 :
4351 0 : aPt->x += direction*data.mPartWidth;
4352 : }
4353 :
4354 : // returns true if a glyph run is using a font with synthetic bolding enabled, false otherwise
4355 : static bool
4356 0 : HasSyntheticBold(gfxTextRun *aRun, PRUint32 aStart, PRUint32 aLength)
4357 : {
4358 0 : gfxTextRun::GlyphRunIterator iter(aRun, aStart, aLength);
4359 0 : while (iter.NextRun()) {
4360 0 : gfxFont *font = iter.GetGlyphRun()->mFont;
4361 0 : if (font && font->IsSyntheticBold()) {
4362 0 : return true;
4363 : }
4364 : }
4365 :
4366 0 : return false;
4367 : }
4368 :
4369 : // returns true if color is non-opaque (i.e. alpha != 1.0) or completely transparent, false otherwise
4370 : // if true, color is set on output
4371 : static bool
4372 0 : HasNonOpaqueColor(gfxContext *aContext, gfxRGBA& aCurrentColor)
4373 : {
4374 0 : if (aContext->GetDeviceColor(aCurrentColor)) {
4375 0 : if (aCurrentColor.a < 1.0 && aCurrentColor.a > 0.0) {
4376 0 : return true;
4377 : }
4378 : }
4379 :
4380 0 : return false;
4381 : }
4382 :
4383 : // helper class for double-buffering drawing with non-opaque color
4384 : struct BufferAlphaColor {
4385 0 : BufferAlphaColor(gfxContext *aContext)
4386 0 : : mContext(aContext)
4387 : {
4388 :
4389 0 : }
4390 :
4391 0 : ~BufferAlphaColor() {}
4392 :
4393 0 : void PushSolidColor(const gfxRect& aBounds, const gfxRGBA& aAlphaColor, PRUint32 appsPerDevUnit)
4394 : {
4395 0 : mContext->Save();
4396 0 : mContext->NewPath();
4397 0 : mContext->Rectangle(gfxRect(aBounds.X() / appsPerDevUnit,
4398 0 : aBounds.Y() / appsPerDevUnit,
4399 0 : aBounds.Width() / appsPerDevUnit,
4400 0 : aBounds.Height() / appsPerDevUnit), true);
4401 0 : mContext->Clip();
4402 0 : mContext->SetColor(gfxRGBA(aAlphaColor.r, aAlphaColor.g, aAlphaColor.b));
4403 0 : mContext->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
4404 0 : mAlpha = aAlphaColor.a;
4405 0 : }
4406 :
4407 0 : void PopAlpha()
4408 : {
4409 : // pop the text, using the color alpha as the opacity
4410 0 : mContext->PopGroupToSource();
4411 0 : mContext->SetOperator(gfxContext::OPERATOR_OVER);
4412 0 : mContext->Paint(mAlpha);
4413 0 : mContext->Restore();
4414 0 : }
4415 :
4416 : gfxContext *mContext;
4417 : gfxFloat mAlpha;
4418 : };
4419 :
4420 : void
4421 0 : gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, gfxFont::DrawMode aDrawMode,
4422 : PRUint32 aStart, PRUint32 aLength,
4423 : PropertyProvider *aProvider, gfxFloat *aAdvanceWidth,
4424 : gfxPattern *aStrokePattern)
4425 : {
4426 0 : NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
4427 0 : NS_ASSERTION(aDrawMode <= gfxFont::GLYPH_PATH, "GLYPH_PATH cannot be used with GLYPH_FILL or GLYPH_STROKE");
4428 :
4429 0 : gfxFloat direction = GetDirection();
4430 :
4431 0 : if (mSkipDrawing) {
4432 : // We're waiting for a user font to finish downloading;
4433 : // but if the caller wants advance width, we need to compute it here
4434 0 : if (aAdvanceWidth) {
4435 : gfxTextRun::Metrics metrics = MeasureText(aStart, aLength,
4436 : gfxFont::LOOSE_INK_EXTENTS,
4437 0 : aContext, aProvider);
4438 0 : *aAdvanceWidth = metrics.mAdvanceWidth * direction;
4439 : }
4440 :
4441 : // return without drawing
4442 0 : return;
4443 : }
4444 :
4445 0 : gfxPoint pt = aPt;
4446 :
4447 : // synthetic bolding draws glyphs twice ==> colors with opacity won't draw correctly unless first drawn without alpha
4448 0 : BufferAlphaColor syntheticBoldBuffer(aContext);
4449 0 : gfxRGBA currentColor;
4450 0 : bool needToRestore = false;
4451 :
4452 0 : if (aDrawMode == gfxFont::GLYPH_FILL && HasNonOpaqueColor(aContext, currentColor)
4453 0 : && HasSyntheticBold(this, aStart, aLength)) {
4454 0 : needToRestore = true;
4455 : // measure text, use the bounding box
4456 : gfxTextRun::Metrics metrics = MeasureText(aStart, aLength, gfxFont::LOOSE_INK_EXTENTS,
4457 0 : aContext, aProvider);
4458 0 : metrics.mBoundingBox.MoveBy(aPt);
4459 0 : syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor, GetAppUnitsPerDevUnit());
4460 : }
4461 :
4462 0 : GlyphRunIterator iter(this, aStart, aLength);
4463 0 : while (iter.NextRun()) {
4464 0 : gfxFont *font = iter.GetGlyphRun()->mFont;
4465 0 : PRUint32 start = iter.GetStringStart();
4466 0 : PRUint32 end = iter.GetStringEnd();
4467 0 : PRUint32 ligatureRunStart = start;
4468 0 : PRUint32 ligatureRunEnd = end;
4469 0 : ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
4470 :
4471 0 : if (aDrawMode == gfxFont::GLYPH_FILL) {
4472 0 : DrawPartialLigature(font, aContext, start, ligatureRunStart, &pt, aProvider);
4473 : }
4474 :
4475 : DrawGlyphs(font, aContext, aDrawMode, &pt, aStrokePattern, ligatureRunStart,
4476 0 : ligatureRunEnd, aProvider, ligatureRunStart, ligatureRunEnd);
4477 :
4478 0 : if (aDrawMode == gfxFont::GLYPH_FILL) {
4479 0 : DrawPartialLigature(font, aContext, ligatureRunEnd, end, &pt, aProvider);
4480 : }
4481 : }
4482 :
4483 : // composite result when synthetic bolding used
4484 0 : if (needToRestore) {
4485 0 : syntheticBoldBuffer.PopAlpha();
4486 : }
4487 :
4488 0 : if (aAdvanceWidth) {
4489 0 : *aAdvanceWidth = (pt.x - aPt.x)*direction;
4490 : }
4491 : }
4492 :
4493 : void
4494 0 : gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont,
4495 : PRUint32 aStart, PRUint32 aEnd,
4496 : gfxFont::BoundingBoxType aBoundingBoxType,
4497 : gfxContext *aRefContext,
4498 : PropertyProvider *aProvider,
4499 : PRUint32 aSpacingStart, PRUint32 aSpacingEnd,
4500 : Metrics *aMetrics)
4501 : {
4502 0 : nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
4503 : bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
4504 0 : aSpacingStart, aSpacingEnd, &spacingBuffer);
4505 : Metrics metrics = aFont->Measure(this, aStart, aEnd, aBoundingBoxType, aRefContext,
4506 0 : haveSpacing ? spacingBuffer.Elements() : nsnull);
4507 0 : aMetrics->CombineWith(metrics, IsRightToLeft());
4508 0 : }
4509 :
4510 : void
4511 0 : gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont,
4512 : PRUint32 aStart, PRUint32 aEnd,
4513 : gfxFont::BoundingBoxType aBoundingBoxType, gfxContext *aRefContext,
4514 : PropertyProvider *aProvider, Metrics *aMetrics)
4515 : {
4516 0 : if (aStart >= aEnd)
4517 0 : return;
4518 :
4519 : // Measure partial ligature. We hack this by clipping the metrics in the
4520 : // same way we clip the drawing.
4521 0 : LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider);
4522 :
4523 : // First measure the complete ligature
4524 0 : Metrics metrics;
4525 : AccumulateMetricsForRun(aFont, data.mLigatureStart, data.mLigatureEnd,
4526 : aBoundingBoxType, aRefContext,
4527 0 : aProvider, aStart, aEnd, &metrics);
4528 :
4529 : // Clip the bounding box to the ligature part
4530 0 : gfxFloat bboxLeft = metrics.mBoundingBox.X();
4531 0 : gfxFloat bboxRight = metrics.mBoundingBox.XMost();
4532 : // Where we are going to start "drawing" relative to our left baseline origin
4533 0 : gfxFloat origin = IsRightToLeft() ? metrics.mAdvanceWidth - data.mPartAdvance : 0;
4534 0 : ClipPartialLigature(this, &bboxLeft, &bboxRight, origin, &data);
4535 0 : metrics.mBoundingBox.x = bboxLeft;
4536 0 : metrics.mBoundingBox.width = bboxRight - bboxLeft;
4537 :
4538 : // mBoundingBox is now relative to the left baseline origin for the entire
4539 : // ligature. Shift it left.
4540 : metrics.mBoundingBox.x -=
4541 0 : IsRightToLeft() ? metrics.mAdvanceWidth - (data.mPartAdvance + data.mPartWidth)
4542 0 : : data.mPartAdvance;
4543 0 : metrics.mAdvanceWidth = data.mPartWidth;
4544 :
4545 0 : aMetrics->CombineWith(metrics, IsRightToLeft());
4546 : }
4547 :
4548 : gfxTextRun::Metrics
4549 0 : gfxTextRun::MeasureText(PRUint32 aStart, PRUint32 aLength,
4550 : gfxFont::BoundingBoxType aBoundingBoxType,
4551 : gfxContext *aRefContext,
4552 : PropertyProvider *aProvider)
4553 : {
4554 0 : NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
4555 :
4556 0 : Metrics accumulatedMetrics;
4557 0 : GlyphRunIterator iter(this, aStart, aLength);
4558 0 : while (iter.NextRun()) {
4559 0 : gfxFont *font = iter.GetGlyphRun()->mFont;
4560 0 : PRUint32 start = iter.GetStringStart();
4561 0 : PRUint32 end = iter.GetStringEnd();
4562 0 : PRUint32 ligatureRunStart = start;
4563 0 : PRUint32 ligatureRunEnd = end;
4564 0 : ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
4565 :
4566 : AccumulatePartialLigatureMetrics(font, start, ligatureRunStart,
4567 0 : aBoundingBoxType, aRefContext, aProvider, &accumulatedMetrics);
4568 :
4569 : // XXX This sucks. We have to get glyph extents just so we can detect
4570 : // glyphs outside the font box, even when aBoundingBoxType is LOOSE,
4571 : // even though in almost all cases we could get correct results just
4572 : // by getting some ascent/descent from the font and using our stored
4573 : // advance widths.
4574 : AccumulateMetricsForRun(font,
4575 : ligatureRunStart, ligatureRunEnd, aBoundingBoxType,
4576 : aRefContext, aProvider, ligatureRunStart, ligatureRunEnd,
4577 0 : &accumulatedMetrics);
4578 :
4579 : AccumulatePartialLigatureMetrics(font, ligatureRunEnd, end,
4580 0 : aBoundingBoxType, aRefContext, aProvider, &accumulatedMetrics);
4581 : }
4582 :
4583 : return accumulatedMetrics;
4584 : }
4585 :
4586 : #define MEASUREMENT_BUFFER_SIZE 100
4587 :
4588 : PRUint32
4589 0 : gfxTextRun::BreakAndMeasureText(PRUint32 aStart, PRUint32 aMaxLength,
4590 : bool aLineBreakBefore, gfxFloat aWidth,
4591 : PropertyProvider *aProvider,
4592 : bool aSuppressInitialBreak,
4593 : gfxFloat *aTrimWhitespace,
4594 : Metrics *aMetrics,
4595 : gfxFont::BoundingBoxType aBoundingBoxType,
4596 : gfxContext *aRefContext,
4597 : bool *aUsedHyphenation,
4598 : PRUint32 *aLastBreak,
4599 : bool aCanWordWrap,
4600 : gfxBreakPriority *aBreakPriority)
4601 : {
4602 0 : aMaxLength = NS_MIN(aMaxLength, mCharacterCount - aStart);
4603 :
4604 0 : NS_ASSERTION(aStart + aMaxLength <= mCharacterCount, "Substring out of range");
4605 :
4606 0 : PRUint32 bufferStart = aStart;
4607 0 : PRUint32 bufferLength = NS_MIN<PRUint32>(aMaxLength, MEASUREMENT_BUFFER_SIZE);
4608 : PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE];
4609 0 : bool haveSpacing = aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING) != 0;
4610 0 : if (haveSpacing) {
4611 : GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
4612 0 : spacingBuffer);
4613 : }
4614 : bool hyphenBuffer[MEASUREMENT_BUFFER_SIZE];
4615 : bool haveHyphenation = aProvider &&
4616 0 : (aProvider->GetHyphensOption() == NS_STYLE_HYPHENS_AUTO ||
4617 0 : (aProvider->GetHyphensOption() == NS_STYLE_HYPHENS_MANUAL &&
4618 0 : (mFlags & gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS) != 0));
4619 0 : if (haveHyphenation) {
4620 : aProvider->GetHyphenationBreaks(bufferStart, bufferLength,
4621 0 : hyphenBuffer);
4622 : }
4623 :
4624 0 : gfxFloat width = 0;
4625 0 : gfxFloat advance = 0;
4626 : // The number of space characters that can be trimmed
4627 0 : PRUint32 trimmableChars = 0;
4628 : // The amount of space removed by ignoring trimmableChars
4629 0 : gfxFloat trimmableAdvance = 0;
4630 0 : PRInt32 lastBreak = -1;
4631 0 : PRInt32 lastBreakTrimmableChars = -1;
4632 0 : gfxFloat lastBreakTrimmableAdvance = -1;
4633 0 : bool aborted = false;
4634 0 : PRUint32 end = aStart + aMaxLength;
4635 0 : bool lastBreakUsedHyphenation = false;
4636 :
4637 0 : PRUint32 ligatureRunStart = aStart;
4638 0 : PRUint32 ligatureRunEnd = end;
4639 0 : ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
4640 :
4641 : PRUint32 i;
4642 0 : for (i = aStart; i < end; ++i) {
4643 0 : if (i >= bufferStart + bufferLength) {
4644 : // Fetch more spacing and hyphenation data
4645 0 : bufferStart = i;
4646 0 : bufferLength = NS_MIN(aStart + aMaxLength, i + MEASUREMENT_BUFFER_SIZE) - i;
4647 0 : if (haveSpacing) {
4648 : GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
4649 0 : spacingBuffer);
4650 : }
4651 0 : if (haveHyphenation) {
4652 : aProvider->GetHyphenationBreaks(bufferStart, bufferLength,
4653 0 : hyphenBuffer);
4654 : }
4655 : }
4656 :
4657 : // There can't be a word-wrap break opportunity at the beginning of the
4658 : // line: if the width is too small for even one character to fit, it
4659 : // could be the first and last break opportunity on the line, and that
4660 : // would trigger an infinite loop.
4661 0 : if (!aSuppressInitialBreak || i > aStart) {
4662 0 : bool lineBreakHere = mCharacterGlyphs[i].CanBreakBefore() == 1;
4663 0 : bool hyphenation = haveHyphenation && hyphenBuffer[i - bufferStart];
4664 0 : bool wordWrapping = aCanWordWrap && *aBreakPriority <= eWordWrapBreak;
4665 :
4666 0 : if (lineBreakHere || hyphenation || wordWrapping) {
4667 0 : gfxFloat hyphenatedAdvance = advance;
4668 0 : if (!lineBreakHere && !wordWrapping) {
4669 0 : hyphenatedAdvance += aProvider->GetHyphenWidth();
4670 : }
4671 :
4672 0 : if (lastBreak < 0 || width + hyphenatedAdvance - trimmableAdvance <= aWidth) {
4673 : // We can break here.
4674 0 : lastBreak = i;
4675 0 : lastBreakTrimmableChars = trimmableChars;
4676 0 : lastBreakTrimmableAdvance = trimmableAdvance;
4677 0 : lastBreakUsedHyphenation = !lineBreakHere && !wordWrapping;
4678 : *aBreakPriority = hyphenation || lineBreakHere ?
4679 0 : eNormalBreak : eWordWrapBreak;
4680 : }
4681 :
4682 0 : width += advance;
4683 0 : advance = 0;
4684 0 : if (width - trimmableAdvance > aWidth) {
4685 : // No more text fits. Abort
4686 0 : aborted = true;
4687 0 : break;
4688 : }
4689 : }
4690 : }
4691 :
4692 : gfxFloat charAdvance;
4693 0 : if (i >= ligatureRunStart && i < ligatureRunEnd) {
4694 0 : charAdvance = GetAdvanceForGlyphs(i, i + 1);
4695 0 : if (haveSpacing) {
4696 0 : PropertyProvider::Spacing *space = &spacingBuffer[i - bufferStart];
4697 0 : charAdvance += space->mBefore + space->mAfter;
4698 0 : }
4699 : } else {
4700 0 : charAdvance = ComputePartialLigatureWidth(i, i + 1, aProvider);
4701 : }
4702 :
4703 0 : advance += charAdvance;
4704 0 : if (aTrimWhitespace) {
4705 0 : if (mCharacterGlyphs[i].CharIsSpace()) {
4706 0 : ++trimmableChars;
4707 0 : trimmableAdvance += charAdvance;
4708 : } else {
4709 0 : trimmableAdvance = 0;
4710 0 : trimmableChars = 0;
4711 : }
4712 : }
4713 : }
4714 :
4715 0 : if (!aborted) {
4716 0 : width += advance;
4717 : }
4718 :
4719 : // There are three possibilities:
4720 : // 1) all the text fit (width <= aWidth)
4721 : // 2) some of the text fit up to a break opportunity (width > aWidth && lastBreak >= 0)
4722 : // 3) none of the text fits before a break opportunity (width > aWidth && lastBreak < 0)
4723 : PRUint32 charsFit;
4724 0 : bool usedHyphenation = false;
4725 0 : if (width - trimmableAdvance <= aWidth) {
4726 0 : charsFit = aMaxLength;
4727 0 : } else if (lastBreak >= 0) {
4728 0 : charsFit = lastBreak - aStart;
4729 0 : trimmableChars = lastBreakTrimmableChars;
4730 0 : trimmableAdvance = lastBreakTrimmableAdvance;
4731 0 : usedHyphenation = lastBreakUsedHyphenation;
4732 : } else {
4733 0 : charsFit = aMaxLength;
4734 : }
4735 :
4736 0 : if (aMetrics) {
4737 : *aMetrics = MeasureText(aStart, charsFit - trimmableChars,
4738 0 : aBoundingBoxType, aRefContext, aProvider);
4739 : }
4740 0 : if (aTrimWhitespace) {
4741 0 : *aTrimWhitespace = trimmableAdvance;
4742 : }
4743 0 : if (aUsedHyphenation) {
4744 0 : *aUsedHyphenation = usedHyphenation;
4745 : }
4746 0 : if (aLastBreak && charsFit == aMaxLength) {
4747 0 : if (lastBreak < 0) {
4748 0 : *aLastBreak = PR_UINT32_MAX;
4749 : } else {
4750 0 : *aLastBreak = lastBreak - aStart;
4751 : }
4752 : }
4753 :
4754 0 : return charsFit;
4755 : }
4756 :
4757 : gfxFloat
4758 0 : gfxTextRun::GetAdvanceWidth(PRUint32 aStart, PRUint32 aLength,
4759 : PropertyProvider *aProvider)
4760 : {
4761 0 : NS_ASSERTION(aStart + aLength <= mCharacterCount, "Substring out of range");
4762 :
4763 0 : PRUint32 ligatureRunStart = aStart;
4764 0 : PRUint32 ligatureRunEnd = aStart + aLength;
4765 0 : ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
4766 :
4767 0 : gfxFloat result = ComputePartialLigatureWidth(aStart, ligatureRunStart, aProvider) +
4768 0 : ComputePartialLigatureWidth(ligatureRunEnd, aStart + aLength, aProvider);
4769 :
4770 : // Account for all remaining spacing here. This is more efficient than
4771 : // processing it along with the glyphs.
4772 0 : if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) {
4773 : PRUint32 i;
4774 0 : nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
4775 0 : if (spacingBuffer.AppendElements(aLength)) {
4776 : GetAdjustedSpacing(this, ligatureRunStart, ligatureRunEnd, aProvider,
4777 0 : spacingBuffer.Elements());
4778 0 : for (i = 0; i < ligatureRunEnd - ligatureRunStart; ++i) {
4779 0 : PropertyProvider::Spacing *space = &spacingBuffer[i];
4780 0 : result += space->mBefore + space->mAfter;
4781 : }
4782 : }
4783 : }
4784 :
4785 0 : return result + GetAdvanceForGlyphs(ligatureRunStart, ligatureRunEnd);
4786 : }
4787 :
4788 : bool
4789 0 : gfxTextRun::SetLineBreaks(PRUint32 aStart, PRUint32 aLength,
4790 : bool aLineBreakBefore, bool aLineBreakAfter,
4791 : gfxFloat *aAdvanceWidthDelta,
4792 : gfxContext *aRefContext)
4793 : {
4794 : // Do nothing because our shaping does not currently take linebreaks into
4795 : // account. There is no change in advance width.
4796 0 : if (aAdvanceWidthDelta) {
4797 0 : *aAdvanceWidthDelta = 0;
4798 : }
4799 0 : return false;
4800 : }
4801 :
4802 : PRUint32
4803 0 : gfxTextRun::FindFirstGlyphRunContaining(PRUint32 aOffset)
4804 : {
4805 0 : NS_ASSERTION(aOffset <= mCharacterCount, "Bad offset looking for glyphrun");
4806 0 : NS_ASSERTION(mCharacterCount == 0 || mGlyphRuns.Length() > 0,
4807 : "non-empty text but no glyph runs present!");
4808 0 : if (aOffset == mCharacterCount)
4809 0 : return mGlyphRuns.Length();
4810 0 : PRUint32 start = 0;
4811 0 : PRUint32 end = mGlyphRuns.Length();
4812 0 : while (end - start > 1) {
4813 0 : PRUint32 mid = (start + end)/2;
4814 0 : if (mGlyphRuns[mid].mCharacterOffset <= aOffset) {
4815 0 : start = mid;
4816 : } else {
4817 0 : end = mid;
4818 : }
4819 : }
4820 0 : NS_ASSERTION(mGlyphRuns[start].mCharacterOffset <= aOffset,
4821 : "Hmm, something went wrong, aOffset should have been found");
4822 0 : return start;
4823 : }
4824 :
4825 : nsresult
4826 0 : gfxTextRun::AddGlyphRun(gfxFont *aFont, PRUint8 aMatchType,
4827 : PRUint32 aUTF16Offset, bool aForceNewRun)
4828 : {
4829 0 : NS_ASSERTION(aFont, "adding glyph run for null font!");
4830 0 : if (!aFont) {
4831 0 : return NS_OK;
4832 : }
4833 0 : PRUint32 numGlyphRuns = mGlyphRuns.Length();
4834 0 : if (!aForceNewRun && numGlyphRuns > 0) {
4835 0 : GlyphRun *lastGlyphRun = &mGlyphRuns[numGlyphRuns - 1];
4836 :
4837 0 : NS_ASSERTION(lastGlyphRun->mCharacterOffset <= aUTF16Offset,
4838 : "Glyph runs out of order (and run not forced)");
4839 :
4840 : // Don't append a run if the font is already the one we want
4841 0 : if (lastGlyphRun->mFont == aFont &&
4842 : lastGlyphRun->mMatchType == aMatchType)
4843 : {
4844 0 : return NS_OK;
4845 : }
4846 :
4847 : // If the offset has not changed, avoid leaving a zero-length run
4848 : // by overwriting the last entry instead of appending...
4849 0 : if (lastGlyphRun->mCharacterOffset == aUTF16Offset) {
4850 :
4851 : // ...except that if the run before the last entry had the same
4852 : // font as the new one wants, merge with it instead of creating
4853 : // adjacent runs with the same font
4854 0 : if (numGlyphRuns > 1 &&
4855 0 : mGlyphRuns[numGlyphRuns - 2].mFont == aFont &&
4856 0 : mGlyphRuns[numGlyphRuns - 2].mMatchType == aMatchType)
4857 : {
4858 0 : mGlyphRuns.TruncateLength(numGlyphRuns - 1);
4859 0 : return NS_OK;
4860 : }
4861 :
4862 0 : lastGlyphRun->mFont = aFont;
4863 0 : lastGlyphRun->mMatchType = aMatchType;
4864 0 : return NS_OK;
4865 : }
4866 : }
4867 :
4868 0 : NS_ASSERTION(aForceNewRun || numGlyphRuns > 0 || aUTF16Offset == 0,
4869 : "First run doesn't cover the first character (and run not forced)?");
4870 :
4871 0 : GlyphRun *glyphRun = mGlyphRuns.AppendElement();
4872 0 : if (!glyphRun)
4873 0 : return NS_ERROR_OUT_OF_MEMORY;
4874 0 : glyphRun->mFont = aFont;
4875 0 : glyphRun->mCharacterOffset = aUTF16Offset;
4876 0 : glyphRun->mMatchType = aMatchType;
4877 0 : return NS_OK;
4878 : }
4879 :
4880 : void
4881 0 : gfxTextRun::SortGlyphRuns()
4882 : {
4883 0 : if (mGlyphRuns.Length() <= 1)
4884 0 : return;
4885 :
4886 0 : nsTArray<GlyphRun> runs(mGlyphRuns);
4887 : GlyphRunOffsetComparator comp;
4888 0 : runs.Sort(comp);
4889 :
4890 : // Now copy back, coalescing adjacent glyph runs that have the same font
4891 0 : mGlyphRuns.Clear();
4892 : PRUint32 i;
4893 0 : for (i = 0; i < runs.Length(); ++i) {
4894 : // a GlyphRun with the same font as the previous GlyphRun can just
4895 : // be skipped; the last GlyphRun will cover its character range.
4896 0 : if (i == 0 || runs[i].mFont != runs[i - 1].mFont) {
4897 0 : mGlyphRuns.AppendElement(runs[i]);
4898 : // If two fonts have the same character offset, Sort() will have
4899 : // randomized the order.
4900 0 : NS_ASSERTION(i == 0 ||
4901 : runs[i].mCharacterOffset !=
4902 : runs[i - 1].mCharacterOffset,
4903 : "Two fonts for the same run, glyph indices may not match the font");
4904 : }
4905 : }
4906 : }
4907 :
4908 : // Note that SanitizeGlyphRuns scans all glyph runs in the textrun;
4909 : // therefore we only call it once, at the end of textrun construction,
4910 : // NOT incrementally as each glyph run is added (bug 680402).
4911 : void
4912 0 : gfxTextRun::SanitizeGlyphRuns()
4913 : {
4914 0 : if (mGlyphRuns.Length() <= 1)
4915 0 : return;
4916 :
4917 : // If any glyph run starts with ligature-continuation characters, we need to advance it
4918 : // to the first "real" character to avoid drawing partial ligature glyphs from wrong font
4919 : // (seen with U+FEFF in reftest 474417-1, as Core Text eliminates the glyph, which makes
4920 : // it appear as if a ligature has been formed)
4921 0 : PRInt32 i, lastRunIndex = mGlyphRuns.Length() - 1;
4922 0 : const CompressedGlyph *charGlyphs = mCharacterGlyphs;
4923 0 : for (i = lastRunIndex; i >= 0; --i) {
4924 0 : GlyphRun& run = mGlyphRuns[i];
4925 0 : while (charGlyphs[run.mCharacterOffset].IsLigatureContinuation() &&
4926 : run.mCharacterOffset < mCharacterCount) {
4927 0 : run.mCharacterOffset++;
4928 : }
4929 : // if the run has become empty, eliminate it
4930 0 : if ((i < lastRunIndex &&
4931 0 : run.mCharacterOffset >= mGlyphRuns[i+1].mCharacterOffset) ||
4932 : (i == lastRunIndex && run.mCharacterOffset == mCharacterCount)) {
4933 0 : mGlyphRuns.RemoveElementAt(i);
4934 0 : --lastRunIndex;
4935 : }
4936 : }
4937 : }
4938 :
4939 : PRUint32
4940 0 : gfxTextRun::CountMissingGlyphs()
4941 : {
4942 : PRUint32 i;
4943 0 : PRUint32 count = 0;
4944 0 : for (i = 0; i < mCharacterCount; ++i) {
4945 0 : if (mCharacterGlyphs[i].IsMissing()) {
4946 0 : ++count;
4947 : }
4948 : }
4949 0 : return count;
4950 : }
4951 :
4952 : gfxTextRun::DetailedGlyph *
4953 0 : gfxTextRun::AllocateDetailedGlyphs(PRUint32 aIndex, PRUint32 aCount)
4954 : {
4955 0 : NS_ASSERTION(aIndex < mCharacterCount, "Index out of range");
4956 :
4957 0 : if (!mDetailedGlyphs) {
4958 0 : mDetailedGlyphs = new DetailedGlyphStore();
4959 : }
4960 :
4961 0 : DetailedGlyph *details = mDetailedGlyphs->Allocate(aIndex, aCount);
4962 0 : if (!details) {
4963 0 : mCharacterGlyphs[aIndex].SetMissing(0);
4964 0 : return nsnull;
4965 : }
4966 :
4967 0 : return details;
4968 : }
4969 :
4970 : void
4971 0 : gfxTextRun::SetGlyphs(PRUint32 aIndex, CompressedGlyph aGlyph,
4972 : const DetailedGlyph *aGlyphs)
4973 : {
4974 0 : NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
4975 0 : NS_ASSERTION(aIndex > 0 || aGlyph.IsLigatureGroupStart(),
4976 : "First character can't be a ligature continuation!");
4977 :
4978 0 : PRUint32 glyphCount = aGlyph.GetGlyphCount();
4979 0 : if (glyphCount > 0) {
4980 0 : DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
4981 0 : if (!details)
4982 0 : return;
4983 0 : memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
4984 : }
4985 0 : mCharacterGlyphs[aIndex] = aGlyph;
4986 : }
4987 :
4988 : void
4989 0 : gfxTextRun::SetMissingGlyph(PRUint32 aIndex, PRUint32 aChar)
4990 : {
4991 0 : PRUint8 category = GetGeneralCategory(aChar);
4992 0 : if (category >= HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK &&
4993 : category <= HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
4994 : {
4995 0 : mCharacterGlyphs[aIndex].SetComplex(false, true, 0);
4996 : }
4997 :
4998 0 : DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
4999 0 : if (!details)
5000 0 : return;
5001 :
5002 0 : details->mGlyphID = aChar;
5003 0 : GlyphRun *glyphRun = &mGlyphRuns[FindFirstGlyphRunContaining(aIndex)];
5004 0 : if (IsDefaultIgnorable(aChar)) {
5005 : // Setting advance width to zero will prevent drawing the hexbox
5006 0 : details->mAdvance = 0;
5007 : } else {
5008 0 : gfxFloat width = NS_MAX(glyphRun->mFont->GetMetrics().aveCharWidth,
5009 0 : gfxFontMissingGlyphs::GetDesiredMinWidth(aChar));
5010 0 : details->mAdvance = PRUint32(width*GetAppUnitsPerDevUnit());
5011 : }
5012 0 : details->mXOffset = 0;
5013 0 : details->mYOffset = 0;
5014 0 : mCharacterGlyphs[aIndex].SetMissing(1);
5015 : }
5016 :
5017 : void
5018 0 : gfxTextRun::CopyGlyphDataFrom(const gfxShapedWord *aShapedWord, PRUint32 aOffset)
5019 : {
5020 0 : PRUint32 wordLen = aShapedWord->Length();
5021 0 : NS_ASSERTION(aOffset + wordLen <= GetLength(),
5022 : "word overruns end of textrun!");
5023 :
5024 0 : const CompressedGlyph *wordGlyphs = aShapedWord->GetCharacterGlyphs();
5025 0 : if (aShapedWord->HasDetailedGlyphs()) {
5026 0 : for (PRUint32 i = 0; i < wordLen; ++i, ++aOffset) {
5027 0 : const CompressedGlyph& g = wordGlyphs[i];
5028 0 : if (g.IsSimpleGlyph()) {
5029 0 : SetSimpleGlyph(aOffset, g);
5030 : } else {
5031 : const DetailedGlyph *details =
5032 0 : g.GetGlyphCount() > 0 ?
5033 0 : aShapedWord->GetDetailedGlyphs(i) : nsnull;
5034 0 : SetGlyphs(aOffset, g, details);
5035 : }
5036 : }
5037 : } else {
5038 0 : memcpy(GetCharacterGlyphs() + aOffset, wordGlyphs,
5039 0 : wordLen * sizeof(CompressedGlyph));
5040 : }
5041 0 : }
5042 :
5043 : void
5044 0 : gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, PRUint32 aStart,
5045 : PRUint32 aLength, PRUint32 aDest)
5046 : {
5047 0 : NS_ASSERTION(aStart + aLength <= aSource->GetLength(),
5048 : "Source substring out of range");
5049 0 : NS_ASSERTION(aDest + aLength <= GetLength(),
5050 : "Destination substring out of range");
5051 :
5052 0 : if (aSource->mSkipDrawing) {
5053 0 : mSkipDrawing = true;
5054 : }
5055 :
5056 : // Copy base glyph data, and DetailedGlyph data where present
5057 0 : const CompressedGlyph *srcGlyphs = aSource->mCharacterGlyphs + aStart;
5058 0 : CompressedGlyph *dstGlyphs = mCharacterGlyphs + aDest;
5059 0 : for (PRUint32 i = 0; i < aLength; ++i) {
5060 0 : CompressedGlyph g = srcGlyphs[i];
5061 0 : g.SetCanBreakBefore(dstGlyphs[i].CanBreakBefore());
5062 0 : if (!g.IsSimpleGlyph()) {
5063 0 : PRUint32 count = g.GetGlyphCount();
5064 0 : if (count > 0) {
5065 0 : DetailedGlyph *dst = AllocateDetailedGlyphs(i + aDest, count);
5066 0 : if (dst) {
5067 0 : DetailedGlyph *src = aSource->GetDetailedGlyphs(i + aStart);
5068 0 : if (src) {
5069 0 : ::memcpy(dst, src, count * sizeof(DetailedGlyph));
5070 : } else {
5071 0 : g.SetMissing(0);
5072 : }
5073 : } else {
5074 0 : g.SetMissing(0);
5075 : }
5076 : }
5077 : }
5078 0 : dstGlyphs[i] = g;
5079 : }
5080 :
5081 : // Copy glyph runs
5082 0 : GlyphRunIterator iter(aSource, aStart, aLength);
5083 : #ifdef DEBUG
5084 0 : gfxFont *lastFont = nsnull;
5085 : #endif
5086 0 : while (iter.NextRun()) {
5087 0 : gfxFont *font = iter.GetGlyphRun()->mFont;
5088 0 : NS_ASSERTION(font != lastFont, "Glyphruns not coalesced?");
5089 : #ifdef DEBUG
5090 0 : lastFont = font;
5091 0 : PRUint32 end = iter.GetStringEnd();
5092 : #endif
5093 0 : PRUint32 start = iter.GetStringStart();
5094 :
5095 : // These used to be NS_ASSERTION()s, but WARNING is more appropriate.
5096 : // Although it's unusual (and not desirable), it's possible for us to assign
5097 : // different fonts to a base character and a following diacritic.
5098 : // Example on OSX 10.5/10.6 with default fonts installed:
5099 : // data:text/html,<p style="font-family:helvetica, arial, sans-serif;">
5100 : // &%23x043E;&%23x0486;&%23x20;&%23x043E;&%23x0486;
5101 : // This means the rendering of the cluster will probably not be very good,
5102 : // but it's the best we can do for now if the specified font only covered the
5103 : // initial base character and not its applied marks.
5104 0 : NS_WARN_IF_FALSE(aSource->IsClusterStart(start),
5105 : "Started font run in the middle of a cluster");
5106 0 : NS_WARN_IF_FALSE(end == aSource->GetLength() || aSource->IsClusterStart(end),
5107 : "Ended font run in the middle of a cluster");
5108 :
5109 0 : nsresult rv = AddGlyphRun(font, iter.GetGlyphRun()->mMatchType,
5110 0 : start - aStart + aDest, false);
5111 0 : if (NS_FAILED(rv))
5112 0 : return;
5113 : }
5114 : }
5115 :
5116 : void
5117 0 : gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext,
5118 : PRUint32 aCharIndex)
5119 : {
5120 0 : if (SetSpaceGlyphIfSimple(aFont, aContext, aCharIndex, ' ')) {
5121 0 : return;
5122 : }
5123 :
5124 0 : aFont->InitWordCache();
5125 : static const PRUint8 space = ' ';
5126 : gfxShapedWord *sw = aFont->GetShapedWord(aContext,
5127 : &space, 1,
5128 : HashMix(0, ' '),
5129 : MOZ_SCRIPT_LATIN,
5130 : mAppUnitsPerDevUnit,
5131 : gfxTextRunFactory::TEXT_IS_8BIT |
5132 : gfxTextRunFactory::TEXT_IS_ASCII |
5133 0 : gfxTextRunFactory::TEXT_IS_PERSISTENT);
5134 0 : if (sw) {
5135 0 : AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false);
5136 0 : CopyGlyphDataFrom(sw, aCharIndex);
5137 : }
5138 : }
5139 :
5140 : bool
5141 0 : gfxTextRun::SetSpaceGlyphIfSimple(gfxFont *aFont, gfxContext *aContext,
5142 : PRUint32 aCharIndex, PRUnichar aSpaceChar)
5143 : {
5144 0 : PRUint32 spaceGlyph = aFont->GetSpaceGlyph();
5145 0 : if (!spaceGlyph || !CompressedGlyph::IsSimpleGlyphID(spaceGlyph)) {
5146 0 : return false;
5147 : }
5148 :
5149 : PRUint32 spaceWidthAppUnits =
5150 0 : NS_lroundf(aFont->GetMetrics().spaceWidth * mAppUnitsPerDevUnit);
5151 0 : if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) {
5152 0 : return false;
5153 : }
5154 :
5155 0 : AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false);
5156 0 : CompressedGlyph g;
5157 0 : g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
5158 0 : if (aSpaceChar == ' ') {
5159 0 : g.SetIsSpace();
5160 : }
5161 0 : SetSimpleGlyph(aCharIndex, g);
5162 0 : return true;
5163 : }
5164 :
5165 : void
5166 0 : gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
5167 : {
5168 0 : bool needsGlyphExtents = NeedsGlyphExtents(this);
5169 0 : if (!needsGlyphExtents && !mDetailedGlyphs)
5170 0 : return;
5171 :
5172 : PRUint32 i;
5173 0 : CompressedGlyph *charGlyphs = mCharacterGlyphs;
5174 0 : for (i = 0; i < mGlyphRuns.Length(); ++i) {
5175 0 : gfxFont *font = mGlyphRuns[i].mFont;
5176 0 : PRUint32 start = mGlyphRuns[i].mCharacterOffset;
5177 0 : PRUint32 end = i + 1 < mGlyphRuns.Length()
5178 0 : ? mGlyphRuns[i + 1].mCharacterOffset : GetLength();
5179 0 : bool fontIsSetup = false;
5180 : PRUint32 j;
5181 0 : gfxGlyphExtents *extents = font->GetOrCreateGlyphExtents(mAppUnitsPerDevUnit);
5182 :
5183 0 : for (j = start; j < end; ++j) {
5184 0 : const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[j];
5185 0 : if (glyphData->IsSimpleGlyph()) {
5186 : // If we're in speed mode, don't set up glyph extents here; we'll
5187 : // just return "optimistic" glyph bounds later
5188 0 : if (needsGlyphExtents) {
5189 0 : PRUint32 glyphIndex = glyphData->GetSimpleGlyph();
5190 0 : if (!extents->IsGlyphKnown(glyphIndex)) {
5191 0 : if (!fontIsSetup) {
5192 0 : font->SetupCairoFont(aRefContext);
5193 0 : fontIsSetup = true;
5194 : }
5195 : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
5196 : ++gGlyphExtentsSetupEagerSimple;
5197 : #endif
5198 0 : font->SetupGlyphExtents(aRefContext, glyphIndex, false, extents);
5199 : }
5200 : }
5201 0 : } else if (!glyphData->IsMissing()) {
5202 0 : PRUint32 glyphCount = glyphData->GetGlyphCount();
5203 0 : if (glyphCount == 0) {
5204 0 : continue;
5205 : }
5206 0 : const gfxTextRun::DetailedGlyph *details = GetDetailedGlyphs(j);
5207 0 : if (!details) {
5208 0 : continue;
5209 : }
5210 0 : for (PRUint32 k = 0; k < glyphCount; ++k, ++details) {
5211 0 : PRUint32 glyphIndex = details->mGlyphID;
5212 0 : if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) {
5213 0 : if (!fontIsSetup) {
5214 0 : font->SetupCairoFont(aRefContext);
5215 0 : fontIsSetup = true;
5216 : }
5217 : #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
5218 : ++gGlyphExtentsSetupEagerTight;
5219 : #endif
5220 0 : font->SetupGlyphExtents(aRefContext, glyphIndex, true, extents);
5221 : }
5222 : }
5223 : }
5224 : }
5225 : }
5226 : }
5227 :
5228 :
5229 0 : gfxTextRun::ClusterIterator::ClusterIterator(gfxTextRun *aTextRun)
5230 0 : : mTextRun(aTextRun), mCurrentChar(PRUint32(-1))
5231 : {
5232 0 : }
5233 :
5234 : void
5235 0 : gfxTextRun::ClusterIterator::Reset()
5236 : {
5237 0 : mCurrentChar = PRUint32(-1);
5238 0 : }
5239 :
5240 : bool
5241 0 : gfxTextRun::ClusterIterator::NextCluster()
5242 : {
5243 0 : while (++mCurrentChar < mTextRun->GetLength()) {
5244 0 : if (mTextRun->IsClusterStart(mCurrentChar)) {
5245 0 : return true;
5246 : }
5247 : }
5248 :
5249 0 : mCurrentChar = PRUint32(-1);
5250 0 : return false;
5251 : }
5252 :
5253 : PRUint32
5254 0 : gfxTextRun::ClusterIterator::ClusterLength() const
5255 : {
5256 0 : if (mCurrentChar == PRUint32(-1)) {
5257 0 : return 0;
5258 : }
5259 :
5260 0 : PRUint32 i = mCurrentChar;
5261 0 : while (++i < mTextRun->GetLength()) {
5262 0 : if (mTextRun->IsClusterStart(i)) {
5263 0 : break;
5264 : }
5265 : }
5266 :
5267 0 : return i - mCurrentChar;
5268 : }
5269 :
5270 : gfxFloat
5271 0 : gfxTextRun::ClusterIterator::ClusterAdvance(PropertyProvider *aProvider) const
5272 : {
5273 0 : if (mCurrentChar == PRUint32(-1)) {
5274 0 : return 0;
5275 : }
5276 :
5277 0 : return mTextRun->GetAdvanceWidth(mCurrentChar, ClusterLength(), aProvider);
5278 : }
5279 :
5280 : size_t
5281 0 : gfxTextRun::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf)
5282 : {
5283 : // The second arg is how much gfxTextRun::AllocateStorage would have
5284 : // allocated.
5285 0 : size_t total = mGlyphRuns.SizeOfExcludingThis(aMallocSizeOf);
5286 :
5287 0 : if (mDetailedGlyphs) {
5288 0 : total += mDetailedGlyphs->SizeOfIncludingThis(aMallocSizeOf);
5289 : }
5290 :
5291 0 : return total;
5292 : }
5293 :
5294 : size_t
5295 0 : gfxTextRun::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf)
5296 : {
5297 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
5298 : }
5299 :
5300 :
5301 : #ifdef DEBUG
5302 : void
5303 0 : gfxTextRun::Dump(FILE* aOutput) {
5304 0 : if (!aOutput) {
5305 0 : aOutput = stdout;
5306 : }
5307 :
5308 : PRUint32 i;
5309 0 : fputc('[', aOutput);
5310 0 : for (i = 0; i < mGlyphRuns.Length(); ++i) {
5311 0 : if (i > 0) {
5312 0 : fputc(',', aOutput);
5313 : }
5314 0 : gfxFont* font = mGlyphRuns[i].mFont;
5315 0 : const gfxFontStyle* style = font->GetStyle();
5316 0 : NS_ConvertUTF16toUTF8 fontName(font->GetName());
5317 0 : nsCAutoString lang;
5318 0 : style->language->ToUTF8String(lang);
5319 0 : fprintf(aOutput, "%d: %s %f/%d/%d/%s", mGlyphRuns[i].mCharacterOffset,
5320 : fontName.get(), style->size,
5321 0 : style->weight, style->style, lang.get());
5322 : }
5323 0 : fputc(']', aOutput);
5324 0 : }
5325 : #endif
|