1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=4 et sw=4 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "gfxTextRun.h"
8 #include "gfxGlyphExtents.h"
9 #include "gfxPlatformFontList.h"
10 #include "gfxUserFontSet.h"
11 #include "mozilla/gfx/2D.h"
12 #include "mozilla/gfx/PathHelpers.h"
13 #include "mozilla/Sprintf.h"
14
15 #include "gfxContext.h"
16 #include "gfxFontConstants.h"
17 #include "gfxFontMissingGlyphs.h"
18 #include "gfxScriptItemizer.h"
19 #include "nsUnicodeProperties.h"
20 #include "nsUnicodeRange.h"
21 #include "nsStyleConsts.h"
22 #include "mozilla/Likely.h"
23 #include "gfx2DGlue.h"
24 #include "mozilla/gfx/Logging.h" // for gfxCriticalError
25 #include "mozilla/UniquePtr.h"
26 #include "TextDrawTarget.h"
27
28 #ifdef XP_WIN
29 #include "gfxWindowsPlatform.h"
30 #endif
31
32 using namespace mozilla;
33 using namespace mozilla::gfx;
34 using namespace mozilla::unicode;
35 using mozilla::services::GetObserverService;
36
37 static const char16_t kEllipsisChar[] = {0x2026, 0x0};
38 static const char16_t kASCIIPeriodsChar[] = {'.', '.', '.', 0x0};
39
40 #ifdef DEBUG_roc
41 #define DEBUG_TEXT_RUN_STORAGE_METRICS
42 #endif
43
44 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
45 extern uint32_t gTextRunStorageHighWaterMark;
46 extern uint32_t gTextRunStorage;
47 extern uint32_t gFontCount;
48 extern uint32_t gGlyphExtentsCount;
49 extern uint32_t gGlyphExtentsWidthsTotalSize;
50 extern uint32_t gGlyphExtentsSetupEagerSimple;
51 extern uint32_t gGlyphExtentsSetupEagerTight;
52 extern uint32_t gGlyphExtentsSetupLazyTight;
53 extern uint32_t gGlyphExtentsSetupFallBackToTight;
54 #endif
55
NextRun()56 bool gfxTextRun::GlyphRunIterator::NextRun() {
57 uint32_t glyphRunCount;
58 if (mTextRun->mHasGlyphRunArray) {
59 glyphRunCount = mTextRun->mGlyphRunArray.Length();
60 if (mNextIndex >= glyphRunCount) {
61 return false;
62 }
63 mGlyphRun = &mTextRun->mGlyphRunArray[mNextIndex];
64 } else {
65 if (mNextIndex > 0 || !mTextRun->mSingleGlyphRun.mFont) {
66 return false;
67 }
68 glyphRunCount = 1;
69 mGlyphRun = &mTextRun->mSingleGlyphRun;
70 }
71
72 if (mGlyphRun->mCharacterOffset >= mEndOffset) {
73 return false;
74 }
75
76 mStringStart = std::max(mStartOffset, mGlyphRun->mCharacterOffset);
77 uint32_t last =
78 mNextIndex + 1 < glyphRunCount
79 ? mTextRun->mGlyphRunArray[mNextIndex + 1].mCharacterOffset
80 : mTextRun->GetLength();
81 mStringEnd = std::min(mEndOffset, last);
82
83 ++mNextIndex;
84 return true;
85 }
86
87 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
AccountStorageForTextRun(gfxTextRun * aTextRun,int32_t aSign)88 static void AccountStorageForTextRun(gfxTextRun* aTextRun, int32_t aSign) {
89 // Ignores detailed glyphs... we don't know when those have been constructed
90 // Also ignores gfxSkipChars dynamic storage (which won't be anything
91 // for preformatted text)
92 // Also ignores GlyphRun array, again because it hasn't been constructed
93 // by the time this gets called. If there's only one glyphrun that's stored
94 // directly in the textrun anyway so no additional overhead.
95 uint32_t length = aTextRun->GetLength();
96 int32_t bytes = length * sizeof(gfxTextRun::CompressedGlyph);
97 bytes += sizeof(gfxTextRun);
98 gTextRunStorage += bytes * aSign;
99 gTextRunStorageHighWaterMark =
100 std::max(gTextRunStorageHighWaterMark, gTextRunStorage);
101 }
102 #endif
103
NeedsGlyphExtents(gfxTextRun * aTextRun)104 static bool NeedsGlyphExtents(gfxTextRun* aTextRun) {
105 if (aTextRun->GetFlags() & gfx::ShapedTextFlags::TEXT_NEED_BOUNDING_BOX)
106 return true;
107 uint32_t numRuns;
108 const gfxTextRun::GlyphRun* glyphRuns = aTextRun->GetGlyphRuns(&numRuns);
109 for (uint32_t i = 0; i < numRuns; ++i) {
110 if (glyphRuns[i].mFont->GetFontEntry()->IsUserFont()) return true;
111 }
112 return false;
113 }
114
115 // Helper for textRun creation to preallocate storage for glyph records;
116 // this function returns a pointer to the newly-allocated glyph storage.
117 // Returns nullptr if allocation fails.
AllocateStorageForTextRun(size_t aSize,uint32_t aLength)118 void* gfxTextRun::AllocateStorageForTextRun(size_t aSize, uint32_t aLength) {
119 // Allocate the storage we need, returning nullptr on failure rather than
120 // throwing an exception (because web content can create huge runs).
121 void* storage = malloc(aSize + aLength * sizeof(CompressedGlyph));
122 if (!storage) {
123 NS_WARNING("failed to allocate storage for text run!");
124 return nullptr;
125 }
126
127 // Initialize the glyph storage (beyond aSize) to zero
128 memset(reinterpret_cast<char*>(storage) + aSize, 0,
129 aLength * sizeof(CompressedGlyph));
130
131 return storage;
132 }
133
Create(const gfxTextRunFactory::Parameters * aParams,uint32_t aLength,gfxFontGroup * aFontGroup,gfx::ShapedTextFlags aFlags,nsTextFrameUtils::Flags aFlags2)134 already_AddRefed<gfxTextRun> gfxTextRun::Create(
135 const gfxTextRunFactory::Parameters* aParams, uint32_t aLength,
136 gfxFontGroup* aFontGroup, gfx::ShapedTextFlags aFlags,
137 nsTextFrameUtils::Flags aFlags2) {
138 void* storage = AllocateStorageForTextRun(sizeof(gfxTextRun), aLength);
139 if (!storage) {
140 return nullptr;
141 }
142
143 RefPtr<gfxTextRun> result =
144 new (storage) gfxTextRun(aParams, aLength, aFontGroup, aFlags, aFlags2);
145 return result.forget();
146 }
147
gfxTextRun(const gfxTextRunFactory::Parameters * aParams,uint32_t aLength,gfxFontGroup * aFontGroup,gfx::ShapedTextFlags aFlags,nsTextFrameUtils::Flags aFlags2)148 gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters* aParams,
149 uint32_t aLength, gfxFontGroup* aFontGroup,
150 gfx::ShapedTextFlags aFlags,
151 nsTextFrameUtils::Flags aFlags2)
152 : gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit),
153 mSingleGlyphRun(),
154 mUserData(aParams->mUserData),
155 mFontGroup(aFontGroup),
156 mFlags2(aFlags2),
157 mReleasedFontGroup(false),
158 mHasGlyphRunArray(false),
159 mShapingState(eShapingState_Normal) {
160 NS_ASSERTION(mAppUnitsPerDevUnit > 0, "Invalid app unit scale");
161 NS_ADDREF(mFontGroup);
162
163 #ifndef RELEASE_OR_BETA
164 gfxTextPerfMetrics* tp = aFontGroup->GetTextPerfMetrics();
165 if (tp) {
166 tp->current.textrunConst++;
167 }
168 #endif
169
170 mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>(this + 1);
171
172 if (aParams->mSkipChars) {
173 mSkipChars.TakeFrom(aParams->mSkipChars);
174 }
175
176 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
177 AccountStorageForTextRun(this, 1);
178 #endif
179
180 mSkipDrawing = mFontGroup->ShouldSkipDrawing();
181 }
182
~gfxTextRun()183 gfxTextRun::~gfxTextRun() {
184 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
185 AccountStorageForTextRun(this, -1);
186 #endif
187 #ifdef DEBUG
188 // Make it easy to detect a dead text run
189 mFlags = ~gfx::ShapedTextFlags();
190 mFlags2 = ~nsTextFrameUtils::Flags();
191 #endif
192
193 if (mHasGlyphRunArray) {
194 mGlyphRunArray.~nsTArray<GlyphRun>();
195 } else {
196 mSingleGlyphRun.mFont = nullptr;
197 }
198
199 // The cached ellipsis textrun (if any) in a fontgroup will have already
200 // been told to release its reference to the group, so we mustn't do that
201 // again here.
202 if (!mReleasedFontGroup) {
203 #ifndef RELEASE_OR_BETA
204 gfxTextPerfMetrics* tp = mFontGroup->GetTextPerfMetrics();
205 if (tp) {
206 tp->current.textrunDestr++;
207 }
208 #endif
209 NS_RELEASE(mFontGroup);
210 }
211 }
212
ReleaseFontGroup()213 void gfxTextRun::ReleaseFontGroup() {
214 NS_ASSERTION(!mReleasedFontGroup, "doubly released!");
215 NS_RELEASE(mFontGroup);
216 mReleasedFontGroup = true;
217 }
218
SetPotentialLineBreaks(Range aRange,const uint8_t * aBreakBefore)219 bool gfxTextRun::SetPotentialLineBreaks(Range aRange,
220 const uint8_t* aBreakBefore) {
221 NS_ASSERTION(aRange.end <= GetLength(), "Overflow");
222
223 uint32_t changed = 0;
224 CompressedGlyph* cg = mCharacterGlyphs + aRange.start;
225 const CompressedGlyph* const end = cg + aRange.Length();
226 while (cg < end) {
227 uint8_t canBreak = *aBreakBefore++;
228 if (canBreak && !cg->IsClusterStart()) {
229 // XXX If we replace the line-breaker with one based more closely
230 // on UAX#14 (e.g. using ICU), this may not be needed any more.
231 // Avoid possible breaks inside a cluster, EXCEPT when the previous
232 // character was a space (compare UAX#14 rules LB9, LB10).
233 if (cg == mCharacterGlyphs || !(cg - 1)->CharIsSpace()) {
234 canBreak = CompressedGlyph::FLAG_BREAK_TYPE_NONE;
235 }
236 }
237 changed |= cg->SetCanBreakBefore(canBreak);
238 ++cg;
239 }
240 return changed != 0;
241 }
242
ComputeLigatureData(Range aPartRange,PropertyProvider * aProvider) const243 gfxTextRun::LigatureData gfxTextRun::ComputeLigatureData(
244 Range aPartRange, PropertyProvider* aProvider) const {
245 NS_ASSERTION(aPartRange.start < aPartRange.end,
246 "Computing ligature data for empty range");
247 NS_ASSERTION(aPartRange.end <= GetLength(), "Character length overflow");
248
249 LigatureData result;
250 const CompressedGlyph* charGlyphs = mCharacterGlyphs;
251
252 uint32_t i;
253 for (i = aPartRange.start; !charGlyphs[i].IsLigatureGroupStart(); --i) {
254 NS_ASSERTION(i > 0, "Ligature at the start of the run??");
255 }
256 result.mRange.start = i;
257 for (i = aPartRange.start + 1;
258 i < GetLength() && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
259 }
260 result.mRange.end = i;
261
262 int32_t ligatureWidth = GetAdvanceForGlyphs(result.mRange);
263 // Count the number of started clusters we have seen
264 uint32_t totalClusterCount = 0;
265 uint32_t partClusterIndex = 0;
266 uint32_t partClusterCount = 0;
267 for (i = result.mRange.start; i < result.mRange.end; ++i) {
268 // Treat the first character of the ligature as the start of a
269 // cluster for our purposes of allocating ligature width to its
270 // characters.
271 if (i == result.mRange.start || charGlyphs[i].IsClusterStart()) {
272 ++totalClusterCount;
273 if (i < aPartRange.start) {
274 ++partClusterIndex;
275 } else if (i < aPartRange.end) {
276 ++partClusterCount;
277 }
278 }
279 }
280 NS_ASSERTION(totalClusterCount > 0, "Ligature involving no clusters??");
281 result.mPartAdvance = partClusterIndex * (ligatureWidth / totalClusterCount);
282 result.mPartWidth = partClusterCount * (ligatureWidth / totalClusterCount);
283
284 // Any rounding errors are apportioned to the final part of the ligature,
285 // so that measuring all parts of a ligature and summing them is equal to
286 // the ligature width.
287 if (aPartRange.end == result.mRange.end) {
288 gfxFloat allParts = totalClusterCount * (ligatureWidth / totalClusterCount);
289 result.mPartWidth += ligatureWidth - allParts;
290 }
291
292 if (partClusterCount == 0) {
293 // nothing to draw
294 result.mClipBeforePart = result.mClipAfterPart = true;
295 } else {
296 // Determine whether we should clip before or after this part when
297 // drawing its slice of the ligature.
298 // We need to clip before the part if any cluster is drawn before
299 // this part.
300 result.mClipBeforePart = partClusterIndex > 0;
301 // We need to clip after the part if any cluster is drawn after
302 // this part.
303 result.mClipAfterPart =
304 partClusterIndex + partClusterCount < totalClusterCount;
305 }
306
307 if (aProvider && (mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING)) {
308 gfxFont::Spacing spacing;
309 if (aPartRange.start == result.mRange.start) {
310 aProvider->GetSpacing(Range(aPartRange.start, aPartRange.start + 1),
311 &spacing);
312 result.mPartWidth += spacing.mBefore;
313 }
314 if (aPartRange.end == result.mRange.end) {
315 aProvider->GetSpacing(Range(aPartRange.end - 1, aPartRange.end),
316 &spacing);
317 result.mPartWidth += spacing.mAfter;
318 }
319 }
320
321 return result;
322 }
323
ComputePartialLigatureWidth(Range aPartRange,PropertyProvider * aProvider) const324 gfxFloat gfxTextRun::ComputePartialLigatureWidth(
325 Range aPartRange, PropertyProvider* aProvider) const {
326 if (aPartRange.start >= aPartRange.end) return 0;
327 LigatureData data = ComputeLigatureData(aPartRange, aProvider);
328 return data.mPartWidth;
329 }
330
GetAdvanceForGlyphs(Range aRange) const331 int32_t gfxTextRun::GetAdvanceForGlyphs(Range aRange) const {
332 int32_t advance = 0;
333 for (auto i = aRange.start; i < aRange.end; ++i) {
334 advance += GetAdvanceForGlyph(i);
335 }
336 return advance;
337 }
338
GetAdjustedSpacing(const gfxTextRun * aTextRun,gfxTextRun::Range aRange,gfxTextRun::PropertyProvider * aProvider,gfxTextRun::PropertyProvider::Spacing * aSpacing)339 static void GetAdjustedSpacing(
340 const gfxTextRun* aTextRun, gfxTextRun::Range aRange,
341 gfxTextRun::PropertyProvider* aProvider,
342 gfxTextRun::PropertyProvider::Spacing* aSpacing) {
343 if (aRange.start >= aRange.end) return;
344
345 aProvider->GetSpacing(aRange, aSpacing);
346
347 #ifdef DEBUG
348 // Check to see if we have spacing inside ligatures
349
350 const gfxTextRun::CompressedGlyph* charGlyphs =
351 aTextRun->GetCharacterGlyphs();
352 uint32_t i;
353
354 for (i = aRange.start; i < aRange.end; ++i) {
355 if (!charGlyphs[i].IsLigatureGroupStart()) {
356 NS_ASSERTION(i == aRange.start || aSpacing[i - aRange.start].mBefore == 0,
357 "Before-spacing inside a ligature!");
358 NS_ASSERTION(
359 i - 1 <= aRange.start || aSpacing[i - 1 - aRange.start].mAfter == 0,
360 "After-spacing inside a ligature!");
361 }
362 }
363 #endif
364 }
365
GetAdjustedSpacingArray(Range aRange,PropertyProvider * aProvider,Range aSpacingRange,nsTArray<PropertyProvider::Spacing> * aSpacing) const366 bool gfxTextRun::GetAdjustedSpacingArray(
367 Range aRange, PropertyProvider* aProvider, Range aSpacingRange,
368 nsTArray<PropertyProvider::Spacing>* aSpacing) const {
369 if (!aProvider || !(mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING))
370 return false;
371 if (!aSpacing->AppendElements(aRange.Length())) return false;
372 auto spacingOffset = aSpacingRange.start - aRange.start;
373 memset(aSpacing->Elements(), 0, sizeof(gfxFont::Spacing) * spacingOffset);
374 GetAdjustedSpacing(this, aSpacingRange, aProvider,
375 aSpacing->Elements() + spacingOffset);
376 memset(aSpacing->Elements() + aSpacingRange.end - aRange.start, 0,
377 sizeof(gfxFont::Spacing) * (aRange.end - aSpacingRange.end));
378 return true;
379 }
380
ShrinkToLigatureBoundaries(Range * aRange) const381 void gfxTextRun::ShrinkToLigatureBoundaries(Range* aRange) const {
382 if (aRange->start >= aRange->end) return;
383
384 const CompressedGlyph* charGlyphs = mCharacterGlyphs;
385
386 while (aRange->start < aRange->end &&
387 !charGlyphs[aRange->start].IsLigatureGroupStart()) {
388 ++aRange->start;
389 }
390 if (aRange->end < GetLength()) {
391 while (aRange->end > aRange->start &&
392 !charGlyphs[aRange->end].IsLigatureGroupStart()) {
393 --aRange->end;
394 }
395 }
396 }
397
DrawGlyphs(gfxFont * aFont,Range aRange,gfx::Point * aPt,PropertyProvider * aProvider,Range aSpacingRange,TextRunDrawParams & aParams,gfx::ShapedTextFlags aOrientation) const398 void gfxTextRun::DrawGlyphs(gfxFont* aFont, Range aRange, gfx::Point* aPt,
399 PropertyProvider* aProvider, Range aSpacingRange,
400 TextRunDrawParams& aParams,
401 gfx::ShapedTextFlags aOrientation) const {
402 AutoTArray<PropertyProvider::Spacing, 200> spacingBuffer;
403 bool haveSpacing =
404 GetAdjustedSpacingArray(aRange, aProvider, aSpacingRange, &spacingBuffer);
405 aParams.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr;
406 aFont->Draw(this, aRange.start, aRange.end, aPt, aParams, aOrientation);
407 }
408
ClipPartialLigature(const gfxTextRun * aTextRun,gfxFloat * aStart,gfxFloat * aEnd,gfxFloat aOrigin,gfxTextRun::LigatureData * aLigature)409 static void ClipPartialLigature(const gfxTextRun* aTextRun, gfxFloat* aStart,
410 gfxFloat* aEnd, gfxFloat aOrigin,
411 gfxTextRun::LigatureData* aLigature) {
412 if (aLigature->mClipBeforePart) {
413 if (aTextRun->IsRightToLeft()) {
414 *aEnd = std::min(*aEnd, aOrigin);
415 } else {
416 *aStart = std::max(*aStart, aOrigin);
417 }
418 }
419 if (aLigature->mClipAfterPart) {
420 gfxFloat endEdge =
421 aOrigin + aTextRun->GetDirection() * aLigature->mPartWidth;
422 if (aTextRun->IsRightToLeft()) {
423 *aStart = std::max(*aStart, endEdge);
424 } else {
425 *aEnd = std::min(*aEnd, endEdge);
426 }
427 }
428 }
429
DrawPartialLigature(gfxFont * aFont,Range aRange,gfx::Point * aPt,PropertyProvider * aProvider,TextRunDrawParams & aParams,gfx::ShapedTextFlags aOrientation) const430 void gfxTextRun::DrawPartialLigature(gfxFont* aFont, Range aRange,
431 gfx::Point* aPt,
432 PropertyProvider* aProvider,
433 TextRunDrawParams& aParams,
434 gfx::ShapedTextFlags aOrientation) const {
435 if (aRange.start >= aRange.end) {
436 return;
437 }
438
439 if (auto* textDrawer = aParams.context->GetTextDrawer()) {
440 textDrawer->FoundUnsupportedFeature();
441 return;
442 }
443
444 // Draw partial ligature. We hack this by clipping the ligature.
445 LigatureData data = ComputeLigatureData(aRange, aProvider);
446 gfxRect clipExtents = aParams.context->GetClipExtents();
447 gfxFloat start, end;
448 if (aParams.isVerticalRun) {
449 start = clipExtents.Y() * mAppUnitsPerDevUnit;
450 end = clipExtents.YMost() * mAppUnitsPerDevUnit;
451 ClipPartialLigature(this, &start, &end, aPt->y, &data);
452 } else {
453 start = clipExtents.X() * mAppUnitsPerDevUnit;
454 end = clipExtents.XMost() * mAppUnitsPerDevUnit;
455 ClipPartialLigature(this, &start, &end, aPt->x, &data);
456 }
457
458 {
459 // use division here to ensure that when the rect is aligned on multiples
460 // of mAppUnitsPerDevUnit, we clip to true device unit boundaries.
461 // Also, make sure we snap the rectangle to device pixels.
462 Rect clipRect =
463 aParams.isVerticalRun
464 ? Rect(clipExtents.X(), start / mAppUnitsPerDevUnit,
465 clipExtents.Width(), (end - start) / mAppUnitsPerDevUnit)
466 : Rect(start / mAppUnitsPerDevUnit, clipExtents.Y(),
467 (end - start) / mAppUnitsPerDevUnit, clipExtents.Height());
468 MaybeSnapToDevicePixels(clipRect, *aParams.dt, true);
469
470 aParams.context->Clip(clipRect);
471 }
472
473 gfx::Point pt;
474 if (aParams.isVerticalRun) {
475 pt = Point(aPt->x, aPt->y - aParams.direction * data.mPartAdvance);
476 } else {
477 pt = Point(aPt->x - aParams.direction * data.mPartAdvance, aPt->y);
478 }
479
480 DrawGlyphs(aFont, data.mRange, &pt, aProvider, aRange, aParams, aOrientation);
481 aParams.context->PopClip();
482
483 if (aParams.isVerticalRun) {
484 aPt->y += aParams.direction * data.mPartWidth;
485 } else {
486 aPt->x += aParams.direction * data.mPartWidth;
487 }
488 }
489
490 // Returns true if a glyph run is using a font with synthetic bolding enabled,
491 // or a color font (COLR/SVG/sbix/CBDT), false otherwise. This is used to
492 // check whether the text run needs to be explicitly composited in order to
493 // support opacity.
HasSyntheticBoldOrColor(const gfxTextRun * aRun,gfxTextRun::Range aRange)494 static bool HasSyntheticBoldOrColor(const gfxTextRun* aRun,
495 gfxTextRun::Range aRange) {
496 gfxTextRun::GlyphRunIterator iter(aRun, aRange);
497 while (iter.NextRun()) {
498 gfxFont* font = iter.GetGlyphRun()->mFont;
499 if (font) {
500 if (font->IsSyntheticBold()) {
501 return true;
502 }
503 gfxFontEntry* fe = font->GetFontEntry();
504 if (fe->TryGetSVGData(font) || fe->TryGetColorGlyphs()) {
505 return true;
506 }
507 #if defined(XP_MACOSX) // sbix fonts only supported via Core Text
508 if (fe->HasFontTable(TRUETYPE_TAG('s', 'b', 'i', 'x'))) {
509 return true;
510 }
511 #endif
512 }
513 }
514 return false;
515 }
516
517 // Returns true if color is neither opaque nor transparent (i.e. alpha is not 0
518 // or 1), and false otherwise. If true, aCurrentColorOut is set on output.
HasNonOpaqueNonTransparentColor(gfxContext * aContext,Color & aCurrentColorOut)519 static bool HasNonOpaqueNonTransparentColor(gfxContext* aContext,
520 Color& aCurrentColorOut) {
521 if (aContext->GetDeviceColor(aCurrentColorOut)) {
522 if (0.f < aCurrentColorOut.a && aCurrentColorOut.a < 1.f) {
523 return true;
524 }
525 }
526 return false;
527 }
528
529 // helper class for double-buffering drawing with non-opaque color
530 struct MOZ_STACK_CLASS BufferAlphaColor {
BufferAlphaColorBufferAlphaColor531 explicit BufferAlphaColor(gfxContext* aContext) : mContext(aContext) {}
532
~BufferAlphaColorBufferAlphaColor533 ~BufferAlphaColor() {}
534
PushSolidColorBufferAlphaColor535 void PushSolidColor(const gfxRect& aBounds, const Color& aAlphaColor,
536 uint32_t appsPerDevUnit) {
537 mContext->Save();
538 mContext->NewPath();
539 mContext->Rectangle(
540 gfxRect(aBounds.X() / appsPerDevUnit, aBounds.Y() / appsPerDevUnit,
541 aBounds.Width() / appsPerDevUnit,
542 aBounds.Height() / appsPerDevUnit),
543 true);
544 mContext->Clip();
545 mContext->SetColor(Color(aAlphaColor.r, aAlphaColor.g, aAlphaColor.b));
546 mContext->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, aAlphaColor.a);
547 }
548
PopAlphaBufferAlphaColor549 void PopAlpha() {
550 // pop the text, using the color alpha as the opacity
551 mContext->PopGroupAndBlend();
552 mContext->Restore();
553 }
554
555 gfxContext* mContext;
556 };
557
Draw(Range aRange,gfx::Point aPt,const DrawParams & aParams) const558 void gfxTextRun::Draw(Range aRange, gfx::Point aPt,
559 const DrawParams& aParams) const {
560 NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range");
561 NS_ASSERTION(aParams.drawMode == DrawMode::GLYPH_PATH ||
562 !(aParams.drawMode & DrawMode::GLYPH_PATH),
563 "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or "
564 "GLYPH_STROKE_UNDERNEATH");
565 NS_ASSERTION(aParams.drawMode == DrawMode::GLYPH_PATH || !aParams.callbacks,
566 "callback must not be specified unless using GLYPH_PATH");
567
568 bool skipDrawing = mSkipDrawing;
569 if (aParams.drawMode & DrawMode::GLYPH_FILL) {
570 Color currentColor;
571 if (aParams.context->GetDeviceColor(currentColor) && currentColor.a == 0 &&
572 !aParams.context->GetTextDrawer()) {
573 skipDrawing = true;
574 }
575 }
576
577 gfxFloat direction = GetDirection();
578
579 if (skipDrawing) {
580 // We don't need to draw anything;
581 // but if the caller wants advance width, we need to compute it here
582 if (aParams.advanceWidth) {
583 gfxTextRun::Metrics metrics =
584 MeasureText(aRange, gfxFont::LOOSE_INK_EXTENTS,
585 aParams.context->GetDrawTarget(), aParams.provider);
586 *aParams.advanceWidth = metrics.mAdvanceWidth * direction;
587 }
588
589 // return without drawing
590 return;
591 }
592
593 // synthetic bolding draws glyphs twice ==> colors with opacity won't draw
594 // correctly unless first drawn without alpha
595 BufferAlphaColor syntheticBoldBuffer(aParams.context);
596 Color currentColor;
597 bool needToRestore = false;
598
599 if (aParams.drawMode & DrawMode::GLYPH_FILL &&
600 HasNonOpaqueNonTransparentColor(aParams.context, currentColor) &&
601 HasSyntheticBoldOrColor(this, aRange) &&
602 !aParams.context->GetTextDrawer()) {
603 needToRestore = true;
604 // Measure text; use the bounding box to determine the area we need
605 // to buffer.
606 gfxTextRun::Metrics metrics =
607 MeasureText(aRange, gfxFont::LOOSE_INK_EXTENTS,
608 aParams.context->GetDrawTarget(), aParams.provider);
609 if (IsRightToLeft()) {
610 metrics.mBoundingBox.MoveBy(
611 gfxPoint(aPt.x - metrics.mAdvanceWidth, aPt.y));
612 } else {
613 metrics.mBoundingBox.MoveBy(gfxPoint(aPt.x, aPt.y));
614 }
615 syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor,
616 GetAppUnitsPerDevUnit());
617 }
618
619 // Set up parameters that will be constant across all glyph runs we need
620 // to draw, regardless of the font used.
621 TextRunDrawParams params;
622 params.context = aParams.context;
623 params.devPerApp = 1.0 / double(GetAppUnitsPerDevUnit());
624 params.isVerticalRun = IsVertical();
625 params.isRTL = IsRightToLeft();
626 params.direction = direction;
627 params.strokeOpts = aParams.strokeOpts;
628 params.textStrokeColor = aParams.textStrokeColor;
629 params.textStrokePattern = aParams.textStrokePattern;
630 params.drawOpts = aParams.drawOpts;
631 params.drawMode = aParams.drawMode;
632 params.callbacks = aParams.callbacks;
633 params.runContextPaint = aParams.contextPaint;
634 params.paintSVGGlyphs =
635 !aParams.callbacks || aParams.callbacks->mShouldPaintSVGGlyphs;
636 params.dt = aParams.context->GetDrawTarget();
637
638 GlyphRunIterator iter(this, aRange);
639 gfxFloat advance = 0.0;
640
641 while (iter.NextRun()) {
642 gfxFont* font = iter.GetGlyphRun()->mFont;
643 uint32_t start = iter.GetStringStart();
644 uint32_t end = iter.GetStringEnd();
645 Range ligatureRange(start, end);
646 ShrinkToLigatureBoundaries(&ligatureRange);
647
648 bool drawPartial =
649 (aParams.drawMode & DrawMode::GLYPH_FILL) ||
650 (aParams.drawMode == DrawMode::GLYPH_PATH && aParams.callbacks);
651 gfx::Point origPt = aPt;
652
653 if (drawPartial) {
654 DrawPartialLigature(font, Range(start, ligatureRange.start), &aPt,
655 aParams.provider, params,
656 iter.GetGlyphRun()->mOrientation);
657 }
658
659 DrawGlyphs(font, ligatureRange, &aPt, aParams.provider, ligatureRange,
660 params, iter.GetGlyphRun()->mOrientation);
661
662 if (drawPartial) {
663 DrawPartialLigature(font, Range(ligatureRange.end, end), &aPt,
664 aParams.provider, params,
665 iter.GetGlyphRun()->mOrientation);
666 }
667
668 if (params.isVerticalRun) {
669 advance += (aPt.y - origPt.y) * params.direction;
670 } else {
671 advance += (aPt.x - origPt.x) * params.direction;
672 }
673 }
674
675 // composite result when synthetic bolding used
676 if (needToRestore) {
677 syntheticBoldBuffer.PopAlpha();
678 }
679
680 if (aParams.advanceWidth) {
681 *aParams.advanceWidth = advance;
682 }
683 }
684
685 // This method is mostly parallel to Draw().
DrawEmphasisMarks(gfxContext * aContext,gfxTextRun * aMark,gfxFloat aMarkAdvance,gfx::Point aPt,Range aRange,PropertyProvider * aProvider) const686 void gfxTextRun::DrawEmphasisMarks(gfxContext* aContext, gfxTextRun* aMark,
687 gfxFloat aMarkAdvance, gfx::Point aPt,
688 Range aRange,
689 PropertyProvider* aProvider) const {
690 MOZ_ASSERT(aRange.end <= GetLength());
691
692 EmphasisMarkDrawParams params;
693 params.context = aContext;
694 params.mark = aMark;
695 params.advance = aMarkAdvance;
696 params.direction = GetDirection();
697 params.isVertical = IsVertical();
698
699 float& inlineCoord = params.isVertical ? aPt.y : aPt.x;
700 float direction = params.direction;
701
702 GlyphRunIterator iter(this, aRange);
703 while (iter.NextRun()) {
704 gfxFont* font = iter.GetGlyphRun()->mFont;
705 uint32_t start = iter.GetStringStart();
706 uint32_t end = iter.GetStringEnd();
707 Range ligatureRange(start, end);
708 ShrinkToLigatureBoundaries(&ligatureRange);
709
710 inlineCoord +=
711 direction * ComputePartialLigatureWidth(
712 Range(start, ligatureRange.start), aProvider);
713
714 AutoTArray<PropertyProvider::Spacing, 200> spacingBuffer;
715 bool haveSpacing = GetAdjustedSpacingArray(ligatureRange, aProvider,
716 ligatureRange, &spacingBuffer);
717 params.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr;
718 font->DrawEmphasisMarks(this, &aPt, ligatureRange.start,
719 ligatureRange.Length(), params);
720
721 inlineCoord += direction * ComputePartialLigatureWidth(
722 Range(ligatureRange.end, end), aProvider);
723 }
724 }
725
AccumulateMetricsForRun(gfxFont * aFont,Range aRange,gfxFont::BoundingBoxType aBoundingBoxType,DrawTarget * aRefDrawTarget,PropertyProvider * aProvider,Range aSpacingRange,gfx::ShapedTextFlags aOrientation,Metrics * aMetrics) const726 void gfxTextRun::AccumulateMetricsForRun(
727 gfxFont* aFont, Range aRange, gfxFont::BoundingBoxType aBoundingBoxType,
728 DrawTarget* aRefDrawTarget, PropertyProvider* aProvider,
729 Range aSpacingRange, gfx::ShapedTextFlags aOrientation,
730 Metrics* aMetrics) const {
731 AutoTArray<PropertyProvider::Spacing, 200> spacingBuffer;
732 bool haveSpacing =
733 GetAdjustedSpacingArray(aRange, aProvider, aSpacingRange, &spacingBuffer);
734 Metrics metrics = aFont->Measure(
735 this, aRange.start, aRange.end, aBoundingBoxType, aRefDrawTarget,
736 haveSpacing ? spacingBuffer.Elements() : nullptr, aOrientation);
737 aMetrics->CombineWith(metrics, IsRightToLeft());
738 }
739
AccumulatePartialLigatureMetrics(gfxFont * aFont,Range aRange,gfxFont::BoundingBoxType aBoundingBoxType,DrawTarget * aRefDrawTarget,PropertyProvider * aProvider,gfx::ShapedTextFlags aOrientation,Metrics * aMetrics) const740 void gfxTextRun::AccumulatePartialLigatureMetrics(
741 gfxFont* aFont, Range aRange, gfxFont::BoundingBoxType aBoundingBoxType,
742 DrawTarget* aRefDrawTarget, PropertyProvider* aProvider,
743 gfx::ShapedTextFlags aOrientation, Metrics* aMetrics) const {
744 if (aRange.start >= aRange.end) return;
745
746 // Measure partial ligature. We hack this by clipping the metrics in the
747 // same way we clip the drawing.
748 LigatureData data = ComputeLigatureData(aRange, aProvider);
749
750 // First measure the complete ligature
751 Metrics metrics;
752 AccumulateMetricsForRun(aFont, data.mRange, aBoundingBoxType, aRefDrawTarget,
753 aProvider, aRange, aOrientation, &metrics);
754
755 // Clip the bounding box to the ligature part
756 gfxFloat bboxLeft = metrics.mBoundingBox.X();
757 gfxFloat bboxRight = metrics.mBoundingBox.XMost();
758 // Where we are going to start "drawing" relative to our left baseline origin
759 gfxFloat origin =
760 IsRightToLeft() ? metrics.mAdvanceWidth - data.mPartAdvance : 0;
761 ClipPartialLigature(this, &bboxLeft, &bboxRight, origin, &data);
762 metrics.mBoundingBox.SetBoxX(bboxLeft, bboxRight);
763
764 // mBoundingBox is now relative to the left baseline origin for the entire
765 // ligature. Shift it left.
766 metrics.mBoundingBox.MoveByX(
767 -(IsRightToLeft()
768 ? metrics.mAdvanceWidth - (data.mPartAdvance + data.mPartWidth)
769 : data.mPartAdvance));
770 metrics.mAdvanceWidth = data.mPartWidth;
771
772 aMetrics->CombineWith(metrics, IsRightToLeft());
773 }
774
MeasureText(Range aRange,gfxFont::BoundingBoxType aBoundingBoxType,DrawTarget * aRefDrawTarget,PropertyProvider * aProvider) const775 gfxTextRun::Metrics gfxTextRun::MeasureText(
776 Range aRange, gfxFont::BoundingBoxType aBoundingBoxType,
777 DrawTarget* aRefDrawTarget, PropertyProvider* aProvider) const {
778 NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range");
779
780 Metrics accumulatedMetrics;
781 GlyphRunIterator iter(this, aRange);
782 while (iter.NextRun()) {
783 gfxFont* font = iter.GetGlyphRun()->mFont;
784 uint32_t start = iter.GetStringStart();
785 uint32_t end = iter.GetStringEnd();
786 Range ligatureRange(start, end);
787 ShrinkToLigatureBoundaries(&ligatureRange);
788
789 AccumulatePartialLigatureMetrics(
790 font, Range(start, ligatureRange.start), aBoundingBoxType,
791 aRefDrawTarget, aProvider, iter.GetGlyphRun()->mOrientation,
792 &accumulatedMetrics);
793
794 // XXX This sucks. We have to get glyph extents just so we can detect
795 // glyphs outside the font box, even when aBoundingBoxType is LOOSE,
796 // even though in almost all cases we could get correct results just
797 // by getting some ascent/descent from the font and using our stored
798 // advance widths.
799 AccumulateMetricsForRun(
800 font, ligatureRange, aBoundingBoxType, aRefDrawTarget, aProvider,
801 ligatureRange, iter.GetGlyphRun()->mOrientation, &accumulatedMetrics);
802
803 AccumulatePartialLigatureMetrics(
804 font, Range(ligatureRange.end, end), aBoundingBoxType, aRefDrawTarget,
805 aProvider, iter.GetGlyphRun()->mOrientation, &accumulatedMetrics);
806 }
807
808 return accumulatedMetrics;
809 }
810
811 #define MEASUREMENT_BUFFER_SIZE 100
812
ClassifyAutoHyphenations(uint32_t aStart,Range aRange,nsTArray<HyphenType> & aHyphenBuffer,HyphenationState * aWordState)813 void gfxTextRun::ClassifyAutoHyphenations(uint32_t aStart, Range aRange,
814 nsTArray<HyphenType>& aHyphenBuffer,
815 HyphenationState* aWordState) {
816 NS_PRECONDITION(
817 aRange.end - aStart <= aHyphenBuffer.Length() && aRange.start >= aStart,
818 "Range out of bounds");
819 MOZ_ASSERT(aWordState->mostRecentBoundary >= aStart,
820 "Unexpected aMostRecentWordBoundary!!");
821
822 uint32_t start =
823 std::min<uint32_t>(aRange.start, aWordState->mostRecentBoundary);
824
825 for (uint32_t i = start; i < aRange.end; ++i) {
826 if (aHyphenBuffer[i - aStart] == HyphenType::Explicit &&
827 !aWordState->hasExplicitHyphen) {
828 aWordState->hasExplicitHyphen = true;
829 }
830 if (!aWordState->hasManualHyphen &&
831 (aHyphenBuffer[i - aStart] == HyphenType::Soft ||
832 aHyphenBuffer[i - aStart] == HyphenType::Explicit)) {
833 aWordState->hasManualHyphen = true;
834 // This is the first manual hyphen in the current word. We can only
835 // know if the current word has a manual hyphen until now. So, we need
836 // to run a sub loop to update the auto hyphens between the start of
837 // the current word and this manual hyphen.
838 if (aWordState->hasAutoHyphen) {
839 for (uint32_t j = aWordState->mostRecentBoundary; j < i; j++) {
840 if (aHyphenBuffer[j - aStart] ==
841 HyphenType::AutoWithoutManualInSameWord) {
842 aHyphenBuffer[j - aStart] = HyphenType::AutoWithManualInSameWord;
843 }
844 }
845 }
846 }
847 if (aHyphenBuffer[i - aStart] == HyphenType::AutoWithoutManualInSameWord) {
848 if (!aWordState->hasAutoHyphen) {
849 aWordState->hasAutoHyphen = true;
850 }
851 if (aWordState->hasManualHyphen) {
852 aHyphenBuffer[i - aStart] = HyphenType::AutoWithManualInSameWord;
853 }
854 }
855
856 // If we're at the word boundary, clear/reset couple states.
857 if (mCharacterGlyphs[i].CharIsSpace() || mCharacterGlyphs[i].CharIsTab() ||
858 mCharacterGlyphs[i].CharIsNewline() ||
859 // Since we will not have a boundary in the end of the string, let's
860 // call the end of the string a special case for word boundary.
861 i == GetLength() - 1) {
862 // We can only get to know whether we should raise/clear an explicit
863 // manual hyphen until we get to the end of a word, because this depends
864 // on whether there exists at least one auto hyphen in the same word.
865 if (!aWordState->hasAutoHyphen && aWordState->hasExplicitHyphen) {
866 for (uint32_t j = aWordState->mostRecentBoundary; j <= i; j++) {
867 if (aHyphenBuffer[j - aStart] == HyphenType::Explicit) {
868 aHyphenBuffer[j - aStart] = HyphenType::None;
869 }
870 }
871 }
872 aWordState->mostRecentBoundary = i;
873 aWordState->hasManualHyphen = false;
874 aWordState->hasAutoHyphen = false;
875 aWordState->hasExplicitHyphen = false;
876 }
877 }
878 }
879
BreakAndMeasureText(uint32_t aStart,uint32_t aMaxLength,bool aLineBreakBefore,gfxFloat aWidth,PropertyProvider * aProvider,SuppressBreak aSuppressBreak,gfxFloat * aTrimWhitespace,bool aWhitespaceCanHang,Metrics * aMetrics,gfxFont::BoundingBoxType aBoundingBoxType,DrawTarget * aRefDrawTarget,bool * aUsedHyphenation,uint32_t * aLastBreak,bool aCanWordWrap,gfxBreakPriority * aBreakPriority)880 uint32_t gfxTextRun::BreakAndMeasureText(
881 uint32_t aStart, uint32_t aMaxLength, bool aLineBreakBefore,
882 gfxFloat aWidth, PropertyProvider* aProvider, SuppressBreak aSuppressBreak,
883 gfxFloat* aTrimWhitespace, bool aWhitespaceCanHang, Metrics* aMetrics,
884 gfxFont::BoundingBoxType aBoundingBoxType, DrawTarget* aRefDrawTarget,
885 bool* aUsedHyphenation, uint32_t* aLastBreak, bool aCanWordWrap,
886 gfxBreakPriority* aBreakPriority) {
887 aMaxLength = std::min(aMaxLength, GetLength() - aStart);
888
889 NS_ASSERTION(aStart + aMaxLength <= GetLength(), "Substring out of range");
890
891 Range bufferRange(
892 aStart, aStart + std::min<uint32_t>(aMaxLength, MEASUREMENT_BUFFER_SIZE));
893 PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE];
894 bool haveSpacing =
895 aProvider && !!(mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING);
896 if (haveSpacing) {
897 GetAdjustedSpacing(this, bufferRange, aProvider, spacingBuffer);
898 }
899 AutoTArray<HyphenType, 4096> hyphenBuffer;
900 HyphenationState wordState;
901 wordState.mostRecentBoundary = aStart;
902 bool haveHyphenation =
903 aProvider &&
904 (aProvider->GetHyphensOption() == StyleHyphens::Auto ||
905 (aProvider->GetHyphensOption() == StyleHyphens::Manual &&
906 !!(mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_HYPHEN_BREAKS)));
907 if (haveHyphenation) {
908 if (hyphenBuffer.AppendElements(bufferRange.Length(), fallible)) {
909 aProvider->GetHyphenationBreaks(bufferRange, hyphenBuffer.Elements());
910 if (aProvider->GetHyphensOption() == StyleHyphens::Auto) {
911 ClassifyAutoHyphenations(aStart, bufferRange, hyphenBuffer, &wordState);
912 }
913 } else {
914 haveHyphenation = false;
915 }
916 }
917
918 gfxFloat width = 0;
919 gfxFloat advance = 0;
920 // The number of space characters that can be trimmed or hang at a soft-wrap
921 uint32_t trimmableChars = 0;
922 // The amount of space removed by ignoring trimmableChars
923 gfxFloat trimmableAdvance = 0;
924 int32_t lastBreak = -1;
925 int32_t lastBreakTrimmableChars = -1;
926 gfxFloat lastBreakTrimmableAdvance = -1;
927 // Cache the last candidate break
928 int32_t lastCandidateBreak = -1;
929 int32_t lastCandidateBreakTrimmableChars = -1;
930 gfxFloat lastCandidateBreakTrimmableAdvance = -1;
931 bool lastCandidateBreakUsedHyphenation = false;
932 gfxBreakPriority lastCandidateBreakPriority = gfxBreakPriority::eNoBreak;
933 bool aborted = false;
934 uint32_t end = aStart + aMaxLength;
935 bool lastBreakUsedHyphenation = false;
936 Range ligatureRange(aStart, end);
937 ShrinkToLigatureBoundaries(&ligatureRange);
938
939 // We may need to move `i` backwards in the following loop, and re-scan
940 // part of the textrun; we'll use `rescanLimit` so we can tell when that
941 // is happening: if `i < rescanLimit` then we're rescanning.
942 uint32_t rescanLimit = aStart;
943 for (uint32_t i = aStart; i < end; ++i) {
944 if (i >= bufferRange.end) {
945 // Fetch more spacing and hyphenation data
946 uint32_t oldHyphenBufferLength = hyphenBuffer.Length();
947 bufferRange.start = i;
948 bufferRange.end =
949 std::min(aStart + aMaxLength, i + MEASUREMENT_BUFFER_SIZE);
950 // For spacing, we always overwrite the old data with the newly
951 // fetched one. However, for hyphenation, hyphenation data sometimes
952 // depends on the context in every word (if "hyphens: auto" is set).
953 // To ensure we get enough information between neighboring buffers,
954 // we grow the hyphenBuffer instead of overwrite it.
955 // NOTE that this means bufferRange does not correspond to the
956 // entire hyphenBuffer, but only to the most recently added portion.
957 // Therefore, we need to add the old length to hyphenBuffer.Elements()
958 // when getting more data.
959 if (haveSpacing) {
960 GetAdjustedSpacing(this, bufferRange, aProvider, spacingBuffer);
961 }
962 if (haveHyphenation) {
963 if (hyphenBuffer.AppendElements(bufferRange.Length(), fallible)) {
964 aProvider->GetHyphenationBreaks(
965 bufferRange, hyphenBuffer.Elements() + oldHyphenBufferLength);
966 if (aProvider->GetHyphensOption() == StyleHyphens::Auto) {
967 uint32_t prevMostRecentWordBoundary = wordState.mostRecentBoundary;
968 ClassifyAutoHyphenations(aStart, bufferRange, hyphenBuffer,
969 &wordState);
970 // If the buffer boundary is in the middle of a word,
971 // we need to go back to the start of the current word.
972 // So, we can correct the wrong candidates that we set
973 // in the previous runs of the loop.
974 if (prevMostRecentWordBoundary < oldHyphenBufferLength) {
975 rescanLimit = i;
976 i = prevMostRecentWordBoundary - 1;
977 continue;
978 }
979 }
980 } else {
981 haveHyphenation = false;
982 }
983 }
984 }
985
986 // There can't be a word-wrap break opportunity at the beginning of the
987 // line: if the width is too small for even one character to fit, it
988 // could be the first and last break opportunity on the line, and that
989 // would trigger an infinite loop.
990 if (aSuppressBreak != eSuppressAllBreaks &&
991 (aSuppressBreak != eSuppressInitialBreak || i > aStart)) {
992 bool atNaturalBreak = mCharacterGlyphs[i].CanBreakBefore() == 1;
993 bool atHyphenationBreak = !atNaturalBreak && haveHyphenation &&
994 hyphenBuffer[i - aStart] != HyphenType::None;
995 bool atAutoHyphenWithManualHyphenInSameWord =
996 atHyphenationBreak &&
997 hyphenBuffer[i - aStart] == HyphenType::AutoWithManualInSameWord;
998 bool atBreak = atNaturalBreak || atHyphenationBreak;
999 bool wordWrapping = aCanWordWrap &&
1000 mCharacterGlyphs[i].IsClusterStart() &&
1001 *aBreakPriority <= gfxBreakPriority::eWordWrapBreak;
1002
1003 if (atBreak || wordWrapping) {
1004 gfxFloat hyphenatedAdvance = advance;
1005 if (atHyphenationBreak) {
1006 hyphenatedAdvance += aProvider->GetHyphenWidth();
1007 }
1008
1009 if (lastBreak < 0 ||
1010 width + hyphenatedAdvance - trimmableAdvance <= aWidth) {
1011 // We can break here.
1012 lastBreak = i;
1013 lastBreakTrimmableChars = trimmableChars;
1014 lastBreakTrimmableAdvance = trimmableAdvance;
1015 lastBreakUsedHyphenation = atHyphenationBreak;
1016 *aBreakPriority = atBreak ? gfxBreakPriority::eNormalBreak
1017 : gfxBreakPriority::eWordWrapBreak;
1018 }
1019
1020 width += advance;
1021 advance = 0;
1022 if (width - trimmableAdvance > aWidth) {
1023 // No more text fits. Abort
1024 aborted = true;
1025 break;
1026 }
1027 // There are various kinds of break opportunities:
1028 // 1. word wrap break,
1029 // 2. natural break,
1030 // 3. manual hyphenation break,
1031 // 4. auto hyphenation break without any manual hyphenation
1032 // in the same word,
1033 // 5. auto hyphenation break with another manual hyphenation
1034 // in the same word.
1035 // Allow all of them except the last one to be a candidate.
1036 // So, we can ensure that we don't use an automatic
1037 // hyphenation opportunity within a word that contains another
1038 // manual hyphenation, unless it is the only choice.
1039 if (wordWrapping || !atAutoHyphenWithManualHyphenInSameWord) {
1040 lastCandidateBreak = lastBreak;
1041 lastCandidateBreakTrimmableChars = lastBreakTrimmableChars;
1042 lastCandidateBreakTrimmableAdvance = lastBreakTrimmableAdvance;
1043 lastCandidateBreakUsedHyphenation = lastBreakUsedHyphenation;
1044 lastCandidateBreakPriority = *aBreakPriority;
1045 }
1046 }
1047 }
1048
1049 // If we're re-scanning part of a word (to re-process potential
1050 // hyphenation types) then we don't want to accumulate widths again
1051 // for the characters that were already added to `advance`.
1052 if (i < rescanLimit) {
1053 continue;
1054 }
1055
1056 gfxFloat charAdvance;
1057 if (i >= ligatureRange.start && i < ligatureRange.end) {
1058 charAdvance = GetAdvanceForGlyphs(Range(i, i + 1));
1059 if (haveSpacing) {
1060 PropertyProvider::Spacing* space =
1061 &spacingBuffer[i - bufferRange.start];
1062 charAdvance += space->mBefore + space->mAfter;
1063 }
1064 } else {
1065 charAdvance = ComputePartialLigatureWidth(Range(i, i + 1), aProvider);
1066 }
1067
1068 advance += charAdvance;
1069 if (aTrimWhitespace || aWhitespaceCanHang) {
1070 if (mCharacterGlyphs[i].CharIsSpace()) {
1071 ++trimmableChars;
1072 trimmableAdvance += charAdvance;
1073 } else {
1074 trimmableAdvance = 0;
1075 trimmableChars = 0;
1076 }
1077 }
1078 }
1079
1080 if (!aborted) {
1081 width += advance;
1082 }
1083
1084 // There are three possibilities:
1085 // 1) all the text fit (width <= aWidth)
1086 // 2) some of the text fit up to a break opportunity (width > aWidth &&
1087 // lastBreak >= 0)
1088 // 3) none of the text fits before a break opportunity (width > aWidth &&
1089 // lastBreak < 0)
1090 uint32_t charsFit;
1091 bool usedHyphenation = false;
1092 if (width - trimmableAdvance <= aWidth) {
1093 charsFit = aMaxLength;
1094 } else if (lastBreak >= 0) {
1095 if (lastCandidateBreak >= 0 && lastCandidateBreak != lastBreak) {
1096 lastBreak = lastCandidateBreak;
1097 lastBreakTrimmableChars = lastCandidateBreakTrimmableChars;
1098 lastBreakTrimmableAdvance = lastCandidateBreakTrimmableAdvance;
1099 lastBreakUsedHyphenation = lastCandidateBreakUsedHyphenation;
1100 *aBreakPriority = lastCandidateBreakPriority;
1101 }
1102 charsFit = lastBreak - aStart;
1103 trimmableChars = lastBreakTrimmableChars;
1104 trimmableAdvance = lastBreakTrimmableAdvance;
1105 usedHyphenation = lastBreakUsedHyphenation;
1106 } else {
1107 charsFit = aMaxLength;
1108 }
1109
1110 if (aMetrics) {
1111 auto fitEnd = aStart + charsFit;
1112 // Initially, measure everything, so that our bounding box includes
1113 // any trimmable or hanging whitespace.
1114 *aMetrics = MeasureText(Range(aStart, fitEnd), aBoundingBoxType,
1115 aRefDrawTarget, aProvider);
1116 if (aTrimWhitespace || aWhitespaceCanHang) {
1117 // Measure trailing whitespace that is to be trimmed/hung.
1118 Metrics trimOrHangMetrics =
1119 MeasureText(Range(fitEnd - trimmableChars, fitEnd), aBoundingBoxType,
1120 aRefDrawTarget, aProvider);
1121 if (aTrimWhitespace) {
1122 aMetrics->mAdvanceWidth -= trimOrHangMetrics.mAdvanceWidth;
1123 } else if (aMetrics->mAdvanceWidth > aWidth) {
1124 // Restrict width of hanging whitespace so it doesn't overflow.
1125 aMetrics->mAdvanceWidth = std::max(
1126 aWidth, aMetrics->mAdvanceWidth - trimOrHangMetrics.mAdvanceWidth);
1127 }
1128 }
1129 }
1130 if (aTrimWhitespace) {
1131 *aTrimWhitespace = trimmableAdvance;
1132 }
1133 if (aUsedHyphenation) {
1134 *aUsedHyphenation = usedHyphenation;
1135 }
1136 if (aLastBreak && charsFit == aMaxLength) {
1137 if (lastBreak < 0) {
1138 *aLastBreak = UINT32_MAX;
1139 } else {
1140 *aLastBreak = lastBreak - aStart;
1141 }
1142 }
1143
1144 return charsFit;
1145 }
1146
GetAdvanceWidth(Range aRange,PropertyProvider * aProvider,PropertyProvider::Spacing * aSpacing) const1147 gfxFloat gfxTextRun::GetAdvanceWidth(
1148 Range aRange, PropertyProvider* aProvider,
1149 PropertyProvider::Spacing* aSpacing) const {
1150 NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range");
1151
1152 Range ligatureRange = aRange;
1153 ShrinkToLigatureBoundaries(&ligatureRange);
1154
1155 gfxFloat result = ComputePartialLigatureWidth(
1156 Range(aRange.start, ligatureRange.start), aProvider) +
1157 ComputePartialLigatureWidth(
1158 Range(ligatureRange.end, aRange.end), aProvider);
1159
1160 if (aSpacing) {
1161 aSpacing->mBefore = aSpacing->mAfter = 0;
1162 }
1163
1164 // Account for all remaining spacing here. This is more efficient than
1165 // processing it along with the glyphs.
1166 if (aProvider && (mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING)) {
1167 uint32_t i;
1168 AutoTArray<PropertyProvider::Spacing, 200> spacingBuffer;
1169 if (spacingBuffer.AppendElements(aRange.Length())) {
1170 GetAdjustedSpacing(this, ligatureRange, aProvider,
1171 spacingBuffer.Elements());
1172 for (i = 0; i < ligatureRange.Length(); ++i) {
1173 PropertyProvider::Spacing* space = &spacingBuffer[i];
1174 result += space->mBefore + space->mAfter;
1175 }
1176 if (aSpacing) {
1177 aSpacing->mBefore = spacingBuffer[0].mBefore;
1178 aSpacing->mAfter = spacingBuffer.LastElement().mAfter;
1179 }
1180 }
1181 }
1182
1183 return result + GetAdvanceForGlyphs(ligatureRange);
1184 }
1185
SetLineBreaks(Range aRange,bool aLineBreakBefore,bool aLineBreakAfter,gfxFloat * aAdvanceWidthDelta)1186 bool gfxTextRun::SetLineBreaks(Range aRange, bool aLineBreakBefore,
1187 bool aLineBreakAfter,
1188 gfxFloat* aAdvanceWidthDelta) {
1189 // Do nothing because our shaping does not currently take linebreaks into
1190 // account. There is no change in advance width.
1191 if (aAdvanceWidthDelta) {
1192 *aAdvanceWidthDelta = 0;
1193 }
1194 return false;
1195 }
1196
FindFirstGlyphRunContaining(uint32_t aOffset) const1197 uint32_t gfxTextRun::FindFirstGlyphRunContaining(uint32_t aOffset) const {
1198 NS_ASSERTION(aOffset <= GetLength(), "Bad offset looking for glyphrun");
1199 NS_ASSERTION(GetLength() == 0 ||
1200 (!mHasGlyphRunArray && mSingleGlyphRun.mFont) ||
1201 (mHasGlyphRunArray && mGlyphRunArray.Length() > 0),
1202 "non-empty text but no glyph runs present!");
1203 if (!mHasGlyphRunArray) {
1204 return 0;
1205 }
1206 if (aOffset == GetLength()) {
1207 return mGlyphRunArray.Length();
1208 }
1209 uint32_t start = 0;
1210 uint32_t end = mGlyphRunArray.Length();
1211 while (end - start > 1) {
1212 uint32_t mid = (start + end) / 2;
1213 if (mGlyphRunArray[mid].mCharacterOffset <= aOffset) {
1214 start = mid;
1215 } else {
1216 end = mid;
1217 }
1218 }
1219 NS_ASSERTION(mGlyphRunArray[start].mCharacterOffset <= aOffset,
1220 "Hmm, something went wrong, aOffset should have been found");
1221 return start;
1222 }
1223
AddGlyphRun(gfxFont * aFont,uint8_t aMatchType,uint32_t aUTF16Offset,bool aForceNewRun,gfx::ShapedTextFlags aOrientation)1224 nsresult gfxTextRun::AddGlyphRun(gfxFont* aFont, uint8_t aMatchType,
1225 uint32_t aUTF16Offset, bool aForceNewRun,
1226 gfx::ShapedTextFlags aOrientation) {
1227 NS_ASSERTION(aFont, "adding glyph run for null font!");
1228 NS_ASSERTION(aOrientation != gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED,
1229 "mixed orientation should have been resolved");
1230 if (!aFont) {
1231 return NS_OK;
1232 }
1233 if (!mHasGlyphRunArray) {
1234 // We don't currently have an array.
1235 if (!mSingleGlyphRun.mFont) {
1236 // This is the first glyph run: just store it directly.
1237 mSingleGlyphRun.mFont = aFont;
1238 mSingleGlyphRun.mMatchType = aMatchType;
1239 mSingleGlyphRun.mOrientation = aOrientation;
1240 mSingleGlyphRun.mCharacterOffset = aUTF16Offset;
1241 return NS_OK;
1242 }
1243 }
1244 uint32_t numGlyphRuns = mHasGlyphRunArray ? mGlyphRunArray.Length() : 1;
1245 if (!aForceNewRun && numGlyphRuns > 0) {
1246 GlyphRun* lastGlyphRun = mHasGlyphRunArray
1247 ? &mGlyphRunArray[numGlyphRuns - 1]
1248 : &mSingleGlyphRun;
1249
1250 NS_ASSERTION(lastGlyphRun->mCharacterOffset <= aUTF16Offset,
1251 "Glyph runs out of order (and run not forced)");
1252
1253 // Don't append a run if the font is already the one we want
1254 if (lastGlyphRun->mFont == aFont &&
1255 lastGlyphRun->mMatchType == aMatchType &&
1256 lastGlyphRun->mOrientation == aOrientation) {
1257 return NS_OK;
1258 }
1259
1260 // If the offset has not changed, avoid leaving a zero-length run
1261 // by overwriting the last entry instead of appending...
1262 if (lastGlyphRun->mCharacterOffset == aUTF16Offset) {
1263 // ...except that if the run before the last entry had the same
1264 // font as the new one wants, merge with it instead of creating
1265 // adjacent runs with the same font
1266 if (numGlyphRuns > 1 && mGlyphRunArray[numGlyphRuns - 2].mFont == aFont &&
1267 mGlyphRunArray[numGlyphRuns - 2].mMatchType == aMatchType &&
1268 mGlyphRunArray[numGlyphRuns - 2].mOrientation == aOrientation) {
1269 mGlyphRunArray.TruncateLength(numGlyphRuns - 1);
1270 if (mGlyphRunArray.Length() == 1) {
1271 ConvertFromGlyphRunArray();
1272 }
1273 return NS_OK;
1274 }
1275
1276 lastGlyphRun->mFont = aFont;
1277 lastGlyphRun->mMatchType = aMatchType;
1278 lastGlyphRun->mOrientation = aOrientation;
1279 return NS_OK;
1280 }
1281 }
1282
1283 NS_ASSERTION(
1284 aForceNewRun || numGlyphRuns > 0 || aUTF16Offset == 0,
1285 "First run doesn't cover the first character (and run not forced)?");
1286
1287 if (!mHasGlyphRunArray) {
1288 ConvertToGlyphRunArray();
1289 }
1290
1291 GlyphRun* glyphRun = mGlyphRunArray.AppendElement();
1292 if (!glyphRun) {
1293 if (mGlyphRunArray.Length() == 1) {
1294 ConvertFromGlyphRunArray();
1295 }
1296 return NS_ERROR_OUT_OF_MEMORY;
1297 }
1298 glyphRun->mFont = aFont;
1299 glyphRun->mCharacterOffset = aUTF16Offset;
1300 glyphRun->mMatchType = aMatchType;
1301 glyphRun->mOrientation = aOrientation;
1302
1303 return NS_OK;
1304 }
1305
SortGlyphRuns()1306 void gfxTextRun::SortGlyphRuns() {
1307 if (!mHasGlyphRunArray) {
1308 return;
1309 }
1310
1311 // We should never have an empty or one-element array here; if there's only
1312 // one glyphrun, it should be stored directly in the textrun without using
1313 // an array at all.
1314 MOZ_ASSERT(mGlyphRunArray.Length() > 1);
1315
1316 AutoTArray<GlyphRun, 16> runs(Move(mGlyphRunArray));
1317 GlyphRunOffsetComparator comp;
1318 runs.Sort(comp);
1319
1320 // Now copy back, coalescing adjacent glyph runs that have the same font
1321 mGlyphRunArray.Clear();
1322 gfxFont* prevFont = nullptr;
1323 gfx::ShapedTextFlags prevOrient = gfx::ShapedTextFlags();
1324 DebugOnly<uint32_t> prevOffset = 0;
1325 for (auto& run : runs) {
1326 // a GlyphRun with the same font and orientation as the previous can
1327 // just be skipped; the last GlyphRun will cover its character range.
1328 MOZ_ASSERT(run.mFont != nullptr);
1329 if (prevFont == nullptr || run.mFont != prevFont ||
1330 run.mOrientation != prevOrient) {
1331 // If two fonts have the same character offset, Sort() will have
1332 // randomized the order.
1333 MOZ_ASSERT(prevFont == nullptr || run.mCharacterOffset != prevOffset,
1334 "Two fonts for the same run, glyph indices unreliable");
1335 prevFont = run.mFont;
1336 prevOrient = run.mOrientation;
1337 #ifdef DEBUG
1338 prevOffset = run.mCharacterOffset;
1339 #endif
1340 if (!mGlyphRunArray.AppendElement(Move(run))) {
1341 NS_WARNING("Failed to append glyph run!");
1342 }
1343 }
1344 }
1345
1346 MOZ_ASSERT(mGlyphRunArray.Length() > 0);
1347 if (mGlyphRunArray.Length() == 1) {
1348 ConvertFromGlyphRunArray();
1349 }
1350 }
1351
1352 // Note that SanitizeGlyphRuns scans all glyph runs in the textrun;
1353 // therefore we only call it once, at the end of textrun construction,
1354 // NOT incrementally as each glyph run is added (bug 680402).
SanitizeGlyphRuns()1355 void gfxTextRun::SanitizeGlyphRuns() {
1356 if (!mHasGlyphRunArray) {
1357 return;
1358 }
1359
1360 MOZ_ASSERT(mGlyphRunArray.Length() > 1);
1361
1362 // If any glyph run starts with ligature-continuation characters, we need to
1363 // advance it to the first "real" character to avoid drawing partial ligature
1364 // glyphs from wrong font (seen with U+FEFF in reftest 474417-1, as Core Text
1365 // eliminates the glyph, which makes it appear as if a ligature has been
1366 // formed)
1367 int32_t i, lastRunIndex = mGlyphRunArray.Length() - 1;
1368 const CompressedGlyph* charGlyphs = mCharacterGlyphs;
1369 for (i = lastRunIndex; i >= 0; --i) {
1370 GlyphRun& run = mGlyphRunArray[i];
1371 while (charGlyphs[run.mCharacterOffset].IsLigatureContinuation() &&
1372 run.mCharacterOffset < GetLength()) {
1373 run.mCharacterOffset++;
1374 }
1375 // if the run has become empty, eliminate it
1376 if ((i < lastRunIndex &&
1377 run.mCharacterOffset >= mGlyphRunArray[i + 1].mCharacterOffset) ||
1378 (i == lastRunIndex && run.mCharacterOffset == GetLength())) {
1379 mGlyphRunArray.RemoveElementAt(i);
1380 --lastRunIndex;
1381 }
1382 }
1383
1384 MOZ_ASSERT(mGlyphRunArray.Length() > 0);
1385 if (mGlyphRunArray.Length() == 1) {
1386 ConvertFromGlyphRunArray();
1387 }
1388 }
1389
CountMissingGlyphs() const1390 uint32_t gfxTextRun::CountMissingGlyphs() const {
1391 uint32_t i;
1392 uint32_t count = 0;
1393 for (i = 0; i < GetLength(); ++i) {
1394 if (mCharacterGlyphs[i].IsMissing()) {
1395 ++count;
1396 }
1397 }
1398 return count;
1399 }
1400
CopyGlyphDataFrom(gfxShapedWord * aShapedWord,uint32_t aOffset)1401 void gfxTextRun::CopyGlyphDataFrom(gfxShapedWord* aShapedWord,
1402 uint32_t aOffset) {
1403 uint32_t wordLen = aShapedWord->GetLength();
1404 NS_ASSERTION(aOffset + wordLen <= GetLength(),
1405 "word overruns end of textrun!");
1406
1407 CompressedGlyph* charGlyphs = GetCharacterGlyphs();
1408 const CompressedGlyph* wordGlyphs = aShapedWord->GetCharacterGlyphs();
1409 if (aShapedWord->HasDetailedGlyphs()) {
1410 for (uint32_t i = 0; i < wordLen; ++i, ++aOffset) {
1411 const CompressedGlyph& g = wordGlyphs[i];
1412 if (g.IsSimpleGlyph()) {
1413 charGlyphs[aOffset] = g;
1414 } else {
1415 const DetailedGlyph* details =
1416 g.GetGlyphCount() > 0 ? aShapedWord->GetDetailedGlyphs(i) : nullptr;
1417 SetGlyphs(aOffset, g, details);
1418 }
1419 }
1420 } else {
1421 memcpy(charGlyphs + aOffset, wordGlyphs, wordLen * sizeof(CompressedGlyph));
1422 }
1423 }
1424
CopyGlyphDataFrom(gfxTextRun * aSource,Range aRange,uint32_t aDest)1425 void gfxTextRun::CopyGlyphDataFrom(gfxTextRun* aSource, Range aRange,
1426 uint32_t aDest) {
1427 NS_ASSERTION(aRange.end <= aSource->GetLength(),
1428 "Source substring out of range");
1429 NS_ASSERTION(aDest + aRange.Length() <= GetLength(),
1430 "Destination substring out of range");
1431
1432 if (aSource->mSkipDrawing) {
1433 mSkipDrawing = true;
1434 }
1435
1436 // Copy base glyph data, and DetailedGlyph data where present
1437 const CompressedGlyph* srcGlyphs = aSource->mCharacterGlyphs + aRange.start;
1438 CompressedGlyph* dstGlyphs = mCharacterGlyphs + aDest;
1439 for (uint32_t i = 0; i < aRange.Length(); ++i) {
1440 CompressedGlyph g = srcGlyphs[i];
1441 g.SetCanBreakBefore(!g.IsClusterStart()
1442 ? CompressedGlyph::FLAG_BREAK_TYPE_NONE
1443 : dstGlyphs[i].CanBreakBefore());
1444 if (!g.IsSimpleGlyph()) {
1445 uint32_t count = g.GetGlyphCount();
1446 if (count > 0) {
1447 DetailedGlyph* dst = AllocateDetailedGlyphs(i + aDest, count);
1448 if (dst) {
1449 DetailedGlyph* src = aSource->GetDetailedGlyphs(i + aRange.start);
1450 if (src) {
1451 ::memcpy(dst, src, count * sizeof(DetailedGlyph));
1452 } else {
1453 g.SetMissing(0);
1454 }
1455 } else {
1456 g.SetMissing(0);
1457 }
1458 }
1459 }
1460 dstGlyphs[i] = g;
1461 }
1462
1463 // Copy glyph runs
1464 GlyphRunIterator iter(aSource, aRange);
1465 #ifdef DEBUG
1466 const GlyphRun* prevRun = nullptr;
1467 #endif
1468 while (iter.NextRun()) {
1469 gfxFont* font = iter.GetGlyphRun()->mFont;
1470 NS_ASSERTION(!prevRun || prevRun->mFont != iter.GetGlyphRun()->mFont ||
1471 prevRun->mMatchType != iter.GetGlyphRun()->mMatchType ||
1472 prevRun->mOrientation != iter.GetGlyphRun()->mOrientation,
1473 "Glyphruns not coalesced?");
1474 #ifdef DEBUG
1475 prevRun = iter.GetGlyphRun();
1476 uint32_t end = iter.GetStringEnd();
1477 #endif
1478 uint32_t start = iter.GetStringStart();
1479
1480 // These used to be NS_ASSERTION()s, but WARNING is more appropriate.
1481 // Although it's unusual (and not desirable), it's possible for us to assign
1482 // different fonts to a base character and a following diacritic.
1483 // Example on OSX 10.5/10.6 with default fonts installed:
1484 // data:text/html,<p style="font-family:helvetica, arial, sans-serif;">
1485 // &%23x043E;&%23x0486;&%23x20;&%23x043E;&%23x0486;
1486 // This means the rendering of the cluster will probably not be very good,
1487 // but it's the best we can do for now if the specified font only covered
1488 // the initial base character and not its applied marks.
1489 NS_WARNING_ASSERTION(aSource->IsClusterStart(start),
1490 "Started font run in the middle of a cluster");
1491 NS_WARNING_ASSERTION(
1492 end == aSource->GetLength() || aSource->IsClusterStart(end),
1493 "Ended font run in the middle of a cluster");
1494
1495 nsresult rv = AddGlyphRun(font, iter.GetGlyphRun()->mMatchType,
1496 start - aRange.start + aDest, false,
1497 iter.GetGlyphRun()->mOrientation);
1498 if (NS_FAILED(rv)) return;
1499 }
1500 }
1501
ClearGlyphsAndCharacters()1502 void gfxTextRun::ClearGlyphsAndCharacters() {
1503 ResetGlyphRuns();
1504 memset(reinterpret_cast<char*>(mCharacterGlyphs), 0,
1505 mLength * sizeof(CompressedGlyph));
1506 mDetailedGlyphs = nullptr;
1507 }
1508
SetSpaceGlyph(gfxFont * aFont,DrawTarget * aDrawTarget,uint32_t aCharIndex,gfx::ShapedTextFlags aOrientation)1509 void gfxTextRun::SetSpaceGlyph(gfxFont* aFont, DrawTarget* aDrawTarget,
1510 uint32_t aCharIndex,
1511 gfx::ShapedTextFlags aOrientation) {
1512 if (SetSpaceGlyphIfSimple(aFont, aCharIndex, ' ', aOrientation)) {
1513 return;
1514 }
1515
1516 aFont->InitWordCache();
1517 static const uint8_t space = ' ';
1518 gfx::ShapedTextFlags flags =
1519 gfx::ShapedTextFlags::TEXT_IS_8BIT | aOrientation;
1520 bool vertical =
1521 !!(GetFlags() & gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT);
1522 gfxFontShaper::RoundingFlags roundingFlags =
1523 aFont->GetRoundOffsetsToPixels(aDrawTarget);
1524 gfxShapedWord* sw = aFont->GetShapedWord(
1525 aDrawTarget, &space, 1, gfxShapedWord::HashMix(0, ' '), Script::LATIN,
1526 vertical, mAppUnitsPerDevUnit, flags, roundingFlags, nullptr);
1527 if (sw) {
1528 AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false,
1529 aOrientation);
1530 CopyGlyphDataFrom(sw, aCharIndex);
1531 }
1532 }
1533
SetSpaceGlyphIfSimple(gfxFont * aFont,uint32_t aCharIndex,char16_t aSpaceChar,gfx::ShapedTextFlags aOrientation)1534 bool gfxTextRun::SetSpaceGlyphIfSimple(gfxFont* aFont, uint32_t aCharIndex,
1535 char16_t aSpaceChar,
1536 gfx::ShapedTextFlags aOrientation) {
1537 uint32_t spaceGlyph = aFont->GetSpaceGlyph();
1538 if (!spaceGlyph || !CompressedGlyph::IsSimpleGlyphID(spaceGlyph)) {
1539 return false;
1540 }
1541
1542 gfxFont::Orientation fontOrientation =
1543 (aOrientation & gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT)
1544 ? gfxFont::eVertical
1545 : gfxFont::eHorizontal;
1546 uint32_t spaceWidthAppUnits = NS_lroundf(
1547 aFont->GetMetrics(fontOrientation).spaceWidth * mAppUnitsPerDevUnit);
1548 if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) {
1549 return false;
1550 }
1551
1552 AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false, aOrientation);
1553 CompressedGlyph g =
1554 CompressedGlyph::MakeSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
1555 if (aSpaceChar == ' ') {
1556 g.SetIsSpace();
1557 }
1558 GetCharacterGlyphs()[aCharIndex] = g;
1559 return true;
1560 }
1561
FetchGlyphExtents(DrawTarget * aRefDrawTarget)1562 void gfxTextRun::FetchGlyphExtents(DrawTarget* aRefDrawTarget) {
1563 bool needsGlyphExtents = NeedsGlyphExtents(this);
1564 if (!needsGlyphExtents && !mDetailedGlyphs) return;
1565
1566 uint32_t runCount;
1567 const GlyphRun* glyphRuns = GetGlyphRuns(&runCount);
1568 CompressedGlyph* charGlyphs = mCharacterGlyphs;
1569 for (uint32_t i = 0; i < runCount; ++i) {
1570 const GlyphRun& run = glyphRuns[i];
1571 gfxFont* font = run.mFont;
1572 if (MOZ_UNLIKELY(font->GetStyle()->size == 0) ||
1573 MOZ_UNLIKELY(font->GetStyle()->sizeAdjust == 0.0f)) {
1574 continue;
1575 }
1576
1577 uint32_t start = run.mCharacterOffset;
1578 uint32_t end =
1579 i + 1 < runCount ? glyphRuns[i + 1].mCharacterOffset : GetLength();
1580 uint32_t j;
1581 gfxGlyphExtents* extents =
1582 font->GetOrCreateGlyphExtents(mAppUnitsPerDevUnit);
1583
1584 for (j = start; j < end; ++j) {
1585 const gfxTextRun::CompressedGlyph* glyphData = &charGlyphs[j];
1586 if (glyphData->IsSimpleGlyph()) {
1587 // If we're in speed mode, don't set up glyph extents here; we'll
1588 // just return "optimistic" glyph bounds later
1589 if (needsGlyphExtents) {
1590 uint32_t glyphIndex = glyphData->GetSimpleGlyph();
1591 if (!extents->IsGlyphKnown(glyphIndex)) {
1592 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
1593 ++gGlyphExtentsSetupEagerSimple;
1594 #endif
1595 font->SetupGlyphExtents(aRefDrawTarget, glyphIndex, false, extents);
1596 }
1597 }
1598 } else if (!glyphData->IsMissing()) {
1599 uint32_t glyphCount = glyphData->GetGlyphCount();
1600 if (glyphCount == 0) {
1601 continue;
1602 }
1603 const gfxTextRun::DetailedGlyph* details = GetDetailedGlyphs(j);
1604 if (!details) {
1605 continue;
1606 }
1607 for (uint32_t k = 0; k < glyphCount; ++k, ++details) {
1608 uint32_t glyphIndex = details->mGlyphID;
1609 if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) {
1610 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
1611 ++gGlyphExtentsSetupEagerTight;
1612 #endif
1613 font->SetupGlyphExtents(aRefDrawTarget, glyphIndex, true, extents);
1614 }
1615 }
1616 }
1617 }
1618 }
1619 }
1620
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)1621 size_t gfxTextRun::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) {
1622 // The second arg is how much gfxTextRun::AllocateStorage would have
1623 // allocated.
1624 size_t total = mHasGlyphRunArray
1625 ? mGlyphRunArray.ShallowSizeOfExcludingThis(aMallocSizeOf)
1626 : 0;
1627
1628 if (mDetailedGlyphs) {
1629 total += mDetailedGlyphs->SizeOfIncludingThis(aMallocSizeOf);
1630 }
1631
1632 return total;
1633 }
1634
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)1635 size_t gfxTextRun::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
1636 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
1637 }
1638
1639 #ifdef DEBUG
Dump(FILE * aOutput)1640 void gfxTextRun::Dump(FILE* aOutput) {
1641 if (!aOutput) {
1642 aOutput = stdout;
1643 }
1644
1645 fputc('[', aOutput);
1646 uint32_t numGlyphRuns;
1647 const GlyphRun* glyphRuns = GetGlyphRuns(&numGlyphRuns);
1648 for (uint32_t i = 0; i < numGlyphRuns; ++i) {
1649 if (i > 0) {
1650 fputc(',', aOutput);
1651 }
1652 gfxFont* font = glyphRuns[i].mFont;
1653 const gfxFontStyle* style = font->GetStyle();
1654 NS_ConvertUTF16toUTF8 fontName(font->GetName());
1655 nsAutoCString lang;
1656 style->language->ToUTF8String(lang);
1657 fprintf(aOutput, "%d: %s %f/%d/%d/%s", glyphRuns[i].mCharacterOffset,
1658 fontName.get(), style->size, style->weight, style->style,
1659 lang.get());
1660 }
1661 fputc(']', aOutput);
1662 }
1663 #endif
1664
gfxFontGroup(const FontFamilyList & aFontFamilyList,const gfxFontStyle * aStyle,gfxTextPerfMetrics * aTextPerf,gfxUserFontSet * aUserFontSet,gfxFloat aDevToCssSize)1665 gfxFontGroup::gfxFontGroup(const FontFamilyList& aFontFamilyList,
1666 const gfxFontStyle* aStyle,
1667 gfxTextPerfMetrics* aTextPerf,
1668 gfxUserFontSet* aUserFontSet, gfxFloat aDevToCssSize)
1669 : mFamilyList(aFontFamilyList),
1670 mStyle(*aStyle),
1671 mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET),
1672 mHyphenWidth(-1),
1673 mDevToCssSize(aDevToCssSize),
1674 mUserFontSet(aUserFontSet),
1675 mTextPerf(aTextPerf),
1676 mLastPrefLang(eFontPrefLang_Western),
1677 mPageLang(gfxPlatformFontList::GetFontPrefLangFor(aStyle->language)),
1678 mLastPrefFirstFont(false),
1679 mSkipDrawing(false) {
1680 // We don't use SetUserFontSet() here, as we want to unconditionally call
1681 // BuildFontList() rather than only do UpdateUserFonts() if it changed.
1682 mCurrGeneration = GetGeneration();
1683 BuildFontList();
1684 }
1685
~gfxFontGroup()1686 gfxFontGroup::~gfxFontGroup() {
1687 // Should not be dropped by stylo
1688 MOZ_ASSERT(NS_IsMainThread());
1689 }
1690
BuildFontList()1691 void gfxFontGroup::BuildFontList() {
1692 // initialize fonts in the font family list
1693 AutoTArray<gfxFontFamily*, 10> fonts;
1694 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
1695
1696 // lookup fonts in the fontlist
1697 for (const FontFamilyName& name : mFamilyList.GetFontlist()->mNames) {
1698 if (name.IsNamed()) {
1699 if (!name.mName.IsEmpty()) {
1700 AddPlatformFont(name.mName, fonts);
1701 } else {
1702 MOZ_ASSERT_UNREACHABLE("empty FontFamilyName!");
1703 }
1704 } else {
1705 pfl->AddGenericFonts(name.mType, mStyle.language, fonts);
1706 if (mTextPerf) {
1707 mTextPerf->current.genericLookups++;
1708 }
1709 }
1710 }
1711
1712 // if necessary, append default generic onto the end
1713 if (mFamilyList.GetDefaultFontType() != eFamily_none &&
1714 !mFamilyList.HasDefaultGeneric()) {
1715 pfl->AddGenericFonts(mFamilyList.GetDefaultFontType(), mStyle.language,
1716 fonts);
1717 if (mTextPerf) {
1718 mTextPerf->current.genericLookups++;
1719 }
1720 }
1721
1722 // build the fontlist from the specified families
1723 for (gfxFontFamily* fontFamily : fonts) {
1724 AddFamilyToFontList(fontFamily);
1725 }
1726 }
1727
AddPlatformFont(const nsAString & aName,nsTArray<gfxFontFamily * > & aFamilyList)1728 void gfxFontGroup::AddPlatformFont(const nsAString& aName,
1729 nsTArray<gfxFontFamily*>& aFamilyList) {
1730 // First, look up in the user font set...
1731 // If the fontSet matches the family, we must not look for a platform
1732 // font of the same name, even if we fail to actually get a fontEntry
1733 // here; we'll fall back to the next name in the CSS font-family list.
1734 if (mUserFontSet) {
1735 // Add userfonts to the fontlist whether already loaded
1736 // or not. Loading is initiated during font matching.
1737 gfxFontFamily* family = mUserFontSet->LookupFamily(aName);
1738 if (family) {
1739 aFamilyList.AppendElement(family);
1740 return;
1741 }
1742 }
1743
1744 // Not known in the user font set ==> check system fonts
1745 gfxPlatformFontList::PlatformFontList()->FindAndAddFamilies(
1746 aName, &aFamilyList, gfxPlatformFontList::FindFamiliesFlags(0), &mStyle,
1747 mDevToCssSize);
1748 }
1749
AddFamilyToFontList(gfxFontFamily * aFamily)1750 void gfxFontGroup::AddFamilyToFontList(gfxFontFamily* aFamily) {
1751 if (!aFamily) {
1752 MOZ_ASSERT_UNREACHABLE("don't try to add a null font family!");
1753 return;
1754 }
1755 AutoTArray<gfxFontEntry*, 4> fontEntryList;
1756 bool needsBold;
1757 aFamily->FindAllFontsForStyle(mStyle, fontEntryList, needsBold);
1758 // add these to the fontlist
1759 for (gfxFontEntry* fe : fontEntryList) {
1760 if (!HasFont(fe)) {
1761 FamilyFace ff(aFamily, fe, needsBold);
1762 if (fe->mIsUserFontContainer) {
1763 ff.CheckState(mSkipDrawing);
1764 }
1765 mFonts.AppendElement(ff);
1766 }
1767 }
1768 // for a family marked as "check fallback faces", only mark the last
1769 // entry so that fallbacks for a family are only checked once
1770 if (aFamily->CheckForFallbackFaces() && !fontEntryList.IsEmpty() &&
1771 !mFonts.IsEmpty()) {
1772 mFonts.LastElement().SetCheckForFallbackFaces();
1773 }
1774 }
1775
HasFont(const gfxFontEntry * aFontEntry)1776 bool gfxFontGroup::HasFont(const gfxFontEntry* aFontEntry) {
1777 uint32_t count = mFonts.Length();
1778 for (uint32_t i = 0; i < count; ++i) {
1779 if (mFonts[i].FontEntry() == aFontEntry) {
1780 return true;
1781 }
1782 }
1783 return false;
1784 }
1785
GetFontAt(int32_t i,uint32_t aCh)1786 gfxFont* gfxFontGroup::GetFontAt(int32_t i, uint32_t aCh) {
1787 if (uint32_t(i) >= mFonts.Length()) {
1788 return nullptr;
1789 }
1790
1791 FamilyFace& ff = mFonts[i];
1792 if (ff.IsInvalid() || ff.IsLoading()) {
1793 return nullptr;
1794 }
1795
1796 gfxFont* font = ff.Font();
1797 if (!font) {
1798 gfxFontEntry* fe = mFonts[i].FontEntry();
1799 gfxCharacterMap* unicodeRangeMap = nullptr;
1800 if (fe->mIsUserFontContainer) {
1801 gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
1802 if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED &&
1803 ufe->CharacterInUnicodeRange(aCh) &&
1804 !FontLoadingForFamily(ff.Family(), aCh)) {
1805 ufe->Load();
1806 ff.CheckState(mSkipDrawing);
1807 }
1808 fe = ufe->GetPlatformFontEntry();
1809 if (!fe) {
1810 return nullptr;
1811 }
1812 unicodeRangeMap = ufe->GetUnicodeRangeMap();
1813 }
1814 font = fe->FindOrMakeFont(&mStyle, mFonts[i].NeedsBold(), unicodeRangeMap);
1815 if (!font || !font->Valid()) {
1816 ff.SetInvalid();
1817 // We can't just |delete font| here, in case there are other
1818 // references to the object FindOrMakeFont returned.
1819 RefPtr<gfxFont> ref(font);
1820 return nullptr;
1821 }
1822 mFonts[i].SetFont(font);
1823 }
1824 return font;
1825 }
1826
CheckState(bool & aSkipDrawing)1827 void gfxFontGroup::FamilyFace::CheckState(bool& aSkipDrawing) {
1828 gfxFontEntry* fe = FontEntry();
1829 if (fe->mIsUserFontContainer) {
1830 gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
1831 gfxUserFontEntry::UserFontLoadState state = ufe->LoadState();
1832 switch (state) {
1833 case gfxUserFontEntry::STATUS_LOAD_PENDING:
1834 case gfxUserFontEntry::STATUS_LOADING:
1835 SetLoading(true);
1836 break;
1837 case gfxUserFontEntry::STATUS_FAILED:
1838 SetInvalid();
1839 // fall-thru to the default case
1840 MOZ_FALLTHROUGH;
1841 default:
1842 SetLoading(false);
1843 }
1844 if (ufe->WaitForUserFont()) {
1845 aSkipDrawing = true;
1846 }
1847 }
1848 }
1849
EqualsUserFont(const gfxUserFontEntry * aUserFont) const1850 bool gfxFontGroup::FamilyFace::EqualsUserFont(
1851 const gfxUserFontEntry* aUserFont) const {
1852 gfxFontEntry* fe = FontEntry();
1853 // if there's a font, the entry is the underlying platform font
1854 if (mFontCreated) {
1855 gfxFontEntry* pfe = aUserFont->GetPlatformFontEntry();
1856 if (pfe == fe) {
1857 return true;
1858 }
1859 } else if (fe == aUserFont) {
1860 return true;
1861 }
1862 return false;
1863 }
1864
FontLoadingForFamily(gfxFontFamily * aFamily,uint32_t aCh) const1865 bool gfxFontGroup::FontLoadingForFamily(gfxFontFamily* aFamily,
1866 uint32_t aCh) const {
1867 uint32_t count = mFonts.Length();
1868 for (uint32_t i = 0; i < count; ++i) {
1869 const FamilyFace& ff = mFonts[i];
1870 if (ff.IsLoading() && ff.Family() == aFamily) {
1871 const gfxUserFontEntry* ufe =
1872 static_cast<gfxUserFontEntry*>(ff.FontEntry());
1873 if (ufe->CharacterInUnicodeRange(aCh)) {
1874 return true;
1875 }
1876 }
1877 }
1878 return false;
1879 }
1880
GetDefaultFont()1881 gfxFont* gfxFontGroup::GetDefaultFont() {
1882 if (mDefaultFont) {
1883 return mDefaultFont.get();
1884 }
1885
1886 bool needsBold;
1887 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
1888 gfxFontFamily* defaultFamily = pfl->GetDefaultFont(&mStyle);
1889 NS_ASSERTION(defaultFamily,
1890 "invalid default font returned by GetDefaultFont");
1891
1892 if (defaultFamily) {
1893 gfxFontEntry* fe = defaultFamily->FindFontForStyle(mStyle, needsBold, true);
1894 if (fe) {
1895 mDefaultFont = fe->FindOrMakeFont(&mStyle, needsBold);
1896 }
1897 }
1898
1899 uint32_t numInits, loaderState;
1900 pfl->GetFontlistInitInfo(numInits, loaderState);
1901 NS_ASSERTION(numInits != 0,
1902 "must initialize system fontlist before getting default font!");
1903
1904 uint32_t numFonts = 0;
1905 if (!mDefaultFont) {
1906 // Try for a "font of last resort...."
1907 // Because an empty font list would be Really Bad for later code
1908 // that assumes it will be able to get valid metrics for layout,
1909 // just look for the first usable font and put in the list.
1910 // (see bug 554544)
1911 AutoTArray<RefPtr<gfxFontFamily>, 200> familyList;
1912 pfl->GetFontFamilyList(familyList);
1913 numFonts = familyList.Length();
1914 for (uint32_t i = 0; i < numFonts; ++i) {
1915 gfxFontEntry* fe =
1916 familyList[i]->FindFontForStyle(mStyle, needsBold, true);
1917 if (fe) {
1918 mDefaultFont = fe->FindOrMakeFont(&mStyle, needsBold);
1919 if (mDefaultFont) {
1920 break;
1921 }
1922 }
1923 }
1924 }
1925
1926 if (!mDefaultFont) {
1927 // an empty font list at this point is fatal; we're not going to
1928 // be able to do even the most basic layout operations
1929
1930 // annotate crash report with fontlist info
1931 nsAutoCString fontInitInfo;
1932 fontInitInfo.AppendPrintf("no fonts - init: %d fonts: %d loader: %d",
1933 numInits, numFonts, loaderState);
1934 #ifdef XP_WIN
1935 bool dwriteEnabled = gfxWindowsPlatform::GetPlatform()->DWriteEnabled();
1936 double upTime = (double)GetTickCount();
1937 fontInitInfo.AppendPrintf(" backend: %s system-uptime: %9.3f sec",
1938 dwriteEnabled ? "directwrite" : "gdi",
1939 upTime / 1000);
1940 #endif
1941 gfxCriticalError() << fontInitInfo.get();
1942
1943 char msg[256]; // CHECK buffer length if revising message below
1944 nsAutoString familiesString;
1945 mFamilyList.ToString(familiesString);
1946 SprintfLiteral(msg, "unable to find a usable font (%.220s)",
1947 NS_ConvertUTF16toUTF8(familiesString).get());
1948 MOZ_CRASH_UNSAFE_OOL(msg);
1949 }
1950
1951 return mDefaultFont.get();
1952 }
1953
GetFirstValidFont(uint32_t aCh)1954 gfxFont* gfxFontGroup::GetFirstValidFont(uint32_t aCh) {
1955 uint32_t count = mFonts.Length();
1956 for (uint32_t i = 0; i < count; ++i) {
1957 FamilyFace& ff = mFonts[i];
1958 if (ff.IsInvalid()) {
1959 continue;
1960 }
1961
1962 // already have a font?
1963 gfxFont* font = ff.Font();
1964 if (font) {
1965 return font;
1966 }
1967
1968 // Need to build a font, loading userfont if not loaded. In
1969 // cases where unicode range might apply, use the character
1970 // provided.
1971 if (ff.IsUserFontContainer()) {
1972 gfxUserFontEntry* ufe =
1973 static_cast<gfxUserFontEntry*>(mFonts[i].FontEntry());
1974 bool inRange = ufe->CharacterInUnicodeRange(aCh);
1975 if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED && inRange &&
1976 !FontLoadingForFamily(ff.Family(), aCh)) {
1977 ufe->Load();
1978 ff.CheckState(mSkipDrawing);
1979 }
1980 if (ufe->LoadState() != gfxUserFontEntry::STATUS_LOADED || !inRange) {
1981 continue;
1982 }
1983 }
1984
1985 font = GetFontAt(i, aCh);
1986 if (font) {
1987 return font;
1988 }
1989 }
1990 return GetDefaultFont();
1991 }
1992
GetFirstMathFont()1993 gfxFont* gfxFontGroup::GetFirstMathFont() {
1994 uint32_t count = mFonts.Length();
1995 for (uint32_t i = 0; i < count; ++i) {
1996 gfxFont* font = GetFontAt(i);
1997 if (font && font->TryGetMathTable()) {
1998 return font;
1999 }
2000 }
2001 return nullptr;
2002 }
2003
Copy(const gfxFontStyle * aStyle)2004 gfxFontGroup* gfxFontGroup::Copy(const gfxFontStyle* aStyle) {
2005 gfxFontGroup* fg = new gfxFontGroup(mFamilyList, aStyle, mTextPerf,
2006 mUserFontSet, mDevToCssSize);
2007 return fg;
2008 }
2009
IsInvalidChar(uint8_t ch)2010 bool gfxFontGroup::IsInvalidChar(uint8_t ch) {
2011 return ((ch & 0x7f) < 0x20 || ch == 0x7f);
2012 }
2013
IsInvalidChar(char16_t ch)2014 bool gfxFontGroup::IsInvalidChar(char16_t ch) {
2015 // All printable 7-bit ASCII values are OK
2016 if (ch >= ' ' && ch < 0x7f) {
2017 return false;
2018 }
2019 // No point in sending non-printing control chars through font shaping
2020 if (ch <= 0x9f) {
2021 return true;
2022 }
2023 // Word-separating format/bidi control characters are not shaped as part
2024 // of words.
2025 return (((ch & 0xFF00) == 0x2000 /* Unicode control character */ &&
2026 (ch == 0x200B /*ZWSP*/ || ch == 0x2028 /*LSEP*/ ||
2027 ch == 0x2029 /*PSEP*/ || ch == 0x2060 /*WJ*/)) ||
2028 ch == 0xfeff /*ZWNBSP*/ || IsBidiControl(ch));
2029 }
2030
MakeEmptyTextRun(const Parameters * aParams,gfx::ShapedTextFlags aFlags,nsTextFrameUtils::Flags aFlags2)2031 already_AddRefed<gfxTextRun> gfxFontGroup::MakeEmptyTextRun(
2032 const Parameters* aParams, gfx::ShapedTextFlags aFlags,
2033 nsTextFrameUtils::Flags aFlags2) {
2034 aFlags |= ShapedTextFlags::TEXT_IS_8BIT;
2035 return gfxTextRun::Create(aParams, 0, this, aFlags, aFlags2);
2036 }
2037
MakeSpaceTextRun(const Parameters * aParams,gfx::ShapedTextFlags aFlags,nsTextFrameUtils::Flags aFlags2)2038 already_AddRefed<gfxTextRun> gfxFontGroup::MakeSpaceTextRun(
2039 const Parameters* aParams, gfx::ShapedTextFlags aFlags,
2040 nsTextFrameUtils::Flags aFlags2) {
2041 aFlags |= ShapedTextFlags::TEXT_IS_8BIT;
2042
2043 RefPtr<gfxTextRun> textRun =
2044 gfxTextRun::Create(aParams, 1, this, aFlags, aFlags2);
2045 if (!textRun) {
2046 return nullptr;
2047 }
2048
2049 gfx::ShapedTextFlags orientation = aFlags & ShapedTextFlags::TEXT_ORIENT_MASK;
2050 if (orientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
2051 orientation = ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
2052 }
2053
2054 gfxFont* font = GetFirstValidFont();
2055 if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
2056 MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
2057 // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
2058 // them, and always create at least size 1 fonts, i.e. they still
2059 // render something for size 0 fonts.
2060 textRun->AddGlyphRun(font, gfxTextRange::kFontGroup, 0, false, orientation);
2061 } else {
2062 if (font->GetSpaceGlyph()) {
2063 // Normally, the font has a cached space glyph, so we can avoid
2064 // the cost of calling FindFontForChar.
2065 textRun->SetSpaceGlyph(font, aParams->mDrawTarget, 0, orientation);
2066 } else {
2067 // In case the primary font doesn't have <space> (bug 970891),
2068 // find one that does.
2069 uint8_t matchType;
2070 gfxFont* spaceFont =
2071 FindFontForChar(' ', 0, 0, Script::LATIN, nullptr, &matchType);
2072 if (spaceFont) {
2073 textRun->SetSpaceGlyph(spaceFont, aParams->mDrawTarget, 0, orientation);
2074 }
2075 }
2076 }
2077
2078 // Note that the gfxGlyphExtents glyph bounds storage for the font will
2079 // always contain an entry for the font's space glyph, so we don't have
2080 // to call FetchGlyphExtents here.
2081 return textRun.forget();
2082 }
2083
MakeBlankTextRun(uint32_t aLength,const Parameters * aParams,gfx::ShapedTextFlags aFlags,nsTextFrameUtils::Flags aFlags2)2084 already_AddRefed<gfxTextRun> gfxFontGroup::MakeBlankTextRun(
2085 uint32_t aLength, const Parameters* aParams, gfx::ShapedTextFlags aFlags,
2086 nsTextFrameUtils::Flags aFlags2) {
2087 RefPtr<gfxTextRun> textRun =
2088 gfxTextRun::Create(aParams, aLength, this, aFlags, aFlags2);
2089 if (!textRun) {
2090 return nullptr;
2091 }
2092
2093 gfx::ShapedTextFlags orientation = aFlags & ShapedTextFlags::TEXT_ORIENT_MASK;
2094 if (orientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
2095 orientation = ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
2096 }
2097 textRun->AddGlyphRun(GetFirstValidFont(), gfxTextRange::kFontGroup, 0, false,
2098 orientation);
2099 return textRun.forget();
2100 }
2101
MakeHyphenTextRun(DrawTarget * aDrawTarget,uint32_t aAppUnitsPerDevUnit)2102 already_AddRefed<gfxTextRun> gfxFontGroup::MakeHyphenTextRun(
2103 DrawTarget* aDrawTarget, uint32_t aAppUnitsPerDevUnit) {
2104 // only use U+2010 if it is supported by the first font in the group;
2105 // it's better to use ASCII '-' from the primary font than to fall back to
2106 // U+2010 from some other, possibly poorly-matching face
2107 static const char16_t hyphen = 0x2010;
2108 gfxFont* font = GetFirstValidFont(uint32_t(hyphen));
2109 if (font->HasCharacter(hyphen)) {
2110 return MakeTextRun(&hyphen, 1, aDrawTarget, aAppUnitsPerDevUnit,
2111 ShapedTextFlags(), nsTextFrameUtils::Flags(), nullptr);
2112 }
2113
2114 static const uint8_t dash = '-';
2115 return MakeTextRun(&dash, 1, aDrawTarget, aAppUnitsPerDevUnit,
2116 ShapedTextFlags(), nsTextFrameUtils::Flags(), nullptr);
2117 }
2118
GetHyphenWidth(const gfxTextRun::PropertyProvider * aProvider)2119 gfxFloat gfxFontGroup::GetHyphenWidth(
2120 const gfxTextRun::PropertyProvider* aProvider) {
2121 if (mHyphenWidth < 0) {
2122 RefPtr<DrawTarget> dt(aProvider->GetDrawTarget());
2123 if (dt) {
2124 RefPtr<gfxTextRun> hyphRun(
2125 MakeHyphenTextRun(dt, aProvider->GetAppUnitsPerDevUnit()));
2126 mHyphenWidth = hyphRun.get() ? hyphRun->GetAdvanceWidth() : 0;
2127 }
2128 }
2129 return mHyphenWidth;
2130 }
2131
MakeTextRun(const uint8_t * aString,uint32_t aLength,const Parameters * aParams,gfx::ShapedTextFlags aFlags,nsTextFrameUtils::Flags aFlags2,gfxMissingFontRecorder * aMFR)2132 already_AddRefed<gfxTextRun> gfxFontGroup::MakeTextRun(
2133 const uint8_t* aString, uint32_t aLength, const Parameters* aParams,
2134 gfx::ShapedTextFlags aFlags, nsTextFrameUtils::Flags aFlags2,
2135 gfxMissingFontRecorder* aMFR) {
2136 if (aLength == 0) {
2137 return MakeEmptyTextRun(aParams, aFlags, aFlags2);
2138 }
2139 if (aLength == 1 && aString[0] == ' ') {
2140 return MakeSpaceTextRun(aParams, aFlags, aFlags2);
2141 }
2142
2143 aFlags |= ShapedTextFlags::TEXT_IS_8BIT;
2144
2145 if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
2146 MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
2147 // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
2148 // them, and always create at least size 1 fonts, i.e. they still
2149 // render something for size 0 fonts.
2150 return MakeBlankTextRun(aLength, aParams, aFlags, aFlags2);
2151 }
2152
2153 RefPtr<gfxTextRun> textRun =
2154 gfxTextRun::Create(aParams, aLength, this, aFlags, aFlags2);
2155 if (!textRun) {
2156 return nullptr;
2157 }
2158
2159 InitTextRun(aParams->mDrawTarget, textRun.get(), aString, aLength, aMFR);
2160
2161 textRun->FetchGlyphExtents(aParams->mDrawTarget);
2162
2163 return textRun.forget();
2164 }
2165
MakeTextRun(const char16_t * aString,uint32_t aLength,const Parameters * aParams,gfx::ShapedTextFlags aFlags,nsTextFrameUtils::Flags aFlags2,gfxMissingFontRecorder * aMFR)2166 already_AddRefed<gfxTextRun> gfxFontGroup::MakeTextRun(
2167 const char16_t* aString, uint32_t aLength, const Parameters* aParams,
2168 gfx::ShapedTextFlags aFlags, nsTextFrameUtils::Flags aFlags2,
2169 gfxMissingFontRecorder* aMFR) {
2170 if (aLength == 0) {
2171 return MakeEmptyTextRun(aParams, aFlags, aFlags2);
2172 }
2173 if (aLength == 1 && aString[0] == ' ') {
2174 return MakeSpaceTextRun(aParams, aFlags, aFlags2);
2175 }
2176 if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
2177 MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
2178 return MakeBlankTextRun(aLength, aParams, aFlags, aFlags2);
2179 }
2180
2181 RefPtr<gfxTextRun> textRun =
2182 gfxTextRun::Create(aParams, aLength, this, aFlags, aFlags2);
2183 if (!textRun) {
2184 return nullptr;
2185 }
2186
2187 InitTextRun(aParams->mDrawTarget, textRun.get(), aString, aLength, aMFR);
2188
2189 textRun->FetchGlyphExtents(aParams->mDrawTarget);
2190
2191 return textRun.forget();
2192 }
2193
2194 template <typename T>
InitTextRun(DrawTarget * aDrawTarget,gfxTextRun * aTextRun,const T * aString,uint32_t aLength,gfxMissingFontRecorder * aMFR)2195 void gfxFontGroup::InitTextRun(DrawTarget* aDrawTarget, gfxTextRun* aTextRun,
2196 const T* aString, uint32_t aLength,
2197 gfxMissingFontRecorder* aMFR) {
2198 NS_ASSERTION(aLength > 0, "don't call InitTextRun for a zero-length run");
2199
2200 // we need to do numeral processing even on 8-bit text,
2201 // in case we're converting Western to Hindi/Arabic digits
2202 int32_t numOption = gfxPlatform::GetPlatform()->GetBidiNumeralOption();
2203 UniquePtr<char16_t[]> transformedString;
2204 if (numOption != IBMBIDI_NUMERAL_NOMINAL) {
2205 // scan the string for numerals that may need to be transformed;
2206 // if we find any, we'll make a local copy here and use that for
2207 // font matching and glyph generation/shaping
2208 bool prevIsArabic =
2209 !!(aTextRun->GetFlags() & ShapedTextFlags::TEXT_INCOMING_ARABICCHAR);
2210 for (uint32_t i = 0; i < aLength; ++i) {
2211 char16_t origCh = aString[i];
2212 char16_t newCh = HandleNumberInChar(origCh, prevIsArabic, numOption);
2213 if (newCh != origCh) {
2214 if (!transformedString) {
2215 transformedString = MakeUnique<char16_t[]>(aLength);
2216 if (sizeof(T) == sizeof(char16_t)) {
2217 memcpy(transformedString.get(), aString, i * sizeof(char16_t));
2218 } else {
2219 for (uint32_t j = 0; j < i; ++j) {
2220 transformedString[j] = aString[j];
2221 }
2222 }
2223 }
2224 }
2225 if (transformedString) {
2226 transformedString[i] = newCh;
2227 }
2228 prevIsArabic = IS_ARABIC_CHAR(newCh);
2229 }
2230 }
2231
2232 LogModule* log = mStyle.systemFont ? gfxPlatform::GetLog(eGfxLog_textrunui)
2233 : gfxPlatform::GetLog(eGfxLog_textrun);
2234
2235 // variant fallback handling may end up passing through this twice
2236 bool redo;
2237 do {
2238 redo = false;
2239
2240 if (sizeof(T) == sizeof(uint8_t) && !transformedString) {
2241 if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
2242 nsAutoCString lang;
2243 mStyle.language->ToUTF8String(lang);
2244 nsAutoString families;
2245 mFamilyList.ToString(families);
2246 nsAutoCString str((const char*)aString, aLength);
2247 MOZ_LOG(log, LogLevel::Warning,
2248 ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
2249 "len %d weight: %d width: %d style: %s size: %6.2f %zu-byte "
2250 "TEXTRUN [%s] ENDTEXTRUN\n",
2251 (mStyle.systemFont ? "textrunui" : "textrun"),
2252 NS_ConvertUTF16toUTF8(families).get(),
2253 (mFamilyList.GetDefaultFontType() == eFamily_serif
2254 ? "serif"
2255 : (mFamilyList.GetDefaultFontType() == eFamily_sans_serif
2256 ? "sans-serif"
2257 : "none")),
2258 lang.get(), static_cast<int>(Script::LATIN), aLength,
2259 uint32_t(mStyle.weight), uint32_t(mStyle.stretch),
2260 (mStyle.style & NS_FONT_STYLE_ITALIC
2261 ? "italic"
2262 : (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique"
2263 : "normal")),
2264 mStyle.size, sizeof(T), str.get()));
2265 }
2266
2267 // the text is still purely 8-bit; bypass the script-run itemizer
2268 // and treat it as a single Latin run
2269 InitScriptRun(aDrawTarget, aTextRun, aString, 0, aLength, Script::LATIN,
2270 aMFR);
2271 } else {
2272 const char16_t* textPtr;
2273 if (transformedString) {
2274 textPtr = transformedString.get();
2275 } else {
2276 // typecast to avoid compilation error for the 8-bit version,
2277 // even though this is dead code in that case
2278 textPtr = reinterpret_cast<const char16_t*>(aString);
2279 }
2280
2281 // split into script runs so that script can potentially influence
2282 // the font matching process below
2283 gfxScriptItemizer scriptRuns(textPtr, aLength);
2284
2285 uint32_t runStart = 0, runLimit = aLength;
2286 Script runScript = Script::LATIN;
2287 while (scriptRuns.Next(runStart, runLimit, runScript)) {
2288 if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
2289 nsAutoCString lang;
2290 mStyle.language->ToUTF8String(lang);
2291 nsAutoString families;
2292 mFamilyList.ToString(families);
2293 uint32_t runLen = runLimit - runStart;
2294 MOZ_LOG(
2295 log, LogLevel::Warning,
2296 ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
2297 "len %d weight: %d width: %d style: %s size: %6.2f "
2298 "%zu-byte TEXTRUN [%s] ENDTEXTRUN\n",
2299 (mStyle.systemFont ? "textrunui" : "textrun"),
2300 NS_ConvertUTF16toUTF8(families).get(),
2301 (mFamilyList.GetDefaultFontType() == eFamily_serif
2302 ? "serif"
2303 : (mFamilyList.GetDefaultFontType() == eFamily_sans_serif
2304 ? "sans-serif"
2305 : "none")),
2306 lang.get(), static_cast<int>(runScript), runLen,
2307 uint32_t(mStyle.weight), uint32_t(mStyle.stretch),
2308 (mStyle.style & NS_FONT_STYLE_ITALIC
2309 ? "italic"
2310 : (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique"
2311 : "normal")),
2312 mStyle.size, sizeof(T),
2313 NS_ConvertUTF16toUTF8(textPtr + runStart, runLen).get()));
2314 }
2315
2316 InitScriptRun(aDrawTarget, aTextRun, textPtr + runStart, runStart,
2317 runLimit - runStart, runScript, aMFR);
2318 }
2319 }
2320
2321 // if shaping was aborted due to lack of feature support, clear out
2322 // glyph runs and redo shaping with fallback forced on
2323 if (aTextRun->GetShapingState() == gfxTextRun::eShapingState_Aborted) {
2324 redo = true;
2325 aTextRun->SetShapingState(gfxTextRun::eShapingState_ForceFallbackFeature);
2326 aTextRun->ClearGlyphsAndCharacters();
2327 }
2328
2329 } while (redo);
2330
2331 if (sizeof(T) == sizeof(char16_t) && aLength > 0) {
2332 gfxTextRun::CompressedGlyph* glyph = aTextRun->GetCharacterGlyphs();
2333 if (!glyph->IsSimpleGlyph()) {
2334 glyph->SetClusterStart(true);
2335 }
2336 }
2337
2338 // It's possible for CoreText to omit glyph runs if it decides they contain
2339 // only invisibles (e.g., U+FEFF, see reftest 474417-1). In this case, we
2340 // need to eliminate them from the glyph run array to avoid drawing "partial
2341 // ligatures" with the wrong font.
2342 // We don't do this during InitScriptRun (or gfxFont::InitTextRun) because
2343 // it will iterate back over all glyphruns in the textrun, which leads to
2344 // pathologically-bad perf in the case where a textrun contains many script
2345 // changes (see bug 680402) - we'd end up re-sanitizing all the earlier runs
2346 // every time a new script subrun is processed.
2347 aTextRun->SanitizeGlyphRuns();
2348
2349 aTextRun->SortGlyphRuns();
2350 }
2351
IsPUA(uint32_t aUSV)2352 static inline bool IsPUA(uint32_t aUSV) {
2353 // We could look up the General Category of the codepoint here,
2354 // but it's simpler to check PUA codepoint ranges.
2355 return (aUSV >= 0xE000 && aUSV <= 0xF8FF) || (aUSV >= 0xF0000);
2356 }
2357
2358 template <typename T>
InitScriptRun(DrawTarget * aDrawTarget,gfxTextRun * aTextRun,const T * aString,uint32_t aOffset,uint32_t aLength,Script aRunScript,gfxMissingFontRecorder * aMFR)2359 void gfxFontGroup::InitScriptRun(
2360 DrawTarget* aDrawTarget, gfxTextRun* aTextRun,
2361 const T* aString, // text for this script run,
2362 // not the entire textrun
2363 uint32_t aOffset, // position of the script run
2364 // within the textrun
2365 uint32_t aLength, // length of the script run
2366 Script aRunScript, gfxMissingFontRecorder* aMFR) {
2367 NS_ASSERTION(aLength > 0, "don't call InitScriptRun for a 0-length run");
2368 NS_ASSERTION(aTextRun->GetShapingState() != gfxTextRun::eShapingState_Aborted,
2369 "don't call InitScriptRun with aborted shaping state");
2370
2371 // confirm the load state of userfonts in the list
2372 if (mUserFontSet && mCurrGeneration != mUserFontSet->GetGeneration()) {
2373 UpdateUserFonts();
2374 }
2375
2376 gfxFont* mainFont = GetFirstValidFont();
2377
2378 ShapedTextFlags orientation =
2379 aTextRun->GetFlags() & ShapedTextFlags::TEXT_ORIENT_MASK;
2380
2381 if (orientation != ShapedTextFlags::TEXT_ORIENT_HORIZONTAL &&
2382 (aRunScript == Script::MONGOLIAN || aRunScript == Script::PHAGS_PA)) {
2383 // Mongolian and Phags-pa text should ignore text-orientation and
2384 // always render in its "native" vertical mode, implemented by fonts
2385 // as sideways-right (i.e as if shaped horizontally, and then the
2386 // entire line is rotated to render vertically). Therefore, we ignore
2387 // the aOrientation value from the textrun's flags, and make all
2388 // vertical Mongolian/Phags-pa use sideways-right.
2389 orientation = ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
2390 }
2391
2392 uint32_t runStart = 0;
2393 AutoTArray<gfxTextRange, 3> fontRanges;
2394 ComputeRanges(fontRanges, aString, aLength, aRunScript, orientation);
2395 uint32_t numRanges = fontRanges.Length();
2396 bool missingChars = false;
2397
2398 for (uint32_t r = 0; r < numRanges; r++) {
2399 const gfxTextRange& range = fontRanges[r];
2400 uint32_t matchedLength = range.Length();
2401 gfxFont* matchedFont = range.font;
2402 // create the glyph run for this range
2403 if (matchedFont && mStyle.noFallbackVariantFeatures) {
2404 // common case - just do glyph layout and record the
2405 // resulting positioned glyphs
2406 aTextRun->AddGlyphRun(matchedFont, range.matchType, aOffset + runStart,
2407 (matchedLength > 0), range.orientation);
2408 if (!matchedFont->SplitAndInitTextRun(
2409 aDrawTarget, aTextRun, aString + runStart, aOffset + runStart,
2410 matchedLength, aRunScript, range.orientation)) {
2411 // glyph layout failed! treat as missing glyphs
2412 matchedFont = nullptr;
2413 }
2414 } else if (matchedFont) {
2415 // shape with some variant feature that requires fallback handling
2416 bool petiteToSmallCaps = false;
2417 bool syntheticLower = false;
2418 bool syntheticUpper = false;
2419
2420 if (mStyle.variantSubSuper != NS_FONT_VARIANT_POSITION_NORMAL &&
2421 (aTextRun->GetShapingState() ==
2422 gfxTextRun::eShapingState_ForceFallbackFeature ||
2423 !matchedFont->SupportsSubSuperscript(mStyle.variantSubSuper, aString,
2424 aLength, aRunScript))) {
2425 // fallback for subscript/superscript variant glyphs
2426
2427 // if the feature was already used, abort and force
2428 // fallback across the entire textrun
2429 gfxTextRun::ShapingState ss = aTextRun->GetShapingState();
2430
2431 if (ss == gfxTextRun::eShapingState_Normal) {
2432 aTextRun->SetShapingState(
2433 gfxTextRun::eShapingState_ShapingWithFallback);
2434 } else if (ss == gfxTextRun::eShapingState_ShapingWithFeature) {
2435 aTextRun->SetShapingState(gfxTextRun::eShapingState_Aborted);
2436 return;
2437 }
2438
2439 RefPtr<gfxFont> subSuperFont = matchedFont->GetSubSuperscriptFont(
2440 aTextRun->GetAppUnitsPerDevUnit());
2441 aTextRun->AddGlyphRun(subSuperFont, range.matchType, aOffset + runStart,
2442 (matchedLength > 0), range.orientation);
2443 if (!subSuperFont->SplitAndInitTextRun(
2444 aDrawTarget, aTextRun, aString + runStart, aOffset + runStart,
2445 matchedLength, aRunScript, range.orientation)) {
2446 // glyph layout failed! treat as missing glyphs
2447 matchedFont = nullptr;
2448 }
2449 } else if (mStyle.variantCaps != NS_FONT_VARIANT_CAPS_NORMAL &&
2450 !matchedFont->SupportsVariantCaps(
2451 aRunScript, mStyle.variantCaps, petiteToSmallCaps,
2452 syntheticLower, syntheticUpper)) {
2453 // fallback for small-caps variant glyphs
2454 if (!matchedFont->InitFakeSmallCapsRun(
2455 aDrawTarget, aTextRun, aString + runStart, aOffset + runStart,
2456 matchedLength, range.matchType, range.orientation, aRunScript,
2457 syntheticLower, syntheticUpper)) {
2458 matchedFont = nullptr;
2459 }
2460 } else {
2461 // shape normally with variant feature enabled
2462 gfxTextRun::ShapingState ss = aTextRun->GetShapingState();
2463
2464 // adjust the shaping state if necessary
2465 if (ss == gfxTextRun::eShapingState_Normal) {
2466 aTextRun->SetShapingState(
2467 gfxTextRun::eShapingState_ShapingWithFeature);
2468 } else if (ss == gfxTextRun::eShapingState_ShapingWithFallback) {
2469 // already have shaping results using fallback, need to redo
2470 aTextRun->SetShapingState(gfxTextRun::eShapingState_Aborted);
2471 return;
2472 }
2473
2474 // do glyph layout and record the resulting positioned glyphs
2475 aTextRun->AddGlyphRun(matchedFont, range.matchType, aOffset + runStart,
2476 (matchedLength > 0), range.orientation);
2477 if (!matchedFont->SplitAndInitTextRun(
2478 aDrawTarget, aTextRun, aString + runStart, aOffset + runStart,
2479 matchedLength, aRunScript, range.orientation)) {
2480 // glyph layout failed! treat as missing glyphs
2481 matchedFont = nullptr;
2482 }
2483 }
2484 } else {
2485 aTextRun->AddGlyphRun(mainFont, gfxTextRange::kFontGroup,
2486 aOffset + runStart, (matchedLength > 0),
2487 range.orientation);
2488 }
2489
2490 if (!matchedFont) {
2491 // We need to set cluster boundaries (and mark spaces) so that
2492 // surrogate pairs, combining characters, etc behave properly,
2493 // even if we don't have glyphs for them
2494 aTextRun->SetupClusterBoundaries(aOffset + runStart, aString + runStart,
2495 matchedLength);
2496
2497 // various "missing" characters may need special handling,
2498 // so we check for them here
2499 uint32_t runLimit = runStart + matchedLength;
2500 for (uint32_t index = runStart; index < runLimit; index++) {
2501 T ch = aString[index];
2502
2503 // tab and newline are not to be displayed as hexboxes,
2504 // but do need to be recorded in the textrun
2505 if (ch == '\n') {
2506 aTextRun->SetIsNewline(aOffset + index);
2507 continue;
2508 }
2509 if (ch == '\t') {
2510 aTextRun->SetIsTab(aOffset + index);
2511 continue;
2512 }
2513
2514 // for 16-bit textruns only, check for surrogate pairs and
2515 // special Unicode spaces; omit these checks in 8-bit runs
2516 if (sizeof(T) == sizeof(char16_t)) {
2517 if (NS_IS_HIGH_SURROGATE(ch) && index + 1 < aLength &&
2518 NS_IS_LOW_SURROGATE(aString[index + 1])) {
2519 uint32_t usv = SURROGATE_TO_UCS4(ch, aString[index + 1]);
2520 aTextRun->SetMissingGlyph(aOffset + index, usv, mainFont);
2521 index++;
2522 if (!mSkipDrawing && !IsPUA(usv)) {
2523 missingChars = true;
2524 }
2525 continue;
2526 }
2527
2528 // check if this is a known Unicode whitespace character that
2529 // we can render using the space glyph with a custom width
2530 gfxFloat wid = mainFont->SynthesizeSpaceWidth(ch);
2531 if (wid >= 0.0) {
2532 nscoord advance =
2533 aTextRun->GetAppUnitsPerDevUnit() * floor(wid + 0.5);
2534 if (gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance)) {
2535 aTextRun->GetCharacterGlyphs()[aOffset + index].SetSimpleGlyph(
2536 advance, mainFont->GetSpaceGlyph());
2537 } else {
2538 gfxTextRun::DetailedGlyph detailedGlyph;
2539 detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph();
2540 detailedGlyph.mAdvance = advance;
2541 CompressedGlyph g = CompressedGlyph::MakeComplex(true, true, 1);
2542 aTextRun->SetGlyphs(aOffset + index, g, &detailedGlyph);
2543 }
2544 continue;
2545 }
2546 }
2547
2548 if (IsInvalidChar(ch)) {
2549 // invalid chars are left as zero-width/invisible
2550 continue;
2551 }
2552
2553 // record char code so we can draw a box with the Unicode value
2554 aTextRun->SetMissingGlyph(aOffset + index, ch, mainFont);
2555 if (!mSkipDrawing && !IsPUA(ch)) {
2556 missingChars = true;
2557 }
2558 }
2559 }
2560
2561 runStart += matchedLength;
2562 }
2563
2564 if (aMFR && missingChars) {
2565 aMFR->RecordScript(aRunScript);
2566 }
2567 }
2568
GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel,gfx::ShapedTextFlags aFlags,LazyReferenceDrawTargetGetter & aRefDrawTargetGetter)2569 gfxTextRun* gfxFontGroup::GetEllipsisTextRun(
2570 int32_t aAppUnitsPerDevPixel, gfx::ShapedTextFlags aFlags,
2571 LazyReferenceDrawTargetGetter& aRefDrawTargetGetter) {
2572 MOZ_ASSERT(!(aFlags & ~ShapedTextFlags::TEXT_ORIENT_MASK),
2573 "flags here should only be used to specify orientation");
2574 if (mCachedEllipsisTextRun &&
2575 (mCachedEllipsisTextRun->GetFlags() &
2576 ShapedTextFlags::TEXT_ORIENT_MASK) == aFlags &&
2577 mCachedEllipsisTextRun->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel) {
2578 return mCachedEllipsisTextRun.get();
2579 }
2580
2581 // Use a Unicode ellipsis if the font supports it,
2582 // otherwise use three ASCII periods as fallback.
2583 gfxFont* firstFont = GetFirstValidFont(uint32_t(kEllipsisChar[0]));
2584 nsString ellipsis =
2585 firstFont->HasCharacter(kEllipsisChar[0])
2586 ? nsDependentString(kEllipsisChar, ArrayLength(kEllipsisChar) - 1)
2587 : nsDependentString(kASCIIPeriodsChar,
2588 ArrayLength(kASCIIPeriodsChar) - 1);
2589
2590 RefPtr<DrawTarget> refDT = aRefDrawTargetGetter.GetRefDrawTarget();
2591 Parameters params = {refDT, nullptr, nullptr,
2592 nullptr, 0, aAppUnitsPerDevPixel};
2593 mCachedEllipsisTextRun =
2594 MakeTextRun(ellipsis.get(), ellipsis.Length(), ¶ms, aFlags,
2595 nsTextFrameUtils::Flags(), nullptr);
2596 if (!mCachedEllipsisTextRun) {
2597 return nullptr;
2598 }
2599 // don't let the presence of a cached ellipsis textrun prolong the
2600 // fontgroup's life
2601 mCachedEllipsisTextRun->ReleaseFontGroup();
2602 return mCachedEllipsisTextRun.get();
2603 }
2604
FindFallbackFaceForChar(gfxFontFamily * aFamily,uint32_t aCh)2605 gfxFont* gfxFontGroup::FindFallbackFaceForChar(gfxFontFamily* aFamily,
2606 uint32_t aCh) {
2607 GlobalFontMatch data(aCh, &mStyle);
2608 aFamily->SearchAllFontsForChar(&data);
2609 gfxFontEntry* fe = data.mBestMatch;
2610 if (!fe) {
2611 return nullptr;
2612 }
2613
2614 bool needsBold =
2615 mStyle.weight >= 600 && !fe->IsBold() && mStyle.allowSyntheticWeight;
2616 return fe->FindOrMakeFont(&mStyle, needsBold);
2617 }
2618
GetUnderlineOffset()2619 gfxFloat gfxFontGroup::GetUnderlineOffset() {
2620 if (mUnderlineOffset == UNDERLINE_OFFSET_NOT_SET) {
2621 // if the fontlist contains a bad underline font, make the underline
2622 // offset the min of the first valid font and bad font underline offsets
2623 uint32_t len = mFonts.Length();
2624 for (uint32_t i = 0; i < len; i++) {
2625 FamilyFace& ff = mFonts[i];
2626 if (!ff.IsUserFontContainer() && !ff.FontEntry()->IsUserFont() &&
2627 ff.Family() && ff.Family()->IsBadUnderlineFamily()) {
2628 gfxFont* font = GetFontAt(i);
2629 if (!font) {
2630 continue;
2631 }
2632 gfxFloat bad = font->GetMetrics(gfxFont::eHorizontal).underlineOffset;
2633 gfxFloat first = GetFirstValidFont()
2634 ->GetMetrics(gfxFont::eHorizontal)
2635 .underlineOffset;
2636 mUnderlineOffset = std::min(first, bad);
2637 return mUnderlineOffset;
2638 }
2639 }
2640
2641 // no bad underline fonts, use the first valid font's metric
2642 mUnderlineOffset =
2643 GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal).underlineOffset;
2644 }
2645
2646 return mUnderlineOffset;
2647 }
2648
2649 #define NARROW_NO_BREAK_SPACE 0x202fu
2650
FindFontForChar(uint32_t aCh,uint32_t aPrevCh,uint32_t aNextCh,Script aRunScript,gfxFont * aPrevMatchedFont,uint8_t * aMatchType)2651 gfxFont* gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
2652 uint32_t aNextCh, Script aRunScript,
2653 gfxFont* aPrevMatchedFont,
2654 uint8_t* aMatchType) {
2655 // If the char is a cluster extender, we want to use the same font as the
2656 // preceding character if possible. This is preferable to using the font
2657 // group because it avoids breaks in shaping within a cluster.
2658 if (aPrevMatchedFont && IsClusterExtender(aCh) &&
2659 aPrevMatchedFont->HasCharacter(aCh)) {
2660 return aPrevMatchedFont;
2661 }
2662
2663 // Special cases for NNBSP (as used in Mongolian):
2664 if (aCh == NARROW_NO_BREAK_SPACE) {
2665 // If there is no preceding character, try the font that we'd use
2666 // for the next char (unless it's just another NNBSP; we don't try
2667 // to look ahead through a whole run of them).
2668 if (!aPrevCh && aNextCh && aNextCh != NARROW_NO_BREAK_SPACE) {
2669 gfxFont* nextFont = FindFontForChar(aNextCh, 0, 0, aRunScript,
2670 aPrevMatchedFont, aMatchType);
2671 if (nextFont && nextFont->HasCharacter(aCh)) {
2672 return nextFont;
2673 }
2674 }
2675 // Otherwise, treat NNBSP like a cluster extender (as above) and try
2676 // to continue the preceding font run.
2677 if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
2678 return aPrevMatchedFont;
2679 }
2680 }
2681
2682 // To optimize common cases, try the first font in the font-group
2683 // before going into the more detailed checks below
2684 uint32_t nextIndex = 0;
2685 bool isJoinControl = gfxFontUtils::IsJoinControl(aCh);
2686 bool wasJoinCauser = gfxFontUtils::IsJoinCauser(aPrevCh);
2687 bool isVarSelector = gfxFontUtils::IsVarSelector(aCh);
2688
2689 if (!isJoinControl && !wasJoinCauser && !isVarSelector) {
2690 gfxFont* firstFont = GetFontAt(0, aCh);
2691 if (firstFont) {
2692 if (firstFont->HasCharacter(aCh)) {
2693 *aMatchType = gfxTextRange::kFontGroup;
2694 return firstFont;
2695 }
2696
2697 gfxFont* font = nullptr;
2698 if (mFonts[0].CheckForFallbackFaces()) {
2699 font = FindFallbackFaceForChar(mFonts[0].Family(), aCh);
2700 } else if (!firstFont->GetFontEntry()->IsUserFont()) {
2701 // For platform fonts (but not userfonts), we may need to do
2702 // fallback within the family to handle cases where some faces
2703 // such as Italic or Black have reduced character sets compared
2704 // to the family's Regular face.
2705 font = FindFallbackFaceForChar(mFonts[0].Family(), aCh);
2706 }
2707 if (font) {
2708 *aMatchType = gfxTextRange::kFontGroup;
2709 return font;
2710 }
2711 }
2712
2713 // we don't need to check the first font again below
2714 ++nextIndex;
2715 }
2716
2717 if (aPrevMatchedFont) {
2718 // Don't switch fonts for control characters, regardless of
2719 // whether they are present in the current font, as they won't
2720 // actually be rendered (see bug 716229)
2721 if (isJoinControl ||
2722 GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_CONTROL) {
2723 return aPrevMatchedFont;
2724 }
2725
2726 // if previous character was a join-causer (ZWJ),
2727 // use the same font as the previous range if we can
2728 if (wasJoinCauser) {
2729 if (aPrevMatchedFont->HasCharacter(aCh)) {
2730 return aPrevMatchedFont;
2731 }
2732 }
2733 }
2734
2735 // if this character is a variation selector,
2736 // use the previous font regardless of whether it supports VS or not.
2737 // otherwise the text run will be divided.
2738 if (isVarSelector) {
2739 if (aPrevMatchedFont) {
2740 return aPrevMatchedFont;
2741 }
2742 // VS alone. it's meaningless to search different fonts
2743 return nullptr;
2744 }
2745
2746 // 1. check remaining fonts in the font group
2747 uint32_t fontListLength = mFonts.Length();
2748 for (uint32_t i = nextIndex; i < fontListLength; i++) {
2749 FamilyFace& ff = mFonts[i];
2750 if (ff.IsInvalid() || ff.IsLoading()) {
2751 continue;
2752 }
2753
2754 // if available, use already made gfxFont and check for character
2755 gfxFont* font = ff.Font();
2756 if (font) {
2757 if (font->HasCharacter(aCh)) {
2758 return font;
2759 }
2760 continue;
2761 }
2762
2763 // don't have a gfxFont yet, test before building
2764 gfxFontEntry* fe = ff.FontEntry();
2765 if (fe->mIsUserFontContainer) {
2766 // for userfonts, need to test both the unicode range map and
2767 // the cmap of the platform font entry
2768 gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
2769
2770 // never match a character outside the defined unicode range
2771 if (!ufe->CharacterInUnicodeRange(aCh)) {
2772 continue;
2773 }
2774
2775 // load if not already loaded but only if no other font in similar
2776 // range within family is loading
2777 if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED &&
2778 !FontLoadingForFamily(ff.Family(), aCh)) {
2779 ufe->Load();
2780 ff.CheckState(mSkipDrawing);
2781 }
2782 gfxFontEntry* pfe = ufe->GetPlatformFontEntry();
2783 if (pfe && pfe->HasCharacter(aCh)) {
2784 font = GetFontAt(i, aCh);
2785 if (font) {
2786 *aMatchType = gfxTextRange::kFontGroup;
2787 return font;
2788 }
2789 }
2790 } else if (fe->HasCharacter(aCh)) {
2791 // for normal platform fonts, after checking the cmap
2792 // build the font via GetFontAt
2793 font = GetFontAt(i, aCh);
2794 if (font) {
2795 *aMatchType = gfxTextRange::kFontGroup;
2796 return font;
2797 }
2798 }
2799
2800 // check other family faces if needed
2801 if (ff.CheckForFallbackFaces()) {
2802 NS_ASSERTION(i == 0 ? true
2803 : !mFonts[i - 1].CheckForFallbackFaces() ||
2804 !mFonts[i - 1].Family()->Name().Equals(
2805 ff.Family()->Name()),
2806 "should only do fallback once per font family");
2807 font = FindFallbackFaceForChar(ff.Family(), aCh);
2808 if (font) {
2809 *aMatchType = gfxTextRange::kFontGroup;
2810 return font;
2811 }
2812 } else {
2813 // For platform fonts, but not user fonts, consider intra-family
2814 // fallback to handle styles with reduced character sets (see
2815 // also above).
2816 fe = ff.FontEntry();
2817 if (!fe->mIsUserFontContainer && !fe->IsUserFont()) {
2818 font = FindFallbackFaceForChar(ff.Family(), aCh);
2819 if (font) {
2820 *aMatchType = gfxTextRange::kFontGroup;
2821 return font;
2822 }
2823 }
2824 }
2825 }
2826
2827 if (fontListLength == 0) {
2828 gfxFont* defaultFont = GetDefaultFont();
2829 if (defaultFont->HasCharacter(aCh)) {
2830 *aMatchType = gfxTextRange::kFontGroup;
2831 return defaultFont;
2832 }
2833 }
2834
2835 // if character is in Private Use Area, don't do matching against pref or
2836 // system fonts
2837 if ((aCh >= 0xE000 && aCh <= 0xF8FF) || (aCh >= 0xF0000 && aCh <= 0x10FFFD))
2838 return nullptr;
2839
2840 // 2. search pref fonts
2841 gfxFont* font = WhichPrefFontSupportsChar(aCh, aNextCh);
2842 if (font) {
2843 *aMatchType = gfxTextRange::kPrefsFallback;
2844 return font;
2845 }
2846
2847 // 3. use fallback fonts
2848 // -- before searching for something else check the font used for the
2849 // previous character
2850 if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
2851 *aMatchType = gfxTextRange::kSystemFallback;
2852 return aPrevMatchedFont;
2853 }
2854
2855 // for known "space" characters, don't do a full system-fallback search;
2856 // we'll synthesize appropriate-width spaces instead of missing-glyph boxes
2857 if (GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR &&
2858 GetFirstValidFont()->SynthesizeSpaceWidth(aCh) >= 0.0) {
2859 return nullptr;
2860 }
2861
2862 // -- otherwise look for other stuff
2863 *aMatchType = gfxTextRange::kSystemFallback;
2864 return WhichSystemFontSupportsChar(aCh, aNextCh, aRunScript);
2865 }
2866
2867 template <typename T>
ComputeRanges(nsTArray<gfxTextRange> & aRanges,const T * aString,uint32_t aLength,Script aRunScript,gfx::ShapedTextFlags aOrientation)2868 void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
2869 const T* aString, uint32_t aLength,
2870 Script aRunScript,
2871 gfx::ShapedTextFlags aOrientation) {
2872 NS_ASSERTION(aRanges.Length() == 0, "aRanges must be initially empty");
2873 NS_ASSERTION(aLength > 0, "don't call ComputeRanges for zero-length text");
2874
2875 uint32_t prevCh = 0;
2876 uint32_t nextCh = aString[0];
2877 if (sizeof(T) == sizeof(char16_t)) {
2878 if (aLength > 1 && NS_IS_HIGH_SURROGATE(nextCh) &&
2879 NS_IS_LOW_SURROGATE(aString[1])) {
2880 nextCh = SURROGATE_TO_UCS4(nextCh, aString[1]);
2881 }
2882 }
2883 int32_t lastRangeIndex = -1;
2884
2885 // initialize prevFont to the group's primary font, so that this will be
2886 // used for string-initial control chars, etc rather than risk hitting font
2887 // fallback for these (bug 716229)
2888 gfxFont* prevFont = GetFirstValidFont();
2889
2890 // if we use the initial value of prevFont, we treat this as a match from
2891 // the font group; fixes bug 978313
2892 uint8_t matchType = gfxTextRange::kFontGroup;
2893
2894 for (uint32_t i = 0; i < aLength; i++) {
2895 const uint32_t origI = i; // save off in case we increase for surrogate
2896
2897 // set up current ch
2898 uint32_t ch = nextCh;
2899
2900 // Get next char (if any) so that FindFontForChar can look ahead
2901 // for a possible variation selector.
2902
2903 if (sizeof(T) == sizeof(char16_t)) {
2904 // In 16-bit case only, check for surrogate pairs.
2905 if (ch > 0xffffu) {
2906 i++;
2907 }
2908 if (i < aLength - 1) {
2909 nextCh = aString[i + 1];
2910 if ((i + 2 < aLength) && NS_IS_HIGH_SURROGATE(nextCh) &&
2911 NS_IS_LOW_SURROGATE(aString[i + 2])) {
2912 nextCh = SURROGATE_TO_UCS4(nextCh, aString[i + 2]);
2913 }
2914 } else {
2915 nextCh = 0;
2916 }
2917 } else {
2918 // 8-bit case is trivial.
2919 nextCh = i < aLength - 1 ? aString[i + 1] : 0;
2920 }
2921
2922 if (ch == 0xa0) {
2923 ch = ' ';
2924 }
2925
2926 gfxFont* font;
2927
2928 // Find the font for this char; but try to avoid calling the expensive
2929 // FindFontForChar method for the most common case, where the first
2930 // font in the list supports the current char, and it is not one of
2931 // the special cases where FindFontForChar will attempt to propagate
2932 // the font selected for an adjacent character.
2933 if ((font = GetFontAt(0, ch)) != nullptr && font->HasCharacter(ch) &&
2934 (sizeof(T) == sizeof(uint8_t) ||
2935 (!IsClusterExtender(ch) && ch != NARROW_NO_BREAK_SPACE &&
2936 !gfxFontUtils::IsJoinControl(ch) &&
2937 !gfxFontUtils::IsJoinCauser(prevCh) &&
2938 !gfxFontUtils::IsVarSelector(ch)))) {
2939 matchType = gfxTextRange::kFontGroup;
2940 } else {
2941 font =
2942 FindFontForChar(ch, prevCh, nextCh, aRunScript, prevFont, &matchType);
2943 }
2944
2945 #ifndef RELEASE_OR_BETA
2946 if (MOZ_UNLIKELY(mTextPerf)) {
2947 if (matchType == gfxTextRange::kPrefsFallback) {
2948 mTextPerf->current.fallbackPrefs++;
2949 } else if (matchType == gfxTextRange::kSystemFallback) {
2950 mTextPerf->current.fallbackSystem++;
2951 }
2952 }
2953 #endif
2954
2955 prevCh = ch;
2956
2957 ShapedTextFlags orient = aOrientation;
2958 if (aOrientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
2959 // For CSS text-orientation:mixed, we need to resolve orientation
2960 // on a per-character basis using the UTR50 orientation property.
2961 switch (GetVerticalOrientation(ch)) {
2962 case VERTICAL_ORIENTATION_U:
2963 case VERTICAL_ORIENTATION_Tu:
2964 orient = ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
2965 break;
2966 case VERTICAL_ORIENTATION_Tr: {
2967 // We check for a vertical presentation form first as that's
2968 // likely to be cheaper than inspecting lookups to see if the
2969 // 'vert' feature is going to handle this character, and if the
2970 // presentation form is available then it will be used as
2971 // fallback if needed, so it's OK if the feature is missing.
2972 uint32_t v = gfxHarfBuzzShaper::GetVerticalPresentationForm(ch);
2973 orient = (!font || (v && font->HasCharacter(v)) ||
2974 font->FeatureWillHandleChar(aRunScript,
2975 HB_TAG('v', 'e', 'r', 't'), ch))
2976 ? ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT
2977 : ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
2978 break;
2979 }
2980 case VERTICAL_ORIENTATION_R:
2981 orient = ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
2982 break;
2983 }
2984 }
2985
2986 if (lastRangeIndex == -1) {
2987 // first char ==> make a new range
2988 aRanges.AppendElement(gfxTextRange(0, 1, font, matchType, orient));
2989 lastRangeIndex++;
2990 prevFont = font;
2991 } else {
2992 // if font or orientation has changed, make a new range...
2993 // unless ch is a variation selector (bug 1248248)
2994 gfxTextRange& prevRange = aRanges[lastRangeIndex];
2995 if (prevRange.font != font || prevRange.matchType != matchType ||
2996 (prevRange.orientation != orient && !IsClusterExtender(ch))) {
2997 // close out the previous range
2998 prevRange.end = origI;
2999 aRanges.AppendElement(
3000 gfxTextRange(origI, i + 1, font, matchType, orient));
3001 lastRangeIndex++;
3002
3003 // update prevFont for the next match, *unless* we switched
3004 // fonts on a ZWJ, in which case propagating the changed font
3005 // is probably not a good idea (see bug 619511)
3006 if (sizeof(T) == sizeof(uint8_t) || !gfxFontUtils::IsJoinCauser(ch)) {
3007 prevFont = font;
3008 }
3009 }
3010 }
3011 }
3012
3013 aRanges[lastRangeIndex].end = aLength;
3014
3015 #ifndef RELEASE_OR_BETA
3016 LogModule* log = mStyle.systemFont ? gfxPlatform::GetLog(eGfxLog_textrunui)
3017 : gfxPlatform::GetLog(eGfxLog_textrun);
3018
3019 if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Debug))) {
3020 nsAutoCString lang;
3021 mStyle.language->ToUTF8String(lang);
3022 nsAutoString families;
3023 mFamilyList.ToString(families);
3024
3025 // collect the font matched for each range
3026 nsAutoCString fontMatches;
3027 for (size_t i = 0, i_end = aRanges.Length(); i < i_end; i++) {
3028 const gfxTextRange& r = aRanges[i];
3029 fontMatches.AppendPrintf(
3030 " [%u:%u] %.200s (%s)", r.start, r.end,
3031 (r.font.get() ? NS_ConvertUTF16toUTF8(r.font->GetName()).get()
3032 : "<null>"),
3033 (r.matchType == gfxTextRange::kFontGroup
3034 ? "list"
3035 : (r.matchType == gfxTextRange::kPrefsFallback) ? "prefs"
3036 : "sys"));
3037 }
3038 MOZ_LOG(log, LogLevel::Debug,
3039 ("(%s-fontmatching) fontgroup: [%s] default: %s lang: %s script: %d"
3040 "%s\n",
3041 (mStyle.systemFont ? "textrunui" : "textrun"),
3042 NS_ConvertUTF16toUTF8(families).get(),
3043 (mFamilyList.GetDefaultFontType() == eFamily_serif
3044 ? "serif"
3045 : (mFamilyList.GetDefaultFontType() == eFamily_sans_serif
3046 ? "sans-serif"
3047 : "none")),
3048 lang.get(), static_cast<int>(aRunScript), fontMatches.get()));
3049 }
3050 #endif
3051 }
3052
GetUserFontSet()3053 gfxUserFontSet* gfxFontGroup::GetUserFontSet() { return mUserFontSet; }
3054
SetUserFontSet(gfxUserFontSet * aUserFontSet)3055 void gfxFontGroup::SetUserFontSet(gfxUserFontSet* aUserFontSet) {
3056 if (aUserFontSet == mUserFontSet) {
3057 return;
3058 }
3059 mUserFontSet = aUserFontSet;
3060 mCurrGeneration = GetGeneration() - 1;
3061 UpdateUserFonts();
3062 }
3063
GetGeneration()3064 uint64_t gfxFontGroup::GetGeneration() {
3065 if (!mUserFontSet) return 0;
3066 return mUserFontSet->GetGeneration();
3067 }
3068
GetRebuildGeneration()3069 uint64_t gfxFontGroup::GetRebuildGeneration() {
3070 if (!mUserFontSet) return 0;
3071 return mUserFontSet->GetRebuildGeneration();
3072 }
3073
UpdateUserFonts()3074 void gfxFontGroup::UpdateUserFonts() {
3075 if (mCurrGeneration < GetRebuildGeneration()) {
3076 // fonts in userfont set changed, need to redo the fontlist
3077 mFonts.Clear();
3078 ClearCachedData();
3079 BuildFontList();
3080 mCurrGeneration = GetGeneration();
3081 } else if (mCurrGeneration != GetGeneration()) {
3082 // load state change occurred, verify load state and validity of fonts
3083 ClearCachedData();
3084
3085 uint32_t len = mFonts.Length();
3086 for (uint32_t i = 0; i < len; i++) {
3087 FamilyFace& ff = mFonts[i];
3088 if (ff.Font() || !ff.IsUserFontContainer()) {
3089 continue;
3090 }
3091 ff.CheckState(mSkipDrawing);
3092 }
3093
3094 mCurrGeneration = GetGeneration();
3095 }
3096 }
3097
ContainsUserFont(const gfxUserFontEntry * aUserFont)3098 bool gfxFontGroup::ContainsUserFont(const gfxUserFontEntry* aUserFont) {
3099 UpdateUserFonts();
3100 // search through the fonts list for a specific user font
3101 uint32_t len = mFonts.Length();
3102 for (uint32_t i = 0; i < len; i++) {
3103 FamilyFace& ff = mFonts[i];
3104 if (ff.EqualsUserFont(aUserFont)) {
3105 return true;
3106 }
3107 }
3108 return false;
3109 }
3110
WhichPrefFontSupportsChar(uint32_t aCh,uint32_t aNextCh)3111 gfxFont* gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh,
3112 uint32_t aNextCh) {
3113 eFontPrefLang charLang;
3114 gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
3115
3116 EmojiPresentation emoji = GetEmojiPresentation(aCh);
3117 if ((emoji != EmojiPresentation::TextOnly &&
3118 (aNextCh == kVariationSelector16 ||
3119 (emoji == EmojiPresentation::EmojiDefault &&
3120 aNextCh != kVariationSelector15)))) {
3121 charLang = eFontPrefLang_Emoji;
3122 } else {
3123 // get the pref font list if it hasn't been set up already
3124 uint32_t unicodeRange = FindCharUnicodeRange(aCh);
3125 charLang = pfl->GetFontPrefLangFor(unicodeRange);
3126 }
3127
3128 // if the last pref font was the first family in the pref list, no need to
3129 // recheck through a list of families
3130 if (mLastPrefFont && charLang == mLastPrefLang && mLastPrefFirstFont &&
3131 mLastPrefFont->HasCharacter(aCh)) {
3132 return mLastPrefFont;
3133 }
3134
3135 // based on char lang and page lang, set up list of pref lang fonts to check
3136 eFontPrefLang prefLangs[kMaxLenPrefLangList];
3137 uint32_t i, numLangs = 0;
3138
3139 pfl->GetLangPrefs(prefLangs, numLangs, charLang, mPageLang);
3140
3141 for (i = 0; i < numLangs; i++) {
3142 eFontPrefLang currentLang = prefLangs[i];
3143 mozilla::FontFamilyType defaultGeneric =
3144 pfl->GetDefaultGeneric(currentLang);
3145 nsTArray<RefPtr<gfxFontFamily>>* families =
3146 pfl->GetPrefFontsLangGroup(defaultGeneric, currentLang);
3147 NS_ASSERTION(families, "no pref font families found");
3148
3149 // find the first pref font that includes the character
3150 uint32_t j, numPrefs;
3151 numPrefs = families->Length();
3152 for (j = 0; j < numPrefs; j++) {
3153 // look up the appropriate face
3154 gfxFontFamily* family = (*families)[j];
3155 if (!family) {
3156 continue;
3157 }
3158
3159 // if a pref font is used, it's likely to be used again in the same text
3160 // run. the style doesn't change so the face lookup can be cached rather
3161 // than calling FindOrMakeFont repeatedly. speeds up FindFontForChar
3162 // lookup times for subsequent pref font lookups
3163 if (family == mLastPrefFamily && mLastPrefFont->HasCharacter(aCh)) {
3164 return mLastPrefFont;
3165 }
3166
3167 bool needsBold;
3168 gfxFontEntry* fe = family->FindFontForStyle(mStyle, needsBold);
3169 if (!fe) {
3170 continue;
3171 }
3172
3173 // if ch in cmap, create and return a gfxFont
3174 if (fe->HasCharacter(aCh)) {
3175 gfxFont* prefFont = fe->FindOrMakeFont(&mStyle, needsBold);
3176 if (!prefFont) {
3177 continue;
3178 }
3179 mLastPrefFamily = family;
3180 mLastPrefFont = prefFont;
3181 mLastPrefLang = charLang;
3182 mLastPrefFirstFont = (i == 0 && j == 0);
3183 return prefFont;
3184 }
3185
3186 // If the char was not available, see if we can fall back to an
3187 // alternative face in the same family.
3188 gfxFont* prefFont = FindFallbackFaceForChar(family, aCh);
3189 if (prefFont) {
3190 mLastPrefFamily = family;
3191 mLastPrefFont = prefFont;
3192 mLastPrefLang = charLang;
3193 mLastPrefFirstFont = (i == 0 && j == 0);
3194 return prefFont;
3195 }
3196 }
3197 }
3198
3199 return nullptr;
3200 }
3201
WhichSystemFontSupportsChar(uint32_t aCh,uint32_t aNextCh,Script aRunScript)3202 gfxFont* gfxFontGroup::WhichSystemFontSupportsChar(uint32_t aCh,
3203 uint32_t aNextCh,
3204 Script aRunScript) {
3205 gfxFontEntry* fe =
3206 gfxPlatformFontList::PlatformFontList()->SystemFindFontForChar(
3207 aCh, aNextCh, aRunScript, &mStyle);
3208 if (fe) {
3209 bool wantBold = mStyle.ComputeWeight() >= 6;
3210 return fe->FindOrMakeFont(&mStyle, wantBold && !fe->IsBold());
3211 }
3212
3213 return nullptr;
3214 }
3215
Flush()3216 void gfxMissingFontRecorder::Flush() {
3217 static bool mNotifiedFontsInitialized = false;
3218 static uint32_t mNotifiedFonts[gfxMissingFontRecorder::kNumScriptBitsWords];
3219 if (!mNotifiedFontsInitialized) {
3220 memset(&mNotifiedFonts, 0, sizeof(mNotifiedFonts));
3221 mNotifiedFontsInitialized = true;
3222 }
3223
3224 nsAutoString fontNeeded;
3225 for (uint32_t i = 0; i < kNumScriptBitsWords; ++i) {
3226 mMissingFonts[i] &= ~mNotifiedFonts[i];
3227 if (!mMissingFonts[i]) {
3228 continue;
3229 }
3230 for (uint32_t j = 0; j < 32; ++j) {
3231 if (!(mMissingFonts[i] & (1 << j))) {
3232 continue;
3233 }
3234 mNotifiedFonts[i] |= (1 << j);
3235 if (!fontNeeded.IsEmpty()) {
3236 fontNeeded.Append(char16_t(','));
3237 }
3238 uint32_t sc = i * 32 + j;
3239 MOZ_ASSERT(sc < static_cast<uint32_t>(Script::NUM_SCRIPT_CODES),
3240 "how did we set the bit for an invalid script code?");
3241 uint32_t tag = GetScriptTagForCode(static_cast<Script>(sc));
3242 fontNeeded.Append(char16_t(tag >> 24));
3243 fontNeeded.Append(char16_t((tag >> 16) & 0xff));
3244 fontNeeded.Append(char16_t((tag >> 8) & 0xff));
3245 fontNeeded.Append(char16_t(tag & 0xff));
3246 }
3247 mMissingFonts[i] = 0;
3248 }
3249 if (!fontNeeded.IsEmpty()) {
3250 nsCOMPtr<nsIObserverService> service = GetObserverService();
3251 service->NotifyObservers(nullptr, "font-needed", fontNeeded.get());
3252 }
3253 }
3254