1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=4 et sw=2 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/. */
7 #include "gfxTextRun.h"
8 #include "gfxGlyphExtents.h"
9 #include "gfxHarfBuzzShaper.h"
10 #include "gfxPlatformFontList.h"
11 #include "gfxUserFontSet.h"
12 #include "mozilla/gfx/2D.h"
13 #include "mozilla/gfx/PathHelpers.h"
14 #include "mozilla/Sprintf.h"
15 #include "mozilla/StaticPresData.h"
17 #include "gfxContext.h"
18 #include "gfxFontConstants.h"
19 #include "gfxFontMissingGlyphs.h"
20 #include "gfxScriptItemizer.h"
21 #include "nsUnicodeProperties.h"
22 #include "nsStyleConsts.h"
23 #include "nsStyleUtil.h"
24 #include "mozilla/Likely.h"
25 #include "gfx2DGlue.h"
26 #include "mozilla/gfx/Logging.h" // for gfxCriticalError
27 #include "mozilla/intl/UnicodeProperties.h"
28 #include "mozilla/UniquePtr.h"
29 #include "mozilla/Unused.h"
30 #include "SharedFontList-impl.h"
31 #include "TextDrawTarget.h"
33 #include <unicode/unorm2.h>
36 # include "gfxWindowsPlatform.h"
39 using namespace mozilla
;
40 using namespace mozilla::gfx
;
41 using namespace mozilla::intl
;
42 using namespace mozilla::unicode
;
43 using mozilla::services::GetObserverService
;
45 static const char16_t kEllipsisChar
[] = {0x2026, 0x0};
46 static const char16_t kASCIIPeriodsChar
[] = {'.', '.', '.', 0x0};
49 # define DEBUG_TEXT_RUN_STORAGE_METRICS
52 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
53 extern uint32_t gTextRunStorageHighWaterMark
;
54 extern uint32_t gTextRunStorage
;
55 extern uint32_t gFontCount
;
56 extern uint32_t gGlyphExtentsCount
;
57 extern uint32_t gGlyphExtentsWidthsTotalSize
;
58 extern uint32_t gGlyphExtentsSetupEagerSimple
;
59 extern uint32_t gGlyphExtentsSetupEagerTight
;
60 extern uint32_t gGlyphExtentsSetupLazyTight
;
61 extern uint32_t gGlyphExtentsSetupFallBackToTight
;
64 bool gfxTextRun::GlyphRunIterator::NextRun() {
65 int32_t glyphRunCount
;
66 if (mTextRun
->mHasGlyphRunArray
) {
67 glyphRunCount
= mTextRun
->mGlyphRunArray
.Length();
68 if (mNextIndex
>= glyphRunCount
|| mNextIndex
< 0) {
71 mGlyphRun
= &mTextRun
->mGlyphRunArray
[mNextIndex
];
73 if (mNextIndex
!= 0 || !mTextRun
->mSingleGlyphRun
.mFont
) {
77 mGlyphRun
= &mTextRun
->mSingleGlyphRun
;
80 if (mGlyphRun
->mCharacterOffset
>= mEndOffset
) {
84 uint32_t glyphRunEndOffset
=
85 mNextIndex
+ 1 < (int32_t)glyphRunCount
86 ? mTextRun
->mGlyphRunArray
[mNextIndex
+ 1].mCharacterOffset
87 : mTextRun
->GetLength();
89 if (glyphRunEndOffset
<= mStartOffset
) {
93 mStringEnd
= std::min(mEndOffset
, glyphRunEndOffset
);
94 mStringStart
= std::max(mStartOffset
, mGlyphRun
->mCharacterOffset
);
95 mNextIndex
+= mDirection
;
99 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
100 static void AccountStorageForTextRun(gfxTextRun
* aTextRun
, int32_t aSign
) {
101 // Ignores detailed glyphs... we don't know when those have been constructed
102 // Also ignores gfxSkipChars dynamic storage (which won't be anything
103 // for preformatted text)
104 // Also ignores GlyphRun array, again because it hasn't been constructed
105 // by the time this gets called. If there's only one glyphrun that's stored
106 // directly in the textrun anyway so no additional overhead.
107 uint32_t length
= aTextRun
->GetLength();
108 int32_t bytes
= length
* sizeof(gfxTextRun::CompressedGlyph
);
109 bytes
+= sizeof(gfxTextRun
);
110 gTextRunStorage
+= bytes
* aSign
;
111 gTextRunStorageHighWaterMark
=
112 std::max(gTextRunStorageHighWaterMark
, gTextRunStorage
);
116 static bool NeedsGlyphExtents(gfxTextRun
* aTextRun
) {
117 if (aTextRun
->GetFlags() & gfx::ShapedTextFlags::TEXT_NEED_BOUNDING_BOX
)
120 const gfxTextRun::GlyphRun
* glyphRuns
= aTextRun
->GetGlyphRuns(&numRuns
);
121 for (uint32_t i
= 0; i
< numRuns
; ++i
) {
122 if (glyphRuns
[i
].mFont
->GetFontEntry()->IsUserFont()) return true;
127 // Helper for textRun creation to preallocate storage for glyph records;
128 // this function returns a pointer to the newly-allocated glyph storage.
129 // Returns nullptr if allocation fails.
130 void* gfxTextRun::AllocateStorageForTextRun(size_t aSize
, uint32_t aLength
) {
131 // Allocate the storage we need, returning nullptr on failure rather than
132 // throwing an exception (because web content can create huge runs).
133 void* storage
= malloc(aSize
+ aLength
* sizeof(CompressedGlyph
));
135 NS_WARNING("failed to allocate storage for text run!");
139 // Initialize the glyph storage (beyond aSize) to zero
140 memset(reinterpret_cast<char*>(storage
) + aSize
, 0,
141 aLength
* sizeof(CompressedGlyph
));
146 already_AddRefed
<gfxTextRun
> gfxTextRun::Create(
147 const gfxTextRunFactory::Parameters
* aParams
, uint32_t aLength
,
148 gfxFontGroup
* aFontGroup
, gfx::ShapedTextFlags aFlags
,
149 nsTextFrameUtils::Flags aFlags2
) {
150 void* storage
= AllocateStorageForTextRun(sizeof(gfxTextRun
), aLength
);
155 RefPtr
<gfxTextRun
> result
=
156 new (storage
) gfxTextRun(aParams
, aLength
, aFontGroup
, aFlags
, aFlags2
);
157 return result
.forget();
160 gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters
* aParams
,
161 uint32_t aLength
, gfxFontGroup
* aFontGroup
,
162 gfx::ShapedTextFlags aFlags
,
163 nsTextFrameUtils::Flags aFlags2
)
164 : gfxShapedText(aLength
, aFlags
, aParams
->mAppUnitsPerDevUnit
),
166 mUserData(aParams
->mUserData
),
167 mFontGroup(aFontGroup
),
169 mReleasedFontGroup(false),
170 mReleasedFontGroupSkippedDrawing(false),
171 mHasGlyphRunArray(false),
172 mShapingState(eShapingState_Normal
) {
173 NS_ASSERTION(mAppUnitsPerDevUnit
> 0, "Invalid app unit scale");
174 NS_ADDREF(mFontGroup
);
176 #ifndef RELEASE_OR_BETA
177 gfxTextPerfMetrics
* tp
= aFontGroup
->GetTextPerfMetrics();
179 tp
->current
.textrunConst
++;
183 mCharacterGlyphs
= reinterpret_cast<CompressedGlyph
*>(this + 1);
185 if (aParams
->mSkipChars
) {
186 mSkipChars
.TakeFrom(aParams
->mSkipChars
);
189 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
190 AccountStorageForTextRun(this, 1);
194 !!(aFlags2
& nsTextFrameUtils::Flags::DontSkipDrawingForPendingUserFonts
);
197 gfxTextRun::~gfxTextRun() {
198 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
199 AccountStorageForTextRun(this, -1);
202 // Make it easy to detect a dead text run
203 mFlags
= ~gfx::ShapedTextFlags();
204 mFlags2
= ~nsTextFrameUtils::Flags();
207 if (mHasGlyphRunArray
) {
208 mGlyphRunArray
.~nsTArray
<GlyphRun
>();
210 mSingleGlyphRun
.mFont
= nullptr;
213 // The cached ellipsis textrun (if any) in a fontgroup will have already
214 // been told to release its reference to the group, so we mustn't do that
216 if (!mReleasedFontGroup
) {
217 #ifndef RELEASE_OR_BETA
218 gfxTextPerfMetrics
* tp
= mFontGroup
->GetTextPerfMetrics();
220 tp
->current
.textrunDestr
++;
223 NS_RELEASE(mFontGroup
);
227 void gfxTextRun::ReleaseFontGroup() {
228 NS_ASSERTION(!mReleasedFontGroup
, "doubly released!");
230 // After dropping our reference to the font group, we'll no longer be able
231 // to get up-to-date results for ShouldSkipDrawing(). Store the current
232 // value in mReleasedFontGroupSkippedDrawing.
234 // (It doesn't actually matter that we can't get up-to-date results for
235 // ShouldSkipDrawing(), since the only text runs that we call
236 // ReleaseFontGroup() for are ellipsis text runs, and we ask the font
237 // group for a new ellipsis text run each time we want to draw one,
238 // and ensure that the cached one is cleared in ClearCachedData() when
239 // font loading status changes.)
240 mReleasedFontGroupSkippedDrawing
= mFontGroup
->ShouldSkipDrawing();
242 NS_RELEASE(mFontGroup
);
243 mReleasedFontGroup
= true;
246 bool gfxTextRun::SetPotentialLineBreaks(Range aRange
,
247 const uint8_t* aBreakBefore
) {
248 NS_ASSERTION(aRange
.end
<= GetLength(), "Overflow");
250 uint32_t changed
= 0;
251 CompressedGlyph
* cg
= mCharacterGlyphs
+ aRange
.start
;
252 const CompressedGlyph
* const end
= cg
+ aRange
.Length();
254 uint8_t canBreak
= *aBreakBefore
++;
255 if (canBreak
&& !cg
->IsClusterStart()) {
256 // XXX If we replace the line-breaker with one based more closely
257 // on UAX#14 (e.g. using ICU), this may not be needed any more.
258 // Avoid possible breaks inside a cluster, EXCEPT when the previous
259 // character was a space (compare UAX#14 rules LB9, LB10).
260 if (cg
== mCharacterGlyphs
|| !(cg
- 1)->CharIsSpace()) {
261 canBreak
= CompressedGlyph::FLAG_BREAK_TYPE_NONE
;
264 changed
|= cg
->SetCanBreakBefore(canBreak
);
270 gfxTextRun::LigatureData
gfxTextRun::ComputeLigatureData(
271 Range aPartRange
, PropertyProvider
* aProvider
) const {
272 NS_ASSERTION(aPartRange
.start
< aPartRange
.end
,
273 "Computing ligature data for empty range");
274 NS_ASSERTION(aPartRange
.end
<= GetLength(), "Character length overflow");
277 const CompressedGlyph
* charGlyphs
= mCharacterGlyphs
;
280 for (i
= aPartRange
.start
; !charGlyphs
[i
].IsLigatureGroupStart(); --i
) {
281 NS_ASSERTION(i
> 0, "Ligature at the start of the run??");
283 result
.mRange
.start
= i
;
284 for (i
= aPartRange
.start
+ 1;
285 i
< GetLength() && !charGlyphs
[i
].IsLigatureGroupStart(); ++i
) {
287 result
.mRange
.end
= i
;
289 int32_t ligatureWidth
= GetAdvanceForGlyphs(result
.mRange
);
290 // Count the number of started clusters we have seen
291 uint32_t totalClusterCount
= 0;
292 uint32_t partClusterIndex
= 0;
293 uint32_t partClusterCount
= 0;
294 for (i
= result
.mRange
.start
; i
< result
.mRange
.end
; ++i
) {
295 // Treat the first character of the ligature as the start of a
296 // cluster for our purposes of allocating ligature width to its
298 if (i
== result
.mRange
.start
|| charGlyphs
[i
].IsClusterStart()) {
300 if (i
< aPartRange
.start
) {
302 } else if (i
< aPartRange
.end
) {
307 NS_ASSERTION(totalClusterCount
> 0, "Ligature involving no clusters??");
308 result
.mPartAdvance
= partClusterIndex
* (ligatureWidth
/ totalClusterCount
);
309 result
.mPartWidth
= partClusterCount
* (ligatureWidth
/ totalClusterCount
);
311 // Any rounding errors are apportioned to the final part of the ligature,
312 // so that measuring all parts of a ligature and summing them is equal to
313 // the ligature width.
314 if (aPartRange
.end
== result
.mRange
.end
) {
315 gfxFloat allParts
= totalClusterCount
* (ligatureWidth
/ totalClusterCount
);
316 result
.mPartWidth
+= ligatureWidth
- allParts
;
319 if (partClusterCount
== 0) {
321 result
.mClipBeforePart
= result
.mClipAfterPart
= true;
323 // Determine whether we should clip before or after this part when
324 // drawing its slice of the ligature.
325 // We need to clip before the part if any cluster is drawn before
327 result
.mClipBeforePart
= partClusterIndex
> 0;
328 // We need to clip after the part if any cluster is drawn after
330 result
.mClipAfterPart
=
331 partClusterIndex
+ partClusterCount
< totalClusterCount
;
334 if (aProvider
&& (mFlags
& gfx::ShapedTextFlags::TEXT_ENABLE_SPACING
)) {
335 gfxFont::Spacing spacing
;
336 if (aPartRange
.start
== result
.mRange
.start
) {
337 aProvider
->GetSpacing(Range(aPartRange
.start
, aPartRange
.start
+ 1),
339 result
.mPartWidth
+= spacing
.mBefore
;
341 if (aPartRange
.end
== result
.mRange
.end
) {
342 aProvider
->GetSpacing(Range(aPartRange
.end
- 1, aPartRange
.end
),
344 result
.mPartWidth
+= spacing
.mAfter
;
351 gfxFloat
gfxTextRun::ComputePartialLigatureWidth(
352 Range aPartRange
, PropertyProvider
* aProvider
) const {
353 if (aPartRange
.start
>= aPartRange
.end
) return 0;
354 LigatureData data
= ComputeLigatureData(aPartRange
, aProvider
);
355 return data
.mPartWidth
;
358 int32_t gfxTextRun::GetAdvanceForGlyphs(Range aRange
) const {
360 for (auto i
= aRange
.start
; i
< aRange
.end
; ++i
) {
361 advance
+= GetAdvanceForGlyph(i
);
366 static void GetAdjustedSpacing(
367 const gfxTextRun
* aTextRun
, gfxTextRun::Range aRange
,
368 gfxTextRun::PropertyProvider
* aProvider
,
369 gfxTextRun::PropertyProvider::Spacing
* aSpacing
) {
370 if (aRange
.start
>= aRange
.end
) return;
372 aProvider
->GetSpacing(aRange
, aSpacing
);
375 // Check to see if we have spacing inside ligatures
377 const gfxTextRun::CompressedGlyph
* charGlyphs
=
378 aTextRun
->GetCharacterGlyphs();
381 for (i
= aRange
.start
; i
< aRange
.end
; ++i
) {
382 if (!charGlyphs
[i
].IsLigatureGroupStart()) {
383 NS_ASSERTION(i
== aRange
.start
|| aSpacing
[i
- aRange
.start
].mBefore
== 0,
384 "Before-spacing inside a ligature!");
386 i
- 1 <= aRange
.start
|| aSpacing
[i
- 1 - aRange
.start
].mAfter
== 0,
387 "After-spacing inside a ligature!");
393 bool gfxTextRun::GetAdjustedSpacingArray(
394 Range aRange
, PropertyProvider
* aProvider
, Range aSpacingRange
,
395 nsTArray
<PropertyProvider::Spacing
>* aSpacing
) const {
396 if (!aProvider
|| !(mFlags
& gfx::ShapedTextFlags::TEXT_ENABLE_SPACING
)) {
399 if (!aSpacing
->AppendElements(aRange
.Length(), fallible
)) {
402 auto spacingOffset
= aSpacingRange
.start
- aRange
.start
;
403 memset(aSpacing
->Elements(), 0, sizeof(gfxFont::Spacing
) * spacingOffset
);
404 GetAdjustedSpacing(this, aSpacingRange
, aProvider
,
405 aSpacing
->Elements() + spacingOffset
);
406 memset(aSpacing
->Elements() + aSpacingRange
.end
- aRange
.start
, 0,
407 sizeof(gfxFont::Spacing
) * (aRange
.end
- aSpacingRange
.end
));
411 void gfxTextRun::ShrinkToLigatureBoundaries(Range
* aRange
) const {
412 if (aRange
->start
>= aRange
->end
) return;
414 const CompressedGlyph
* charGlyphs
= mCharacterGlyphs
;
416 while (aRange
->start
< aRange
->end
&&
417 !charGlyphs
[aRange
->start
].IsLigatureGroupStart()) {
420 if (aRange
->end
< GetLength()) {
421 while (aRange
->end
> aRange
->start
&&
422 !charGlyphs
[aRange
->end
].IsLigatureGroupStart()) {
428 void gfxTextRun::DrawGlyphs(gfxFont
* aFont
, Range aRange
, gfx::Point
* aPt
,
429 PropertyProvider
* aProvider
, Range aSpacingRange
,
430 TextRunDrawParams
& aParams
,
431 gfx::ShapedTextFlags aOrientation
) const {
432 AutoTArray
<PropertyProvider::Spacing
, 200> spacingBuffer
;
434 GetAdjustedSpacingArray(aRange
, aProvider
, aSpacingRange
, &spacingBuffer
);
435 aParams
.spacing
= haveSpacing
? spacingBuffer
.Elements() : nullptr;
436 aFont
->Draw(this, aRange
.start
, aRange
.end
, aPt
, aParams
, aOrientation
);
439 static void ClipPartialLigature(const gfxTextRun
* aTextRun
, gfxFloat
* aStart
,
440 gfxFloat
* aEnd
, gfxFloat aOrigin
,
441 gfxTextRun::LigatureData
* aLigature
) {
442 if (aLigature
->mClipBeforePart
) {
443 if (aTextRun
->IsRightToLeft()) {
444 *aEnd
= std::min(*aEnd
, aOrigin
);
446 *aStart
= std::max(*aStart
, aOrigin
);
449 if (aLigature
->mClipAfterPart
) {
451 aOrigin
+ aTextRun
->GetDirection() * aLigature
->mPartWidth
;
452 if (aTextRun
->IsRightToLeft()) {
453 *aStart
= std::max(*aStart
, endEdge
);
455 *aEnd
= std::min(*aEnd
, endEdge
);
460 void gfxTextRun::DrawPartialLigature(gfxFont
* aFont
, Range aRange
,
462 PropertyProvider
* aProvider
,
463 TextRunDrawParams
& aParams
,
464 gfx::ShapedTextFlags aOrientation
) const {
465 if (aRange
.start
>= aRange
.end
) {
469 // Draw partial ligature. We hack this by clipping the ligature.
470 LigatureData data
= ComputeLigatureData(aRange
, aProvider
);
471 gfxRect clipExtents
= aParams
.context
->GetClipExtents();
473 if (aParams
.isVerticalRun
) {
474 start
= clipExtents
.Y() * mAppUnitsPerDevUnit
;
475 end
= clipExtents
.YMost() * mAppUnitsPerDevUnit
;
476 ClipPartialLigature(this, &start
, &end
, aPt
->y
, &data
);
478 start
= clipExtents
.X() * mAppUnitsPerDevUnit
;
479 end
= clipExtents
.XMost() * mAppUnitsPerDevUnit
;
480 ClipPartialLigature(this, &start
, &end
, aPt
->x
, &data
);
484 // use division here to ensure that when the rect is aligned on multiples
485 // of mAppUnitsPerDevUnit, we clip to true device unit boundaries.
486 // Also, make sure we snap the rectangle to device pixels.
488 aParams
.isVerticalRun
489 ? Rect(clipExtents
.X(), start
/ mAppUnitsPerDevUnit
,
490 clipExtents
.Width(), (end
- start
) / mAppUnitsPerDevUnit
)
491 : Rect(start
/ mAppUnitsPerDevUnit
, clipExtents
.Y(),
492 (end
- start
) / mAppUnitsPerDevUnit
, clipExtents
.Height());
493 MaybeSnapToDevicePixels(clipRect
, *aParams
.dt
, true);
495 aParams
.context
->Clip(clipRect
);
499 if (aParams
.isVerticalRun
) {
500 pt
= Point(aPt
->x
, aPt
->y
- aParams
.direction
* data
.mPartAdvance
);
502 pt
= Point(aPt
->x
- aParams
.direction
* data
.mPartAdvance
, aPt
->y
);
505 DrawGlyphs(aFont
, data
.mRange
, &pt
, aProvider
, aRange
, aParams
, aOrientation
);
506 aParams
.context
->PopClip();
508 if (aParams
.isVerticalRun
) {
509 aPt
->y
+= aParams
.direction
* data
.mPartWidth
;
511 aPt
->x
+= aParams
.direction
* data
.mPartWidth
;
515 // Returns true if the font has synthetic bolding enabled,
516 // or is a color font (COLR/SVG/sbix/CBDT), false otherwise. This is used to
517 // check whether the text run needs to be explicitly composited in order to
519 static bool HasSyntheticBoldOrColor(gfxFont
* aFont
) {
520 if (aFont
->ApplySyntheticBold()) {
523 gfxFontEntry
* fe
= aFont
->GetFontEntry();
524 if (fe
->TryGetSVGData(aFont
) || fe
->TryGetColorGlyphs()) {
527 #if defined(XP_MACOSX) // sbix fonts only supported via Core Text
528 if (fe
->HasFontTable(TRUETYPE_TAG('s', 'b', 'i', 'x'))) {
535 // helper class for double-buffering drawing with non-opaque color
536 struct MOZ_STACK_CLASS BufferAlphaColor
{
537 explicit BufferAlphaColor(gfxContext
* aContext
) : mContext(aContext
) {}
539 ~BufferAlphaColor() = default;
541 void PushSolidColor(const gfxRect
& aBounds
, const DeviceColor
& aAlphaColor
,
542 uint32_t appsPerDevUnit
) {
544 mContext
->SnappedClip(gfxRect(
545 aBounds
.X() / appsPerDevUnit
, aBounds
.Y() / appsPerDevUnit
,
546 aBounds
.Width() / appsPerDevUnit
, aBounds
.Height() / appsPerDevUnit
));
547 mContext
->SetDeviceColor(
548 DeviceColor(aAlphaColor
.r
, aAlphaColor
.g
, aAlphaColor
.b
));
549 mContext
->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA
, aAlphaColor
.a
);
553 // pop the text, using the color alpha as the opacity
554 mContext
->PopGroupAndBlend();
558 gfxContext
* mContext
;
561 void gfxTextRun::Draw(const Range aRange
, const gfx::Point aPt
,
562 const DrawParams
& aParams
) const {
563 NS_ASSERTION(aRange
.end
<= GetLength(), "Substring out of range");
564 NS_ASSERTION(aParams
.drawMode
== DrawMode::GLYPH_PATH
||
565 !(aParams
.drawMode
& DrawMode::GLYPH_PATH
),
566 "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or "
567 "GLYPH_STROKE_UNDERNEATH");
568 NS_ASSERTION(aParams
.drawMode
== DrawMode::GLYPH_PATH
|| !aParams
.callbacks
,
569 "callback must not be specified unless using GLYPH_PATH");
572 !mDontSkipDrawing
&& (mFontGroup
? mFontGroup
->ShouldSkipDrawing()
573 : mReleasedFontGroupSkippedDrawing
);
574 if (aParams
.drawMode
& DrawMode::GLYPH_FILL
) {
575 DeviceColor currentColor
;
576 if (aParams
.context
->GetDeviceColor(currentColor
) && currentColor
.a
== 0 &&
577 !aParams
.context
->GetTextDrawer()) {
582 gfxFloat direction
= GetDirection();
585 // We don't need to draw anything;
586 // but if the caller wants advance width, we need to compute it here
587 if (aParams
.advanceWidth
) {
588 gfxTextRun::Metrics metrics
=
589 MeasureText(aRange
, gfxFont::LOOSE_INK_EXTENTS
,
590 aParams
.context
->GetDrawTarget(), aParams
.provider
);
591 *aParams
.advanceWidth
= metrics
.mAdvanceWidth
* direction
;
594 // return without drawing
598 // synthetic bolding draws glyphs twice ==> colors with opacity won't draw
599 // correctly unless first drawn without alpha
600 BufferAlphaColor
syntheticBoldBuffer(aParams
.context
);
601 DeviceColor currentColor
;
602 bool mayNeedBuffering
=
603 aParams
.drawMode
& DrawMode::GLYPH_FILL
&&
604 aParams
.context
->HasNonOpaqueNonTransparentColor(currentColor
) &&
605 !aParams
.context
->GetTextDrawer();
607 // If we need to double-buffer, we'll need to measure the text first to
608 // get the bounds of the area of interest. Ideally we'd do that just for
609 // the specific glyph run(s) that need buffering, but because of bug
610 // 1612610 we currently use the extent of the entire range even when
611 // just buffering a subrange. So we'll measure the full range once and
612 // keep the metrics on hand for any subsequent subranges.
613 gfxTextRun::Metrics metrics
;
614 bool gotMetrics
= false;
616 // Set up parameters that will be constant across all glyph runs we need
617 // to draw, regardless of the font used.
618 TextRunDrawParams params
;
619 params
.context
= aParams
.context
;
620 params
.devPerApp
= 1.0 / double(GetAppUnitsPerDevUnit());
621 params
.isVerticalRun
= IsVertical();
622 params
.isRTL
= IsRightToLeft();
623 params
.direction
= direction
;
624 params
.strokeOpts
= aParams
.strokeOpts
;
625 params
.textStrokeColor
= aParams
.textStrokeColor
;
626 params
.textStrokePattern
= aParams
.textStrokePattern
;
627 params
.drawOpts
= aParams
.drawOpts
;
628 params
.drawMode
= aParams
.drawMode
;
629 params
.callbacks
= aParams
.callbacks
;
630 params
.runContextPaint
= aParams
.contextPaint
;
631 params
.paintSVGGlyphs
=
632 !aParams
.callbacks
|| aParams
.callbacks
->mShouldPaintSVGGlyphs
;
633 params
.dt
= aParams
.context
->GetDrawTarget();
634 params
.allowGDI
= aParams
.allowGDI
;
636 GlyphRunIterator
iter(this, aRange
);
637 gfxFloat advance
= 0.0;
640 while (iter
.NextRun()) {
641 gfxFont
* font
= iter
.GetGlyphRun()->mFont
;
642 Range
runRange(iter
.GetStringStart(), iter
.GetStringEnd());
644 bool needToRestore
= false;
645 if (mayNeedBuffering
&& HasSyntheticBoldOrColor(font
)) {
646 needToRestore
= true;
648 // Measure text; use the bounding box to determine the area we need
649 // to buffer. We measure the entire range, rather than just the glyph
650 // run that we're actually handling, because of bug 1612610: if the
651 // bounding box passed to PushSolidColor does not intersect the
652 // drawTarget's current clip, the skia backend fails to clip properly.
653 // This means we may use a larger buffer than actually needed, but is
654 // otherwise harmless.
656 MeasureText(aRange
, gfxFont::LOOSE_INK_EXTENTS
,
657 aParams
.context
->GetDrawTarget(), aParams
.provider
);
658 if (IsRightToLeft()) {
659 metrics
.mBoundingBox
.MoveBy(
660 gfxPoint(aPt
.x
- metrics
.mAdvanceWidth
, aPt
.y
));
662 metrics
.mBoundingBox
.MoveBy(gfxPoint(aPt
.x
, aPt
.y
));
666 syntheticBoldBuffer
.PushSolidColor(metrics
.mBoundingBox
, currentColor
,
667 GetAppUnitsPerDevUnit());
670 Range
ligatureRange(runRange
);
671 ShrinkToLigatureBoundaries(&ligatureRange
);
674 (aParams
.drawMode
& (DrawMode::GLYPH_FILL
| DrawMode::GLYPH_STROKE
)) ||
675 (aParams
.drawMode
== DrawMode::GLYPH_PATH
&& aParams
.callbacks
);
676 gfx::Point origPt
= pt
;
679 DrawPartialLigature(font
, Range(runRange
.start
, ligatureRange
.start
), &pt
,
680 aParams
.provider
, params
,
681 iter
.GetGlyphRun()->mOrientation
);
684 DrawGlyphs(font
, ligatureRange
, &pt
, aParams
.provider
, ligatureRange
,
685 params
, iter
.GetGlyphRun()->mOrientation
);
688 DrawPartialLigature(font
, Range(ligatureRange
.end
, runRange
.end
), &pt
,
689 aParams
.provider
, params
,
690 iter
.GetGlyphRun()->mOrientation
);
693 if (params
.isVerticalRun
) {
694 advance
+= (pt
.y
- origPt
.y
) * params
.direction
;
696 advance
+= (pt
.x
- origPt
.x
) * params
.direction
;
699 // composite result when synthetic bolding used
701 syntheticBoldBuffer
.PopAlpha();
705 if (aParams
.advanceWidth
) {
706 *aParams
.advanceWidth
= advance
;
710 // This method is mostly parallel to Draw().
711 void gfxTextRun::DrawEmphasisMarks(gfxContext
* aContext
, gfxTextRun
* aMark
,
712 gfxFloat aMarkAdvance
, gfx::Point aPt
,
714 PropertyProvider
* aProvider
) const {
715 MOZ_ASSERT(aRange
.end
<= GetLength());
717 EmphasisMarkDrawParams params
;
718 params
.context
= aContext
;
720 params
.advance
= aMarkAdvance
;
721 params
.direction
= GetDirection();
722 params
.isVertical
= IsVertical();
724 float& inlineCoord
= params
.isVertical
? aPt
.y
: aPt
.x
;
725 float direction
= params
.direction
;
727 GlyphRunIterator
iter(this, aRange
);
728 while (iter
.NextRun()) {
729 gfxFont
* font
= iter
.GetGlyphRun()->mFont
;
730 uint32_t start
= iter
.GetStringStart();
731 uint32_t end
= iter
.GetStringEnd();
732 Range
ligatureRange(start
, end
);
733 ShrinkToLigatureBoundaries(&ligatureRange
);
736 direction
* ComputePartialLigatureWidth(
737 Range(start
, ligatureRange
.start
), aProvider
);
739 AutoTArray
<PropertyProvider::Spacing
, 200> spacingBuffer
;
740 bool haveSpacing
= GetAdjustedSpacingArray(ligatureRange
, aProvider
,
741 ligatureRange
, &spacingBuffer
);
742 params
.spacing
= haveSpacing
? spacingBuffer
.Elements() : nullptr;
743 font
->DrawEmphasisMarks(this, &aPt
, ligatureRange
.start
,
744 ligatureRange
.Length(), params
);
746 inlineCoord
+= direction
* ComputePartialLigatureWidth(
747 Range(ligatureRange
.end
, end
), aProvider
);
751 void gfxTextRun::AccumulateMetricsForRun(
752 gfxFont
* aFont
, Range aRange
, gfxFont::BoundingBoxType aBoundingBoxType
,
753 DrawTarget
* aRefDrawTarget
, PropertyProvider
* aProvider
,
754 Range aSpacingRange
, gfx::ShapedTextFlags aOrientation
,
755 Metrics
* aMetrics
) const {
756 AutoTArray
<PropertyProvider::Spacing
, 200> spacingBuffer
;
758 GetAdjustedSpacingArray(aRange
, aProvider
, aSpacingRange
, &spacingBuffer
);
759 Metrics metrics
= aFont
->Measure(
760 this, aRange
.start
, aRange
.end
, aBoundingBoxType
, aRefDrawTarget
,
761 haveSpacing
? spacingBuffer
.Elements() : nullptr, aOrientation
);
762 aMetrics
->CombineWith(metrics
, IsRightToLeft());
765 void gfxTextRun::AccumulatePartialLigatureMetrics(
766 gfxFont
* aFont
, Range aRange
, gfxFont::BoundingBoxType aBoundingBoxType
,
767 DrawTarget
* aRefDrawTarget
, PropertyProvider
* aProvider
,
768 gfx::ShapedTextFlags aOrientation
, Metrics
* aMetrics
) const {
769 if (aRange
.start
>= aRange
.end
) return;
771 // Measure partial ligature. We hack this by clipping the metrics in the
772 // same way we clip the drawing.
773 LigatureData data
= ComputeLigatureData(aRange
, aProvider
);
775 // First measure the complete ligature
777 AccumulateMetricsForRun(aFont
, data
.mRange
, aBoundingBoxType
, aRefDrawTarget
,
778 aProvider
, aRange
, aOrientation
, &metrics
);
780 // Clip the bounding box to the ligature part
781 gfxFloat bboxLeft
= metrics
.mBoundingBox
.X();
782 gfxFloat bboxRight
= metrics
.mBoundingBox
.XMost();
783 // Where we are going to start "drawing" relative to our left baseline origin
785 IsRightToLeft() ? metrics
.mAdvanceWidth
- data
.mPartAdvance
: 0;
786 ClipPartialLigature(this, &bboxLeft
, &bboxRight
, origin
, &data
);
787 metrics
.mBoundingBox
.SetBoxX(bboxLeft
, bboxRight
);
789 // mBoundingBox is now relative to the left baseline origin for the entire
790 // ligature. Shift it left.
791 metrics
.mBoundingBox
.MoveByX(
793 ? metrics
.mAdvanceWidth
- (data
.mPartAdvance
+ data
.mPartWidth
)
794 : data
.mPartAdvance
));
795 metrics
.mAdvanceWidth
= data
.mPartWidth
;
797 aMetrics
->CombineWith(metrics
, IsRightToLeft());
800 gfxTextRun::Metrics
gfxTextRun::MeasureText(
801 Range aRange
, gfxFont::BoundingBoxType aBoundingBoxType
,
802 DrawTarget
* aRefDrawTarget
, PropertyProvider
* aProvider
) const {
803 NS_ASSERTION(aRange
.end
<= GetLength(), "Substring out of range");
805 Metrics accumulatedMetrics
;
806 GlyphRunIterator
iter(this, aRange
);
807 while (iter
.NextRun()) {
808 gfxFont
* font
= iter
.GetGlyphRun()->mFont
;
809 uint32_t start
= iter
.GetStringStart();
810 uint32_t end
= iter
.GetStringEnd();
811 Range
ligatureRange(start
, end
);
812 ShrinkToLigatureBoundaries(&ligatureRange
);
814 AccumulatePartialLigatureMetrics(
815 font
, Range(start
, ligatureRange
.start
), aBoundingBoxType
,
816 aRefDrawTarget
, aProvider
, iter
.GetGlyphRun()->mOrientation
,
817 &accumulatedMetrics
);
819 // XXX This sucks. We have to get glyph extents just so we can detect
820 // glyphs outside the font box, even when aBoundingBoxType is LOOSE,
821 // even though in almost all cases we could get correct results just
822 // by getting some ascent/descent from the font and using our stored
824 AccumulateMetricsForRun(
825 font
, ligatureRange
, aBoundingBoxType
, aRefDrawTarget
, aProvider
,
826 ligatureRange
, iter
.GetGlyphRun()->mOrientation
, &accumulatedMetrics
);
828 AccumulatePartialLigatureMetrics(
829 font
, Range(ligatureRange
.end
, end
), aBoundingBoxType
, aRefDrawTarget
,
830 aProvider
, iter
.GetGlyphRun()->mOrientation
, &accumulatedMetrics
);
833 return accumulatedMetrics
;
836 #define MEASUREMENT_BUFFER_SIZE 100
838 void gfxTextRun::ClassifyAutoHyphenations(uint32_t aStart
, Range aRange
,
839 nsTArray
<HyphenType
>& aHyphenBuffer
,
840 HyphenationState
* aWordState
) {
842 aRange
.end
- aStart
<= aHyphenBuffer
.Length() && aRange
.start
>= aStart
,
843 "Range out of bounds");
844 MOZ_ASSERT(aWordState
->mostRecentBoundary
>= aStart
,
845 "Unexpected aMostRecentWordBoundary!!");
848 std::min
<uint32_t>(aRange
.start
, aWordState
->mostRecentBoundary
);
850 for (uint32_t i
= start
; i
< aRange
.end
; ++i
) {
851 if (aHyphenBuffer
[i
- aStart
] == HyphenType::Explicit
&&
852 !aWordState
->hasExplicitHyphen
) {
853 aWordState
->hasExplicitHyphen
= true;
855 if (!aWordState
->hasManualHyphen
&&
856 (aHyphenBuffer
[i
- aStart
] == HyphenType::Soft
||
857 aHyphenBuffer
[i
- aStart
] == HyphenType::Explicit
)) {
858 aWordState
->hasManualHyphen
= true;
859 // This is the first manual hyphen in the current word. We can only
860 // know if the current word has a manual hyphen until now. So, we need
861 // to run a sub loop to update the auto hyphens between the start of
862 // the current word and this manual hyphen.
863 if (aWordState
->hasAutoHyphen
) {
864 for (uint32_t j
= aWordState
->mostRecentBoundary
; j
< i
; j
++) {
865 if (aHyphenBuffer
[j
- aStart
] ==
866 HyphenType::AutoWithoutManualInSameWord
) {
867 aHyphenBuffer
[j
- aStart
] = HyphenType::AutoWithManualInSameWord
;
872 if (aHyphenBuffer
[i
- aStart
] == HyphenType::AutoWithoutManualInSameWord
) {
873 if (!aWordState
->hasAutoHyphen
) {
874 aWordState
->hasAutoHyphen
= true;
876 if (aWordState
->hasManualHyphen
) {
877 aHyphenBuffer
[i
- aStart
] = HyphenType::AutoWithManualInSameWord
;
881 // If we're at the word boundary, clear/reset couple states.
882 if (mCharacterGlyphs
[i
].CharIsSpace() || mCharacterGlyphs
[i
].CharIsTab() ||
883 mCharacterGlyphs
[i
].CharIsNewline() ||
884 // Since we will not have a boundary in the end of the string, let's
885 // call the end of the string a special case for word boundary.
886 i
== GetLength() - 1) {
887 // We can only get to know whether we should raise/clear an explicit
888 // manual hyphen until we get to the end of a word, because this depends
889 // on whether there exists at least one auto hyphen in the same word.
890 if (!aWordState
->hasAutoHyphen
&& aWordState
->hasExplicitHyphen
) {
891 for (uint32_t j
= aWordState
->mostRecentBoundary
; j
<= i
; j
++) {
892 if (aHyphenBuffer
[j
- aStart
] == HyphenType::Explicit
) {
893 aHyphenBuffer
[j
- aStart
] = HyphenType::None
;
897 aWordState
->mostRecentBoundary
= i
;
898 aWordState
->hasManualHyphen
= false;
899 aWordState
->hasAutoHyphen
= false;
900 aWordState
->hasExplicitHyphen
= false;
905 uint32_t gfxTextRun::BreakAndMeasureText(
906 uint32_t aStart
, uint32_t aMaxLength
, bool aLineBreakBefore
,
907 gfxFloat aWidth
, PropertyProvider
* aProvider
, SuppressBreak aSuppressBreak
,
908 gfxFloat
* aTrimWhitespace
, bool aWhitespaceCanHang
, Metrics
* aMetrics
,
909 gfxFont::BoundingBoxType aBoundingBoxType
, DrawTarget
* aRefDrawTarget
,
910 bool* aUsedHyphenation
, uint32_t* aLastBreak
, bool aCanWordWrap
,
911 bool aCanWhitespaceWrap
, gfxBreakPriority
* aBreakPriority
) {
912 aMaxLength
= std::min(aMaxLength
, GetLength() - aStart
);
914 NS_ASSERTION(aStart
+ aMaxLength
<= GetLength(), "Substring out of range");
917 aStart
, aStart
+ std::min
<uint32_t>(aMaxLength
, MEASUREMENT_BUFFER_SIZE
));
918 PropertyProvider::Spacing spacingBuffer
[MEASUREMENT_BUFFER_SIZE
];
920 aProvider
&& !!(mFlags
& gfx::ShapedTextFlags::TEXT_ENABLE_SPACING
);
922 GetAdjustedSpacing(this, bufferRange
, aProvider
, spacingBuffer
);
924 AutoTArray
<HyphenType
, 4096> hyphenBuffer
;
925 HyphenationState wordState
;
926 wordState
.mostRecentBoundary
= aStart
;
927 bool haveHyphenation
=
929 (aProvider
->GetHyphensOption() == StyleHyphens::Auto
||
930 (aProvider
->GetHyphensOption() == StyleHyphens::Manual
&&
931 !!(mFlags
& gfx::ShapedTextFlags::TEXT_ENABLE_HYPHEN_BREAKS
)));
932 if (haveHyphenation
) {
933 if (hyphenBuffer
.AppendElements(bufferRange
.Length(), fallible
)) {
934 aProvider
->GetHyphenationBreaks(bufferRange
, hyphenBuffer
.Elements());
935 if (aProvider
->GetHyphensOption() == StyleHyphens::Auto
) {
936 ClassifyAutoHyphenations(aStart
, bufferRange
, hyphenBuffer
, &wordState
);
939 haveHyphenation
= false;
944 gfxFloat advance
= 0;
945 // The number of space characters that can be trimmed or hang at a soft-wrap
946 uint32_t trimmableChars
= 0;
947 // The amount of space removed by ignoring trimmableChars
948 gfxFloat trimmableAdvance
= 0;
949 int32_t lastBreak
= -1;
950 int32_t lastBreakTrimmableChars
= -1;
951 gfxFloat lastBreakTrimmableAdvance
= -1;
952 // Cache the last candidate break
953 int32_t lastCandidateBreak
= -1;
954 int32_t lastCandidateBreakTrimmableChars
= -1;
955 gfxFloat lastCandidateBreakTrimmableAdvance
= -1;
956 bool lastCandidateBreakUsedHyphenation
= false;
957 gfxBreakPriority lastCandidateBreakPriority
= gfxBreakPriority::eNoBreak
;
958 bool aborted
= false;
959 uint32_t end
= aStart
+ aMaxLength
;
960 bool lastBreakUsedHyphenation
= false;
961 Range
ligatureRange(aStart
, end
);
962 ShrinkToLigatureBoundaries(&ligatureRange
);
964 // We may need to move `i` backwards in the following loop, and re-scan
965 // part of the textrun; we'll use `rescanLimit` so we can tell when that
966 // is happening: if `i < rescanLimit` then we're rescanning.
967 uint32_t rescanLimit
= aStart
;
968 for (uint32_t i
= aStart
; i
< end
; ++i
) {
969 if (i
>= bufferRange
.end
) {
970 // Fetch more spacing and hyphenation data
971 uint32_t oldHyphenBufferLength
= hyphenBuffer
.Length();
972 bufferRange
.start
= i
;
974 std::min(aStart
+ aMaxLength
, i
+ MEASUREMENT_BUFFER_SIZE
);
975 // For spacing, we always overwrite the old data with the newly
976 // fetched one. However, for hyphenation, hyphenation data sometimes
977 // depends on the context in every word (if "hyphens: auto" is set).
978 // To ensure we get enough information between neighboring buffers,
979 // we grow the hyphenBuffer instead of overwrite it.
980 // NOTE that this means bufferRange does not correspond to the
981 // entire hyphenBuffer, but only to the most recently added portion.
982 // Therefore, we need to add the old length to hyphenBuffer.Elements()
983 // when getting more data.
985 GetAdjustedSpacing(this, bufferRange
, aProvider
, spacingBuffer
);
987 if (haveHyphenation
) {
988 if (hyphenBuffer
.AppendElements(bufferRange
.Length(), fallible
)) {
989 aProvider
->GetHyphenationBreaks(
990 bufferRange
, hyphenBuffer
.Elements() + oldHyphenBufferLength
);
991 if (aProvider
->GetHyphensOption() == StyleHyphens::Auto
) {
992 uint32_t prevMostRecentWordBoundary
= wordState
.mostRecentBoundary
;
993 ClassifyAutoHyphenations(aStart
, bufferRange
, hyphenBuffer
,
995 // If the buffer boundary is in the middle of a word,
996 // we need to go back to the start of the current word.
997 // So, we can correct the wrong candidates that we set
998 // in the previous runs of the loop.
999 if (prevMostRecentWordBoundary
< oldHyphenBufferLength
) {
1001 i
= prevMostRecentWordBoundary
- 1;
1006 haveHyphenation
= false;
1011 // There can't be a word-wrap break opportunity at the beginning of the
1012 // line: if the width is too small for even one character to fit, it
1013 // could be the first and last break opportunity on the line, and that
1014 // would trigger an infinite loop.
1015 if (aSuppressBreak
!= eSuppressAllBreaks
&&
1016 (aSuppressBreak
!= eSuppressInitialBreak
|| i
> aStart
)) {
1017 bool atNaturalBreak
= mCharacterGlyphs
[i
].CanBreakBefore() == 1;
1018 // atHyphenationBreak indicates we're at a "soft" hyphen, where an extra
1019 // hyphen glyph will need to be painted. It is NOT set for breaks at an
1020 // explicit hyphen present in the text.
1022 // NOTE(emilio): If you change this condition you also need to change
1023 // nsTextFrame::AddInlineMinISizeForFlow to match.
1024 bool atHyphenationBreak
= !atNaturalBreak
&& haveHyphenation
&&
1025 IsOptionalHyphenBreak(hyphenBuffer
[i
- aStart
]);
1026 bool atAutoHyphenWithManualHyphenInSameWord
=
1027 atHyphenationBreak
&&
1028 hyphenBuffer
[i
- aStart
] == HyphenType::AutoWithManualInSameWord
;
1029 bool atBreak
= atNaturalBreak
|| atHyphenationBreak
;
1030 bool wordWrapping
= aCanWordWrap
&&
1031 mCharacterGlyphs
[i
].IsClusterStart() &&
1032 *aBreakPriority
<= gfxBreakPriority::eWordWrapBreak
;
1034 bool whitespaceWrapping
= false;
1036 // The spec says the breaking opportunity is *after* whitespace.
1037 auto const& g
= mCharacterGlyphs
[i
- 1];
1038 whitespaceWrapping
=
1039 aCanWhitespaceWrap
&&
1040 (g
.CharIsSpace() || g
.CharIsTab() || g
.CharIsNewline());
1043 if (atBreak
|| wordWrapping
|| whitespaceWrapping
) {
1044 gfxFloat hyphenatedAdvance
= advance
;
1045 if (atHyphenationBreak
) {
1046 hyphenatedAdvance
+= aProvider
->GetHyphenWidth();
1049 if (lastBreak
< 0 ||
1050 width
+ hyphenatedAdvance
- trimmableAdvance
<= aWidth
) {
1051 // We can break here.
1053 lastBreakTrimmableChars
= trimmableChars
;
1054 lastBreakTrimmableAdvance
= trimmableAdvance
;
1055 lastBreakUsedHyphenation
= atHyphenationBreak
;
1056 *aBreakPriority
= (atBreak
|| whitespaceWrapping
)
1057 ? gfxBreakPriority::eNormalBreak
1058 : gfxBreakPriority::eWordWrapBreak
;
1063 if (width
- trimmableAdvance
> aWidth
) {
1064 // No more text fits. Abort
1068 // There are various kinds of break opportunities:
1069 // 1. word wrap break,
1070 // 2. natural break,
1071 // 3. manual hyphenation break,
1072 // 4. auto hyphenation break without any manual hyphenation
1073 // in the same word,
1074 // 5. auto hyphenation break with another manual hyphenation
1075 // in the same word.
1076 // Allow all of them except the last one to be a candidate.
1077 // So, we can ensure that we don't use an automatic
1078 // hyphenation opportunity within a word that contains another
1079 // manual hyphenation, unless it is the only choice.
1080 if (wordWrapping
|| !atAutoHyphenWithManualHyphenInSameWord
) {
1081 lastCandidateBreak
= lastBreak
;
1082 lastCandidateBreakTrimmableChars
= lastBreakTrimmableChars
;
1083 lastCandidateBreakTrimmableAdvance
= lastBreakTrimmableAdvance
;
1084 lastCandidateBreakUsedHyphenation
= lastBreakUsedHyphenation
;
1085 lastCandidateBreakPriority
= *aBreakPriority
;
1090 // If we're re-scanning part of a word (to re-process potential
1091 // hyphenation types) then we don't want to accumulate widths again
1092 // for the characters that were already added to `advance`.
1093 if (i
< rescanLimit
) {
1097 gfxFloat charAdvance
;
1098 if (i
>= ligatureRange
.start
&& i
< ligatureRange
.end
) {
1099 charAdvance
= GetAdvanceForGlyphs(Range(i
, i
+ 1));
1101 PropertyProvider::Spacing
* space
=
1102 &spacingBuffer
[i
- bufferRange
.start
];
1103 charAdvance
+= space
->mBefore
+ space
->mAfter
;
1106 charAdvance
= ComputePartialLigatureWidth(Range(i
, i
+ 1), aProvider
);
1109 advance
+= charAdvance
;
1110 if (aTrimWhitespace
|| aWhitespaceCanHang
) {
1111 if (mCharacterGlyphs
[i
].CharIsSpace()) {
1113 trimmableAdvance
+= charAdvance
;
1115 trimmableAdvance
= 0;
1125 // There are three possibilities:
1126 // 1) all the text fit (width <= aWidth)
1127 // 2) some of the text fit up to a break opportunity (width > aWidth &&
1129 // 3) none of the text fits before a break opportunity (width > aWidth &&
1132 bool usedHyphenation
= false;
1133 if (width
- trimmableAdvance
<= aWidth
) {
1134 charsFit
= aMaxLength
;
1135 } else if (lastBreak
>= 0) {
1136 if (lastCandidateBreak
>= 0 && lastCandidateBreak
!= lastBreak
) {
1137 lastBreak
= lastCandidateBreak
;
1138 lastBreakTrimmableChars
= lastCandidateBreakTrimmableChars
;
1139 lastBreakTrimmableAdvance
= lastCandidateBreakTrimmableAdvance
;
1140 lastBreakUsedHyphenation
= lastCandidateBreakUsedHyphenation
;
1141 *aBreakPriority
= lastCandidateBreakPriority
;
1143 charsFit
= lastBreak
- aStart
;
1144 trimmableChars
= lastBreakTrimmableChars
;
1145 trimmableAdvance
= lastBreakTrimmableAdvance
;
1146 usedHyphenation
= lastBreakUsedHyphenation
;
1148 charsFit
= aMaxLength
;
1152 auto fitEnd
= aStart
+ charsFit
;
1153 // Initially, measure everything, so that our bounding box includes
1154 // any trimmable or hanging whitespace.
1155 *aMetrics
= MeasureText(Range(aStart
, fitEnd
), aBoundingBoxType
,
1156 aRefDrawTarget
, aProvider
);
1157 if (aTrimWhitespace
|| aWhitespaceCanHang
) {
1158 // Measure trailing whitespace that is to be trimmed/hung.
1159 Metrics trimOrHangMetrics
=
1160 MeasureText(Range(fitEnd
- trimmableChars
, fitEnd
), aBoundingBoxType
,
1161 aRefDrawTarget
, aProvider
);
1162 if (aTrimWhitespace
) {
1163 aMetrics
->mAdvanceWidth
-= trimOrHangMetrics
.mAdvanceWidth
;
1164 } else if (aMetrics
->mAdvanceWidth
> aWidth
) {
1165 // Restrict width of hanging whitespace so it doesn't overflow.
1166 aMetrics
->mAdvanceWidth
= std::max(
1167 aWidth
, aMetrics
->mAdvanceWidth
- trimOrHangMetrics
.mAdvanceWidth
);
1171 if (aTrimWhitespace
) {
1172 *aTrimWhitespace
= trimmableAdvance
;
1174 if (aUsedHyphenation
) {
1175 *aUsedHyphenation
= usedHyphenation
;
1177 if (aLastBreak
&& charsFit
== aMaxLength
) {
1178 if (lastBreak
< 0) {
1179 *aLastBreak
= UINT32_MAX
;
1181 *aLastBreak
= lastBreak
- aStart
;
1188 gfxFloat
gfxTextRun::GetAdvanceWidth(
1189 Range aRange
, PropertyProvider
* aProvider
,
1190 PropertyProvider::Spacing
* aSpacing
) const {
1191 NS_ASSERTION(aRange
.end
<= GetLength(), "Substring out of range");
1193 Range ligatureRange
= aRange
;
1194 ShrinkToLigatureBoundaries(&ligatureRange
);
1196 gfxFloat result
= ComputePartialLigatureWidth(
1197 Range(aRange
.start
, ligatureRange
.start
), aProvider
) +
1198 ComputePartialLigatureWidth(
1199 Range(ligatureRange
.end
, aRange
.end
), aProvider
);
1202 aSpacing
->mBefore
= aSpacing
->mAfter
= 0;
1205 // Account for all remaining spacing here. This is more efficient than
1206 // processing it along with the glyphs.
1207 if (aProvider
&& (mFlags
& gfx::ShapedTextFlags::TEXT_ENABLE_SPACING
)) {
1209 AutoTArray
<PropertyProvider::Spacing
, 200> spacingBuffer
;
1210 if (spacingBuffer
.AppendElements(aRange
.Length(), fallible
)) {
1211 GetAdjustedSpacing(this, ligatureRange
, aProvider
,
1212 spacingBuffer
.Elements());
1213 for (i
= 0; i
< ligatureRange
.Length(); ++i
) {
1214 PropertyProvider::Spacing
* space
= &spacingBuffer
[i
];
1215 result
+= space
->mBefore
+ space
->mAfter
;
1218 aSpacing
->mBefore
= spacingBuffer
[0].mBefore
;
1219 aSpacing
->mAfter
= spacingBuffer
.LastElement().mAfter
;
1224 return result
+ GetAdvanceForGlyphs(ligatureRange
);
1227 gfxFloat
gfxTextRun::GetMinAdvanceWidth(Range aRange
) {
1228 MOZ_ASSERT(aRange
.end
<= GetLength(), "Substring out of range");
1230 Range ligatureRange
= aRange
;
1231 ShrinkToLigatureBoundaries(&ligatureRange
);
1234 std::max(ComputePartialLigatureWidth(
1235 Range(aRange
.start
, ligatureRange
.start
), nullptr),
1236 ComputePartialLigatureWidth(Range(ligatureRange
.end
, aRange
.end
),
1239 // Compute min advance width by assuming each grapheme cluster takes its own
1241 gfxFloat clusterAdvance
= 0;
1242 for (uint32_t i
= ligatureRange
.start
; i
< ligatureRange
.end
; ++i
) {
1243 if (mCharacterGlyphs
[i
].CharIsSpace()) {
1244 // Skip space char to prevent its advance width contributing to the
1245 // result. That is, don't consider a space can be in its own line.
1248 clusterAdvance
+= GetAdvanceForGlyph(i
);
1249 if (i
+ 1 == ligatureRange
.end
|| IsClusterStart(i
+ 1)) {
1250 result
= std::max(result
, clusterAdvance
);
1258 bool gfxTextRun::SetLineBreaks(Range aRange
, bool aLineBreakBefore
,
1259 bool aLineBreakAfter
,
1260 gfxFloat
* aAdvanceWidthDelta
) {
1261 // Do nothing because our shaping does not currently take linebreaks into
1262 // account. There is no change in advance width.
1263 if (aAdvanceWidthDelta
) {
1264 *aAdvanceWidthDelta
= 0;
1269 uint32_t gfxTextRun::FindFirstGlyphRunContaining(uint32_t aOffset
) const {
1270 NS_ASSERTION(aOffset
<= GetLength(), "Bad offset looking for glyphrun");
1271 NS_ASSERTION(GetLength() == 0 ||
1272 (!mHasGlyphRunArray
&& mSingleGlyphRun
.mFont
) ||
1273 (mHasGlyphRunArray
&& mGlyphRunArray
.Length() > 0),
1274 "non-empty text but no glyph runs present!");
1275 if (!mHasGlyphRunArray
) {
1278 if (aOffset
== GetLength()) {
1279 return mGlyphRunArray
.Length();
1282 uint32_t end
= mGlyphRunArray
.Length();
1283 while (end
- start
> 1) {
1284 uint32_t mid
= (start
+ end
) / 2;
1285 if (mGlyphRunArray
[mid
].mCharacterOffset
<= aOffset
) {
1291 NS_ASSERTION(mGlyphRunArray
[start
].mCharacterOffset
<= aOffset
,
1292 "Hmm, something went wrong, aOffset should have been found");
1296 void gfxTextRun::AddGlyphRun(gfxFont
* aFont
, FontMatchType aMatchType
,
1297 uint32_t aUTF16Offset
, bool aForceNewRun
,
1298 gfx::ShapedTextFlags aOrientation
, bool aIsCJK
) {
1299 NS_ASSERTION(aFont
, "adding glyph run for null font!");
1300 NS_ASSERTION(aOrientation
!= gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED
,
1301 "mixed orientation should have been resolved");
1305 if (!mHasGlyphRunArray
) {
1306 // We don't currently have an array.
1307 if (!mSingleGlyphRun
.mFont
) {
1308 // This is the first glyph run: just store it directly.
1309 mSingleGlyphRun
.SetProperties(aFont
, aOrientation
, aIsCJK
, aMatchType
);
1310 mSingleGlyphRun
.mCharacterOffset
= aUTF16Offset
;
1314 uint32_t numGlyphRuns
= mHasGlyphRunArray
? mGlyphRunArray
.Length() : 1;
1315 if (!aForceNewRun
&& numGlyphRuns
> 0) {
1316 GlyphRun
* lastGlyphRun
= mHasGlyphRunArray
1317 ? &mGlyphRunArray
[numGlyphRuns
- 1]
1320 NS_ASSERTION(lastGlyphRun
->mCharacterOffset
<= aUTF16Offset
,
1321 "Glyph runs out of order (and run not forced)");
1323 // Don't append a run if the font is already the one we want
1324 if (lastGlyphRun
->Matches(aFont
, aOrientation
, aIsCJK
, aMatchType
)) {
1328 // If the offset has not changed, avoid leaving a zero-length run
1329 // by overwriting the last entry instead of appending...
1330 if (lastGlyphRun
->mCharacterOffset
== aUTF16Offset
) {
1331 // ...except that if the run before the last entry had the same
1332 // font as the new one wants, merge with it instead of creating
1333 // adjacent runs with the same font
1334 if (numGlyphRuns
> 1 && mGlyphRunArray
[numGlyphRuns
- 2].Matches(
1335 aFont
, aOrientation
, aIsCJK
, aMatchType
)) {
1336 mGlyphRunArray
.TruncateLength(numGlyphRuns
- 1);
1337 if (mGlyphRunArray
.Length() == 1) {
1338 ConvertFromGlyphRunArray();
1343 lastGlyphRun
->SetProperties(aFont
, aOrientation
, aIsCJK
, aMatchType
);
1349 aForceNewRun
|| numGlyphRuns
> 0 || aUTF16Offset
== 0,
1350 "First run doesn't cover the first character (and run not forced)?");
1352 if (!mHasGlyphRunArray
) {
1353 ConvertToGlyphRunArray();
1356 GlyphRun
* glyphRun
= mGlyphRunArray
.AppendElement();
1357 glyphRun
->SetProperties(aFont
, aOrientation
, aIsCJK
, aMatchType
);
1358 glyphRun
->mCharacterOffset
= aUTF16Offset
;
1361 void gfxTextRun::SortGlyphRuns() {
1362 if (!mHasGlyphRunArray
) {
1366 // We should never have an empty or one-element array here; if there's only
1367 // one glyphrun, it should be stored directly in the textrun without using
1369 MOZ_ASSERT(mGlyphRunArray
.Length() > 1);
1371 AutoTArray
<GlyphRun
, 16> runs(std::move(mGlyphRunArray
));
1372 GlyphRunOffsetComparator comp
;
1375 // Now copy back, coalescing adjacent glyph runs that have the same
1377 mGlyphRunArray
.Clear();
1378 GlyphRun
* prevRun
= nullptr;
1379 for (auto& run
: runs
) {
1380 // A GlyphRun with the same font and orientation as the previous can
1381 // just be skipped; the last GlyphRun will cover its character range.
1382 MOZ_ASSERT(run
.mFont
!= nullptr);
1383 if (!prevRun
|| !prevRun
->Matches(run
.mFont
, run
.mOrientation
, run
.mIsCJK
,
1385 // If two font runs have the same character offset, Sort() will have
1386 // randomized their order!
1387 MOZ_ASSERT(prevRun
== nullptr ||
1388 prevRun
->mCharacterOffset
< run
.mCharacterOffset
,
1389 "Two fonts for the same run, glyph indices unreliable");
1390 prevRun
= mGlyphRunArray
.AppendElement(std::move(run
));
1394 MOZ_ASSERT(mGlyphRunArray
.Length() > 0);
1395 if (mGlyphRunArray
.Length() == 1) {
1396 ConvertFromGlyphRunArray();
1400 // Note that SanitizeGlyphRuns scans all glyph runs in the textrun;
1401 // therefore we only call it once, at the end of textrun construction,
1402 // NOT incrementally as each glyph run is added (bug 680402).
1403 void gfxTextRun::SanitizeGlyphRuns() {
1404 if (!mHasGlyphRunArray
) {
1408 MOZ_ASSERT(mGlyphRunArray
.Length() > 1);
1410 // If any glyph run starts with ligature-continuation characters, we need to
1411 // advance it to the first "real" character to avoid drawing partial ligature
1412 // glyphs from wrong font (seen with U+FEFF in reftest 474417-1, as Core Text
1413 // eliminates the glyph, which makes it appear as if a ligature has been
1415 int32_t i
, lastRunIndex
= mGlyphRunArray
.Length() - 1;
1416 const CompressedGlyph
* charGlyphs
= mCharacterGlyphs
;
1417 for (i
= lastRunIndex
; i
>= 0; --i
) {
1418 GlyphRun
& run
= mGlyphRunArray
[i
];
1419 while (charGlyphs
[run
.mCharacterOffset
].IsLigatureContinuation() &&
1420 run
.mCharacterOffset
< GetLength()) {
1421 run
.mCharacterOffset
++;
1423 // if the run has become empty, eliminate it
1424 if ((i
< lastRunIndex
&&
1425 run
.mCharacterOffset
>= mGlyphRunArray
[i
+ 1].mCharacterOffset
) ||
1426 (i
== lastRunIndex
&& run
.mCharacterOffset
== GetLength())) {
1427 mGlyphRunArray
.RemoveElementAt(i
);
1432 MOZ_ASSERT(mGlyphRunArray
.Length() > 0);
1433 if (mGlyphRunArray
.Length() == 1) {
1434 ConvertFromGlyphRunArray();
1438 void gfxTextRun::CopyGlyphDataFrom(gfxShapedWord
* aShapedWord
,
1440 uint32_t wordLen
= aShapedWord
->GetLength();
1441 NS_ASSERTION(aOffset
+ wordLen
<= GetLength(),
1442 "word overruns end of textrun!");
1444 CompressedGlyph
* charGlyphs
= GetCharacterGlyphs();
1445 const CompressedGlyph
* wordGlyphs
= aShapedWord
->GetCharacterGlyphs();
1446 if (aShapedWord
->HasDetailedGlyphs()) {
1447 for (uint32_t i
= 0; i
< wordLen
; ++i
, ++aOffset
) {
1448 const CompressedGlyph
& g
= wordGlyphs
[i
];
1449 if (!g
.IsSimpleGlyph()) {
1450 const DetailedGlyph
* details
=
1451 g
.GetGlyphCount() > 0 ? aShapedWord
->GetDetailedGlyphs(i
) : nullptr;
1452 SetDetailedGlyphs(aOffset
, g
.GetGlyphCount(), details
);
1454 charGlyphs
[aOffset
] = g
;
1457 memcpy(charGlyphs
+ aOffset
, wordGlyphs
, wordLen
* sizeof(CompressedGlyph
));
1461 void gfxTextRun::CopyGlyphDataFrom(gfxTextRun
* aSource
, Range aRange
,
1463 NS_ASSERTION(aRange
.end
<= aSource
->GetLength(),
1464 "Source substring out of range");
1465 NS_ASSERTION(aDest
+ aRange
.Length() <= GetLength(),
1466 "Destination substring out of range");
1468 if (aSource
->mDontSkipDrawing
) {
1469 mDontSkipDrawing
= true;
1472 // Copy base glyph data, and DetailedGlyph data where present
1473 const CompressedGlyph
* srcGlyphs
= aSource
->mCharacterGlyphs
+ aRange
.start
;
1474 CompressedGlyph
* dstGlyphs
= mCharacterGlyphs
+ aDest
;
1475 for (uint32_t i
= 0; i
< aRange
.Length(); ++i
) {
1476 CompressedGlyph g
= srcGlyphs
[i
];
1477 g
.SetCanBreakBefore(!g
.IsClusterStart()
1478 ? CompressedGlyph::FLAG_BREAK_TYPE_NONE
1479 : dstGlyphs
[i
].CanBreakBefore());
1480 if (!g
.IsSimpleGlyph()) {
1481 uint32_t count
= g
.GetGlyphCount();
1483 // DetailedGlyphs allocation is infallible, so this should never be
1484 // null unless the source textrun is somehow broken.
1485 DetailedGlyph
* src
= aSource
->GetDetailedGlyphs(i
+ aRange
.start
);
1486 MOZ_ASSERT(src
, "missing DetailedGlyphs?");
1488 DetailedGlyph
* dst
= AllocateDetailedGlyphs(i
+ aDest
, count
);
1489 ::memcpy(dst
, src
, count
* sizeof(DetailedGlyph
));
1499 GlyphRunIterator
iter(aSource
, aRange
);
1501 GlyphRun
* prevRun
= nullptr;
1503 while (iter
.NextRun()) {
1504 gfxFont
* font
= iter
.GetGlyphRun()->mFont
;
1505 MOZ_ASSERT(!prevRun
|| !prevRun
->Matches(iter
.GetGlyphRun()->mFont
,
1506 iter
.GetGlyphRun()->mOrientation
,
1507 iter
.GetGlyphRun()->mIsCJK
,
1508 FontMatchType::Kind::kUnspecified
),
1509 "Glyphruns not coalesced?");
1511 prevRun
= const_cast<GlyphRun
*>(iter
.GetGlyphRun());
1512 uint32_t end
= iter
.GetStringEnd();
1514 uint32_t start
= iter
.GetStringStart();
1516 // These used to be NS_ASSERTION()s, but WARNING is more appropriate.
1517 // Although it's unusual (and not desirable), it's possible for us to assign
1518 // different fonts to a base character and a following diacritic.
1519 // Example on OSX 10.5/10.6 with default fonts installed:
1520 // data:text/html,<p style="font-family:helvetica, arial, sans-serif;">
1521 // &%23x043E;&%23x0486;&%23x20;&%23x043E;&%23x0486;
1522 // This means the rendering of the cluster will probably not be very good,
1523 // but it's the best we can do for now if the specified font only covered
1524 // the initial base character and not its applied marks.
1525 NS_WARNING_ASSERTION(aSource
->IsClusterStart(start
),
1526 "Started font run in the middle of a cluster");
1527 NS_WARNING_ASSERTION(
1528 end
== aSource
->GetLength() || aSource
->IsClusterStart(end
),
1529 "Ended font run in the middle of a cluster");
1531 AddGlyphRun(font
, iter
.GetGlyphRun()->mMatchType
,
1532 start
- aRange
.start
+ aDest
, false,
1533 iter
.GetGlyphRun()->mOrientation
, iter
.GetGlyphRun()->mIsCJK
);
1537 void gfxTextRun::ClearGlyphsAndCharacters() {
1539 memset(reinterpret_cast<char*>(mCharacterGlyphs
), 0,
1540 mLength
* sizeof(CompressedGlyph
));
1541 mDetailedGlyphs
= nullptr;
1544 void gfxTextRun::SetSpaceGlyph(gfxFont
* aFont
, DrawTarget
* aDrawTarget
,
1545 uint32_t aCharIndex
,
1546 gfx::ShapedTextFlags aOrientation
) {
1547 if (SetSpaceGlyphIfSimple(aFont
, aCharIndex
, ' ', aOrientation
)) {
1551 aFont
->InitWordCache();
1552 static const uint8_t space
= ' ';
1553 gfx::ShapedTextFlags flags
=
1554 gfx::ShapedTextFlags::TEXT_IS_8BIT
| aOrientation
;
1556 !!(GetFlags() & gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT
);
1557 gfxFontShaper::RoundingFlags roundingFlags
=
1558 aFont
->GetRoundOffsetsToPixels(aDrawTarget
);
1559 gfxShapedWord
* sw
= aFont
->GetShapedWord(
1560 aDrawTarget
, &space
, 1, gfxShapedWord::HashMix(0, ' '), Script::LATIN
,
1561 /* aLanguage = */ nullptr, vertical
, mAppUnitsPerDevUnit
, flags
,
1562 roundingFlags
, nullptr);
1564 const GlyphRun
* prevRun
= TrailingGlyphRun();
1565 bool isCJK
= prevRun
&& prevRun
->mFont
== aFont
&&
1566 prevRun
->mOrientation
== aOrientation
1569 AddGlyphRun(aFont
, FontMatchType::Kind::kUnspecified
, aCharIndex
, false,
1570 aOrientation
, isCJK
);
1571 CopyGlyphDataFrom(sw
, aCharIndex
);
1572 GetCharacterGlyphs()[aCharIndex
].SetIsSpace();
1576 bool gfxTextRun::SetSpaceGlyphIfSimple(gfxFont
* aFont
, uint32_t aCharIndex
,
1577 char16_t aSpaceChar
,
1578 gfx::ShapedTextFlags aOrientation
) {
1579 uint32_t spaceGlyph
= aFont
->GetSpaceGlyph();
1580 if (!spaceGlyph
|| !CompressedGlyph::IsSimpleGlyphID(spaceGlyph
)) {
1584 gfxFont::Orientation fontOrientation
=
1585 (aOrientation
& gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT
)
1586 ? nsFontMetrics::eVertical
1587 : nsFontMetrics::eHorizontal
;
1588 uint32_t spaceWidthAppUnits
= NS_lroundf(
1589 aFont
->GetMetrics(fontOrientation
).spaceWidth
* mAppUnitsPerDevUnit
);
1590 if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits
)) {
1594 const GlyphRun
* prevRun
= TrailingGlyphRun();
1595 bool isCJK
= prevRun
&& prevRun
->mFont
== aFont
&&
1596 prevRun
->mOrientation
== aOrientation
1599 AddGlyphRun(aFont
, FontMatchType::Kind::kUnspecified
, aCharIndex
, false,
1600 aOrientation
, isCJK
);
1602 CompressedGlyph::MakeSimpleGlyph(spaceWidthAppUnits
, spaceGlyph
);
1603 if (aSpaceChar
== ' ') {
1606 GetCharacterGlyphs()[aCharIndex
] = g
;
1610 void gfxTextRun::FetchGlyphExtents(DrawTarget
* aRefDrawTarget
) {
1611 bool needsGlyphExtents
= NeedsGlyphExtents(this);
1612 if (!needsGlyphExtents
&& !mDetailedGlyphs
) return;
1615 const GlyphRun
* glyphRuns
= GetGlyphRuns(&runCount
);
1616 CompressedGlyph
* charGlyphs
= mCharacterGlyphs
;
1617 for (uint32_t i
= 0; i
< runCount
; ++i
) {
1618 const GlyphRun
& run
= glyphRuns
[i
];
1619 gfxFont
* font
= run
.mFont
;
1620 if (MOZ_UNLIKELY(font
->GetStyle()->AdjustedSizeMustBeZero())) {
1624 uint32_t start
= run
.mCharacterOffset
;
1626 i
+ 1 < runCount
? glyphRuns
[i
+ 1].mCharacterOffset
: GetLength();
1628 gfxGlyphExtents
* extents
=
1629 font
->GetOrCreateGlyphExtents(mAppUnitsPerDevUnit
);
1631 for (j
= start
; j
< end
; ++j
) {
1632 const gfxTextRun::CompressedGlyph
* glyphData
= &charGlyphs
[j
];
1633 if (glyphData
->IsSimpleGlyph()) {
1634 // If we're in speed mode, don't set up glyph extents here; we'll
1635 // just return "optimistic" glyph bounds later
1636 if (needsGlyphExtents
) {
1637 uint32_t glyphIndex
= glyphData
->GetSimpleGlyph();
1638 if (!extents
->IsGlyphKnown(glyphIndex
)) {
1639 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
1640 ++gGlyphExtentsSetupEagerSimple
;
1642 font
->SetupGlyphExtents(aRefDrawTarget
, glyphIndex
, false, extents
);
1645 } else if (!glyphData
->IsMissing()) {
1646 uint32_t glyphCount
= glyphData
->GetGlyphCount();
1647 if (glyphCount
== 0) {
1650 const gfxTextRun::DetailedGlyph
* details
= GetDetailedGlyphs(j
);
1654 for (uint32_t k
= 0; k
< glyphCount
; ++k
, ++details
) {
1655 uint32_t glyphIndex
= details
->mGlyphID
;
1656 if (!extents
->IsGlyphKnownWithTightExtents(glyphIndex
)) {
1657 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
1658 ++gGlyphExtentsSetupEagerTight
;
1660 font
->SetupGlyphExtents(aRefDrawTarget
, glyphIndex
, true, extents
);
1668 size_t gfxTextRun::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf
) {
1669 // The second arg is how much gfxTextRun::AllocateStorage would have
1671 size_t total
= mHasGlyphRunArray
1672 ? mGlyphRunArray
.ShallowSizeOfExcludingThis(aMallocSizeOf
)
1675 if (mDetailedGlyphs
) {
1676 total
+= mDetailedGlyphs
->SizeOfIncludingThis(aMallocSizeOf
);
1682 size_t gfxTextRun::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) {
1683 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf
);
1686 #ifdef DEBUG_FRAME_DUMP
1687 void gfxTextRun::Dump(FILE* out
) {
1688 # define APPEND_FLAG(string_, enum_, field_, flag_) \
1689 if (field_ & enum_::flag_) { \
1690 string_.AppendPrintf(remaining != field_ ? " %s" : "%s", #flag_); \
1691 remaining &= ~enum_::flag_; \
1693 # define APPEND_FLAGS(string_, enum_, field_, flags_) \
1695 auto remaining = field_; \
1696 MOZ_FOR_EACH(APPEND_FLAG, (string_, enum_, field_, ), flags_) \
1697 if (int(remaining)) { \
1698 string_.AppendPrintf(" %s(0x%0x)", #enum_, int(remaining)); \
1702 nsCString flagsString
;
1703 ShapedTextFlags orient
= mFlags
& ShapedTextFlags::TEXT_ORIENT_MASK
;
1704 ShapedTextFlags otherFlags
= mFlags
& ~ShapedTextFlags::TEXT_ORIENT_MASK
;
1705 APPEND_FLAGS(flagsString
, ShapedTextFlags
, otherFlags
,
1706 (TEXT_IS_RTL
, TEXT_ENABLE_SPACING
, TEXT_IS_8BIT
,
1707 TEXT_ENABLE_HYPHEN_BREAKS
, TEXT_NEED_BOUNDING_BOX
,
1708 TEXT_DISABLE_OPTIONAL_LIGATURES
, TEXT_OPTIMIZE_SPEED
,
1709 TEXT_HIDE_CONTROL_CHARACTERS
, TEXT_TRAILING_ARABICCHAR
,
1710 TEXT_INCOMING_ARABICCHAR
, TEXT_USE_MATH_SCRIPT
))
1712 if (orient
!= ShapedTextFlags::TEXT_ORIENT_HORIZONTAL
&&
1713 !flagsString
.IsEmpty()) {
1718 case ShapedTextFlags::TEXT_ORIENT_HORIZONTAL
:
1720 case ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT
:
1721 flagsString
+= "TEXT_ORIENT_VERTICAL_UPRIGHT";
1723 case ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT
:
1724 flagsString
+= "TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT";
1726 case ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED
:
1727 flagsString
+= "TEXT_ORIENT_VERTICAL_MIXED";
1729 case ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT
:
1730 flagsString
+= "TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT";
1733 flagsString
.AppendPrintf("UNKNOWN_TEXT_ORIENT_MASK(0x%0x)", int(orient
));
1737 nsCString flags2String
;
1739 flags2String
, nsTextFrameUtils::Flags
, mFlags2
,
1740 (HasTab
, HasShy
, DontSkipDrawingForPendingUserFonts
, IsSimpleFlow
,
1741 IncomingWhitespace
, TrailingWhitespace
, CompressedLeadingWhitespace
,
1742 NoBreaks
, IsTransformed
, HasTrailingBreak
, IsSingleCharMi
,
1743 MightHaveGlyphChanges
, RunSizeAccounted
))
1745 # undef APPEND_FLAGS
1749 mFontGroup
->Language()->ToUTF8String(lang
);
1750 fprintf(out
, "gfxTextRun@%p (length %u) [%s] [%s] [%s]\n", this, mLength
,
1751 flagsString
.get(), flags2String
.get(), lang
.get());
1753 uint32_t numGlyphRuns
;
1754 const GlyphRun
* glyphRuns
= GetGlyphRuns(&numGlyphRuns
);
1755 fprintf(out
, " Glyph runs:\n");
1756 for (uint32_t i
= 0; i
< numGlyphRuns
; ++i
) {
1757 gfxFont
* font
= glyphRuns
[i
].mFont
;
1758 const gfxFontStyle
* style
= font
->GetStyle();
1759 nsAutoString styleString
;
1760 nsStyleUtil::AppendFontSlantStyle(style
->style
, styleString
);
1761 fprintf(out
, " [%d] offset=%d %s %f/%g/%s\n", i
,
1762 glyphRuns
[i
].mCharacterOffset
, font
->GetName().get(), style
->size
,
1763 style
->weight
.ToFloat(), NS_ConvertUTF16toUTF8(styleString
).get());
1766 fprintf(out
, " Glyphs:\n");
1767 for (uint32_t i
= 0; i
< mLength
; ++i
) {
1768 auto glyphData
= GetCharacterGlyphs()[i
];
1771 line
.AppendPrintf(" [%d] 0x%p %s", i
, GetCharacterGlyphs() + i
,
1772 glyphData
.IsSimpleGlyph() ? "simple" : "detailed");
1774 if (glyphData
.IsSimpleGlyph()) {
1775 line
.AppendPrintf(" id=%d adv=%d", glyphData
.GetSimpleGlyph(),
1776 glyphData
.GetSimpleAdvance());
1778 uint32_t count
= glyphData
.GetGlyphCount();
1781 for (uint32_t j
= 0; j
< count
; j
++) {
1782 line
.AppendPrintf(j
? ",%d" : "%d", GetDetailedGlyphs(i
)[j
].mGlyphID
);
1785 for (uint32_t j
= 0; j
< count
; j
++) {
1786 line
.AppendPrintf(j
? ",%d" : "%d", GetDetailedGlyphs(i
)[j
].mAdvance
);
1788 line
+= " offsets=";
1789 for (uint32_t j
= 0; j
< count
; j
++) {
1790 auto offset
= GetDetailedGlyphs(i
)[j
].mOffset
;
1791 line
.AppendPrintf(j
? ",(%g,%g)" : "(%g,%g)", offset
.x
, offset
.y
);
1794 line
+= " (no glyphs)";
1798 if (glyphData
.CharIsSpace()) {
1799 line
+= " CHAR_IS_SPACE";
1801 if (glyphData
.CharIsTab()) {
1802 line
+= " CHAR_IS_TAB";
1804 if (glyphData
.CharIsNewline()) {
1805 line
+= " CHAR_IS_NEWLINE";
1807 if (glyphData
.CharIsFormattingControl()) {
1808 line
+= " CHAR_IS_FORMATTING_CONTROL";
1810 if (glyphData
.CharTypeFlags() &
1811 CompressedGlyph::FLAG_CHAR_NO_EMPHASIS_MARK
) {
1812 line
+= " CHAR_NO_EMPHASIS_MARK";
1815 if (!glyphData
.IsSimpleGlyph()) {
1816 if (!glyphData
.IsMissing()) {
1817 line
+= " NOT_MISSING";
1819 if (!glyphData
.IsClusterStart()) {
1820 line
+= " NOT_IS_CLUSTER_START";
1822 if (!glyphData
.IsLigatureGroupStart()) {
1823 line
+= " NOT_LIGATURE_GROUP_START";
1827 switch (glyphData
.CanBreakBefore()) {
1828 case CompressedGlyph::FLAG_BREAK_TYPE_NORMAL
:
1829 line
+= " BREAK_TYPE_NORMAL";
1831 case CompressedGlyph::FLAG_BREAK_TYPE_HYPHEN
:
1832 line
+= " BREAK_TYPE_HYPHEN";
1836 fprintf(out
, "%s\n", line
.get());
1841 gfxFontGroup::gfxFontGroup(nsPresContext
* aPresContext
,
1842 const StyleFontFamilyList
& aFontFamilyList
,
1843 const gfxFontStyle
* aStyle
, nsAtom
* aLanguage
,
1844 bool aExplicitLanguage
,
1845 gfxTextPerfMetrics
* aTextPerf
,
1846 gfxUserFontSet
* aUserFontSet
, gfxFloat aDevToCssSize
)
1847 : mPresContext(aPresContext
), // Note that aPresContext may be null!
1848 mFamilyList(aFontFamilyList
),
1850 mLanguage(aLanguage
),
1851 mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET
),
1853 mDevToCssSize(aDevToCssSize
),
1854 mUserFontSet(aUserFontSet
),
1855 mTextPerf(aTextPerf
),
1856 mLastPrefLang(eFontPrefLang_Western
),
1857 mPageLang(gfxPlatformFontList::GetFontPrefLangFor(aLanguage
)),
1858 mLastPrefFirstFont(false),
1859 mSkipDrawing(false),
1860 mExplicitLanguage(aExplicitLanguage
) {
1861 // We don't use SetUserFontSet() here, as we want to unconditionally call
1862 // BuildFontList() rather than only do UpdateUserFonts() if it changed.
1863 mCurrGeneration
= GetGeneration();
1867 gfxFontGroup::~gfxFontGroup() {
1868 // Should not be dropped by stylo
1869 MOZ_ASSERT(NS_IsMainThread());
1872 static StyleGenericFontFamily
GetDefaultGeneric(nsAtom
* aLanguage
) {
1873 return StaticPresData::Get()
1874 ->GetFontPrefsForLang(aLanguage
)
1875 ->GetDefaultGeneric();
1878 void gfxFontGroup::BuildFontList() {
1879 // initialize fonts in the font family list
1880 AutoTArray
<FamilyAndGeneric
, 10> fonts
;
1881 gfxPlatformFontList
* pfl
= gfxPlatformFontList::PlatformFontList();
1883 // lookup fonts in the fontlist
1884 for (const StyleSingleFontFamily
& name
: mFamilyList
.list
.AsSpan()) {
1885 if (name
.IsFamilyName()) {
1886 const auto& familyName
= name
.AsFamilyName();
1887 AddPlatformFont(nsAtomCString(familyName
.name
.AsAtom()),
1888 familyName
.syntax
== StyleFontFamilyNameSyntax::Quoted
,
1891 MOZ_ASSERT(name
.IsGeneric());
1892 const StyleGenericFontFamily generic
= name
.AsGeneric();
1893 // system-ui is usually a single family, so it doesn't work great as
1894 // fallback. Prefer the following generic or the language default instead.
1895 if (mFallbackGeneric
== StyleGenericFontFamily::None
&&
1896 generic
!= StyleGenericFontFamily::SystemUi
) {
1897 mFallbackGeneric
= generic
;
1899 pfl
->AddGenericFonts(mPresContext
, generic
, mLanguage
, fonts
);
1901 mTextPerf
->current
.genericLookups
++;
1906 // If necessary, append default language generic onto the end.
1907 if (mFallbackGeneric
== StyleGenericFontFamily::None
&& !mStyle
.systemFont
) {
1908 auto defaultLanguageGeneric
= GetDefaultGeneric(mLanguage
);
1910 pfl
->AddGenericFonts(mPresContext
, defaultLanguageGeneric
, mLanguage
,
1913 mTextPerf
->current
.genericLookups
++;
1917 // build the fontlist from the specified families
1918 for (const auto& f
: fonts
) {
1919 if (f
.mFamily
.mIsShared
) {
1920 AddFamilyToFontList(f
.mFamily
.mShared
, f
.mGeneric
);
1922 AddFamilyToFontList(f
.mFamily
.mUnshared
, f
.mGeneric
);
1926 mFontListGeneration
= pfl
->GetGeneration();
1929 void gfxFontGroup::AddPlatformFont(const nsACString
& aName
, bool aQuotedName
,
1930 nsTArray
<FamilyAndGeneric
>& aFamilyList
) {
1931 // First, look up in the user font set...
1932 // If the fontSet matches the family, we must not look for a platform
1933 // font of the same name, even if we fail to actually get a fontEntry
1934 // here; we'll fall back to the next name in the CSS font-family list.
1936 // Add userfonts to the fontlist whether already loaded
1937 // or not. Loading is initiated during font matching.
1938 gfxFontFamily
* family
= mUserFontSet
->LookupFamily(aName
);
1940 aFamilyList
.AppendElement(family
);
1945 // Not known in the user font set ==> check system fonts
1946 gfxPlatformFontList::PlatformFontList()->FindAndAddFamilies(
1947 mPresContext
, StyleGenericFontFamily::None
, aName
, &aFamilyList
,
1948 aQuotedName
? gfxPlatformFontList::FindFamiliesFlags::eQuotedFamilyName
1949 : gfxPlatformFontList::FindFamiliesFlags(0),
1950 &mStyle
, mLanguage
.get(), mDevToCssSize
);
1953 void gfxFontGroup::AddFamilyToFontList(gfxFontFamily
* aFamily
,
1954 StyleGenericFontFamily aGeneric
) {
1956 MOZ_ASSERT_UNREACHABLE("don't try to add a null font family!");
1959 AutoTArray
<gfxFontEntry
*, 4> fontEntryList
;
1960 aFamily
->FindAllFontsForStyle(mStyle
, fontEntryList
);
1961 // add these to the fontlist
1962 for (gfxFontEntry
* fe
: fontEntryList
) {
1964 FamilyFace
ff(aFamily
, fe
, aGeneric
);
1965 if (fe
->mIsUserFontContainer
) {
1966 ff
.CheckState(mSkipDrawing
);
1968 mFonts
.AppendElement(ff
);
1971 // for a family marked as "check fallback faces", only mark the last
1972 // entry so that fallbacks for a family are only checked once
1973 if (aFamily
->CheckForFallbackFaces() && !fontEntryList
.IsEmpty() &&
1974 !mFonts
.IsEmpty()) {
1975 mFonts
.LastElement().SetCheckForFallbackFaces();
1979 void gfxFontGroup::AddFamilyToFontList(fontlist::Family
* aFamily
,
1980 StyleGenericFontFamily aGeneric
) {
1981 gfxPlatformFontList
* pfl
= gfxPlatformFontList::PlatformFontList();
1982 if (!aFamily
->IsInitialized()) {
1983 if (!NS_IsMainThread()) {
1984 // If we need to initialize a Family record, but we're on a style
1985 // worker thread, we have to defer it.
1986 ServoStyleSet
* set
= ServoStyleSet::Current();
1988 set
->AppendTask(PostTraversalTask::InitializeFamily(aFamily
));
1989 set
->AppendTask(PostTraversalTask::FontInfoUpdate(set
));
1992 if (!pfl
->InitializeFamily(aFamily
)) {
1996 AutoTArray
<fontlist::Face
*, 4> faceList
;
1997 aFamily
->FindAllFacesForStyle(pfl
->SharedFontList(), mStyle
, faceList
);
1998 for (auto face
: faceList
) {
1999 gfxFontEntry
* fe
= pfl
->GetOrCreateFontEntry(face
, aFamily
);
2000 if (fe
&& !HasFont(fe
)) {
2001 FamilyFace
ff(aFamily
, fe
, aGeneric
);
2002 mFonts
.AppendElement(ff
);
2007 bool gfxFontGroup::HasFont(const gfxFontEntry
* aFontEntry
) {
2008 for (auto& f
: mFonts
) {
2009 if (f
.FontEntry() == aFontEntry
) {
2016 gfxFont
* gfxFontGroup::GetFontAt(int32_t i
, uint32_t aCh
, bool* aLoading
) {
2017 if (uint32_t(i
) >= mFonts
.Length()) {
2021 FamilyFace
& ff
= mFonts
[i
];
2022 if (ff
.IsInvalid() || ff
.IsLoading()) {
2026 gfxFont
* font
= ff
.Font();
2028 gfxFontEntry
* fe
= ff
.FontEntry();
2032 gfxCharacterMap
* unicodeRangeMap
= nullptr;
2033 if (fe
->mIsUserFontContainer
) {
2034 gfxUserFontEntry
* ufe
= static_cast<gfxUserFontEntry
*>(fe
);
2035 if (ufe
->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED
&&
2036 ufe
->CharacterInUnicodeRange(aCh
) && !*aLoading
) {
2038 ff
.CheckState(mSkipDrawing
);
2039 *aLoading
= ff
.IsLoading();
2041 fe
= ufe
->GetPlatformFontEntry();
2045 unicodeRangeMap
= ufe
->GetUnicodeRangeMap();
2047 font
= fe
->FindOrMakeFont(&mStyle
, unicodeRangeMap
);
2048 if (!font
|| !font
->Valid()) {
2050 // We can't just |delete font| here, in case there are other
2051 // references to the object FindOrMakeFont returned.
2052 RefPtr
<gfxFont
> ref(font
);
2060 void gfxFontGroup::FamilyFace::CheckState(bool& aSkipDrawing
) {
2061 gfxFontEntry
* fe
= FontEntry();
2065 if (fe
->mIsUserFontContainer
) {
2066 gfxUserFontEntry
* ufe
= static_cast<gfxUserFontEntry
*>(fe
);
2067 gfxUserFontEntry::UserFontLoadState state
= ufe
->LoadState();
2069 case gfxUserFontEntry::STATUS_LOAD_PENDING
:
2070 case gfxUserFontEntry::STATUS_LOADING
:
2073 case gfxUserFontEntry::STATUS_FAILED
:
2075 // fall-thru to the default case
2080 if (ufe
->WaitForUserFont()) {
2081 aSkipDrawing
= true;
2086 bool gfxFontGroup::FamilyFace::EqualsUserFont(
2087 const gfxUserFontEntry
* aUserFont
) const {
2088 gfxFontEntry
* fe
= FontEntry();
2089 // if there's a font, the entry is the underlying platform font
2091 gfxFontEntry
* pfe
= aUserFont
->GetPlatformFontEntry();
2095 } else if (fe
== aUserFont
) {
2101 static nsAutoCString
FamilyListToString(
2102 const StyleFontFamilyList
& aFamilyList
) {
2103 return StringJoin(","_ns
, aFamilyList
.list
.AsSpan(),
2104 [](nsACString
& dst
, const StyleSingleFontFamily
& name
) {
2105 name
.AppendToString(dst
);
2109 gfxFont
* gfxFontGroup::GetDefaultFont() {
2111 return mDefaultFont
.get();
2114 gfxPlatformFontList
* pfl
= gfxPlatformFontList::PlatformFontList();
2115 FontFamily family
= pfl
->GetDefaultFont(mPresContext
, &mStyle
);
2116 MOZ_ASSERT(!family
.IsNull(),
2117 "invalid default font returned by GetDefaultFont");
2119 gfxFontEntry
* fe
= nullptr;
2120 if (family
.mIsShared
) {
2121 fontlist::Family
* fam
= family
.mShared
;
2122 if (!fam
->IsInitialized()) {
2123 // If this fails, FindFaceForStyle will just safely return nullptr
2124 Unused
<< pfl
->InitializeFamily(fam
);
2126 fontlist::Face
* face
= fam
->FindFaceForStyle(pfl
->SharedFontList(), mStyle
);
2128 fe
= pfl
->GetOrCreateFontEntry(face
, fam
);
2131 fe
= family
.mUnshared
->FindFontForStyle(mStyle
);
2134 mDefaultFont
= fe
->FindOrMakeFont(&mStyle
);
2137 uint32_t numInits
, loaderState
;
2138 pfl
->GetFontlistInitInfo(numInits
, loaderState
);
2140 MOZ_ASSERT(numInits
!= 0,
2141 "must initialize system fontlist before getting default font!");
2143 uint32_t numFonts
= 0;
2144 if (!mDefaultFont
) {
2145 // Try for a "font of last resort...."
2146 // Because an empty font list would be Really Bad for later code
2147 // that assumes it will be able to get valid metrics for layout,
2148 // just look for the first usable font and put in the list.
2150 if (pfl
->SharedFontList()) {
2151 fontlist::FontList
* list
= pfl
->SharedFontList();
2152 numFonts
= list
->NumFamilies();
2153 fontlist::Family
* families
= list
->Families();
2154 for (uint32_t i
= 0; i
< numFonts
; ++i
) {
2155 fontlist::Family
* fam
= &families
[i
];
2156 if (!fam
->IsInitialized()) {
2157 Unused
<< pfl
->InitializeFamily(fam
);
2159 fontlist::Face
* face
=
2160 fam
->FindFaceForStyle(pfl
->SharedFontList(), mStyle
);
2162 fe
= pfl
->GetOrCreateFontEntry(face
, fam
);
2164 mDefaultFont
= fe
->FindOrMakeFont(&mStyle
);
2168 NS_WARNING("FindOrMakeFont failed");
2173 AutoTArray
<RefPtr
<gfxFontFamily
>, 200> familyList
;
2174 pfl
->GetFontFamilyList(familyList
);
2175 numFonts
= familyList
.Length();
2176 for (uint32_t i
= 0; i
< numFonts
; ++i
) {
2177 gfxFontEntry
* fe
= familyList
[i
]->FindFontForStyle(mStyle
, true);
2179 mDefaultFont
= fe
->FindOrMakeFont(&mStyle
);
2188 if (!mDefaultFont
&& pfl
->SharedFontList() && !XRE_IsParentProcess()) {
2189 // If we're a content process, it's possible this is failing because the
2190 // chrome process has just updated the shared font list and we haven't yet
2191 // refreshed our reference to it. If that's the case, update and retry.
2192 // But if we're not on the main thread, we can't do this, so just use
2193 // the platform default font directly.
2194 if (NS_IsMainThread()) {
2195 uint32_t oldGeneration
= pfl
->SharedFontList()->GetGeneration();
2196 pfl
->UpdateFontList();
2197 if (pfl
->SharedFontList()->GetGeneration() != oldGeneration
) {
2198 return GetDefaultFont();
2201 gfxFontEntry
* fe
= pfl
->GetDefaultFontEntry();
2203 gfxFont
* f
= fe
->FindOrMakeFont(&mStyle
);
2211 if (!mDefaultFont
) {
2212 // an empty font list at this point is fatal; we're not going to
2213 // be able to do even the most basic layout operations
2215 // annotate crash report with fontlist info
2216 nsAutoCString fontInitInfo
;
2217 fontInitInfo
.AppendPrintf("no fonts - init: %d fonts: %d loader: %d",
2218 numInits
, numFonts
, loaderState
);
2220 bool dwriteEnabled
= gfxWindowsPlatform::GetPlatform()->DWriteEnabled();
2221 double upTime
= (double)GetTickCount();
2222 fontInitInfo
.AppendPrintf(" backend: %s system-uptime: %9.3f sec",
2223 dwriteEnabled
? "directwrite" : "gdi",
2226 gfxCriticalError() << fontInitInfo
.get();
2228 char msg
[256]; // CHECK buffer length if revising message below
2229 SprintfLiteral(msg
, "unable to find a usable font (%.220s)",
2230 FamilyListToString(mFamilyList
).get());
2231 MOZ_CRASH_UNSAFE(msg
);
2234 return mDefaultFont
.get();
2237 gfxFont
* gfxFontGroup::GetFirstValidFont(uint32_t aCh
,
2238 StyleGenericFontFamily
* aGeneric
) {
2239 // Ensure cached font instances are valid.
2240 CheckForUpdatedPlatformList();
2242 uint32_t count
= mFonts
.Length();
2243 bool loading
= false;
2244 for (uint32_t i
= 0; i
< count
; ++i
) {
2245 FamilyFace
& ff
= mFonts
[i
];
2246 if (ff
.IsInvalid()) {
2250 // already have a font?
2251 gfxFont
* font
= ff
.Font();
2254 *aGeneric
= ff
.Generic();
2259 // Need to build a font, loading userfont if not loaded. In
2260 // cases where unicode range might apply, use the character
2262 gfxFontEntry
* fe
= ff
.FontEntry();
2263 if (fe
&& fe
->mIsUserFontContainer
) {
2264 gfxUserFontEntry
* ufe
= static_cast<gfxUserFontEntry
*>(fe
);
2265 bool inRange
= ufe
->CharacterInUnicodeRange(aCh
);
2268 ufe
->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED
) {
2270 ff
.CheckState(mSkipDrawing
);
2272 if (ff
.IsLoading()) {
2276 if (ufe
->LoadState() != gfxUserFontEntry::STATUS_LOADED
|| !inRange
) {
2281 font
= GetFontAt(i
, aCh
, &loading
);
2284 *aGeneric
= ff
.Generic();
2290 *aGeneric
= StyleGenericFontFamily::None
;
2292 return GetDefaultFont();
2295 gfxFont
* gfxFontGroup::GetFirstMathFont() {
2296 uint32_t count
= mFonts
.Length();
2297 for (uint32_t i
= 0; i
< count
; ++i
) {
2298 gfxFont
* font
= GetFontAt(i
);
2299 if (font
&& font
->TryGetMathTable()) {
2306 bool gfxFontGroup::IsInvalidChar(uint8_t ch
) {
2307 return ((ch
& 0x7f) < 0x20 || ch
== 0x7f);
2310 bool gfxFontGroup::IsInvalidChar(char16_t ch
) {
2311 // All printable 7-bit ASCII values are OK
2312 if (ch
>= ' ' && ch
< 0x7f) {
2315 // No point in sending non-printing control chars through font shaping
2319 // Word-separating format/bidi control characters are not shaped as part
2321 return (((ch
& 0xFF00) == 0x2000 /* Unicode control character */ &&
2322 (ch
== 0x200B /*ZWSP*/ || ch
== 0x2028 /*LSEP*/ ||
2323 ch
== 0x2029 /*PSEP*/ || ch
== 0x2060 /*WJ*/)) ||
2324 ch
== 0xfeff /*ZWNBSP*/ || IsBidiControl(ch
));
2327 already_AddRefed
<gfxTextRun
> gfxFontGroup::MakeEmptyTextRun(
2328 const Parameters
* aParams
, gfx::ShapedTextFlags aFlags
,
2329 nsTextFrameUtils::Flags aFlags2
) {
2330 aFlags
|= ShapedTextFlags::TEXT_IS_8BIT
;
2331 return gfxTextRun::Create(aParams
, 0, this, aFlags
, aFlags2
);
2334 already_AddRefed
<gfxTextRun
> gfxFontGroup::MakeSpaceTextRun(
2335 const Parameters
* aParams
, gfx::ShapedTextFlags aFlags
,
2336 nsTextFrameUtils::Flags aFlags2
) {
2337 aFlags
|= ShapedTextFlags::TEXT_IS_8BIT
;
2339 RefPtr
<gfxTextRun
> textRun
=
2340 gfxTextRun::Create(aParams
, 1, this, aFlags
, aFlags2
);
2345 gfx::ShapedTextFlags orientation
= aFlags
& ShapedTextFlags::TEXT_ORIENT_MASK
;
2346 if (orientation
== ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED
) {
2347 orientation
= ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT
;
2350 gfxFont
* font
= GetFirstValidFont();
2351 if (MOZ_UNLIKELY(GetStyle()->AdjustedSizeMustBeZero())) {
2352 // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
2353 // them, and always create at least size 1 fonts, i.e. they still
2354 // render something for size 0 fonts.
2355 textRun
->AddGlyphRun(font
, FontMatchType::Kind::kUnspecified
, 0, false,
2356 orientation
, false);
2358 if (font
->GetSpaceGlyph()) {
2359 // Normally, the font has a cached space glyph, so we can avoid
2360 // the cost of calling FindFontForChar.
2361 textRun
->SetSpaceGlyph(font
, aParams
->mDrawTarget
, 0, orientation
);
2363 // In case the primary font doesn't have <space> (bug 970891),
2364 // find one that does.
2365 FontMatchType matchType
;
2366 gfxFont
* spaceFont
=
2367 FindFontForChar(' ', 0, 0, Script::LATIN
, nullptr, &matchType
);
2369 textRun
->SetSpaceGlyph(spaceFont
, aParams
->mDrawTarget
, 0, orientation
);
2374 // Note that the gfxGlyphExtents glyph bounds storage for the font will
2375 // always contain an entry for the font's space glyph, so we don't have
2376 // to call FetchGlyphExtents here.
2377 return textRun
.forget();
2380 template <typename T
>
2381 already_AddRefed
<gfxTextRun
> gfxFontGroup::MakeBlankTextRun(
2382 const T
* aString
, uint32_t aLength
, const Parameters
* aParams
,
2383 gfx::ShapedTextFlags aFlags
, nsTextFrameUtils::Flags aFlags2
) {
2384 RefPtr
<gfxTextRun
> textRun
=
2385 gfxTextRun::Create(aParams
, aLength
, this, aFlags
, aFlags2
);
2390 gfx::ShapedTextFlags orientation
= aFlags
& ShapedTextFlags::TEXT_ORIENT_MASK
;
2391 if (orientation
== ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED
) {
2392 orientation
= ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT
;
2394 textRun
->AddGlyphRun(GetFirstValidFont(), FontMatchType::Kind::kUnspecified
,
2395 0, false, orientation
, false);
2397 for (uint32_t i
= 0; i
< aLength
; i
++) {
2398 if (aString
[i
] == '\n') {
2399 textRun
->SetIsNewline(i
);
2400 } else if (aString
[i
] == '\t') {
2401 textRun
->SetIsTab(i
);
2405 return textRun
.forget();
2408 already_AddRefed
<gfxTextRun
> gfxFontGroup::MakeHyphenTextRun(
2409 DrawTarget
* aDrawTarget
, gfx::ShapedTextFlags aFlags
,
2410 uint32_t aAppUnitsPerDevUnit
) {
2411 // only use U+2010 if it is supported by the first font in the group;
2412 // it's better to use ASCII '-' from the primary font than to fall back to
2413 // U+2010 from some other, possibly poorly-matching face
2414 static const char16_t hyphen
= 0x2010;
2415 gfxFont
* font
= GetFirstValidFont(uint32_t(hyphen
));
2416 if (font
->HasCharacter(hyphen
)) {
2417 return MakeTextRun(&hyphen
, 1, aDrawTarget
, aAppUnitsPerDevUnit
, aFlags
,
2418 nsTextFrameUtils::Flags(), nullptr);
2421 static const uint8_t dash
= '-';
2422 return MakeTextRun(&dash
, 1, aDrawTarget
, aAppUnitsPerDevUnit
, aFlags
,
2423 nsTextFrameUtils::Flags(), nullptr);
2426 gfxFloat
gfxFontGroup::GetHyphenWidth(
2427 const gfxTextRun::PropertyProvider
* aProvider
) {
2428 if (mHyphenWidth
< 0) {
2429 RefPtr
<DrawTarget
> dt(aProvider
->GetDrawTarget());
2431 RefPtr
<gfxTextRun
> hyphRun(
2432 MakeHyphenTextRun(dt
, aProvider
->GetShapedTextFlags(),
2433 aProvider
->GetAppUnitsPerDevUnit()));
2434 mHyphenWidth
= hyphRun
.get() ? hyphRun
->GetAdvanceWidth() : 0;
2437 return mHyphenWidth
;
2440 already_AddRefed
<gfxTextRun
> gfxFontGroup::MakeTextRun(
2441 const uint8_t* aString
, uint32_t aLength
, const Parameters
* aParams
,
2442 gfx::ShapedTextFlags aFlags
, nsTextFrameUtils::Flags aFlags2
,
2443 gfxMissingFontRecorder
* aMFR
) {
2445 return MakeEmptyTextRun(aParams
, aFlags
, aFlags2
);
2447 if (aLength
== 1 && aString
[0] == ' ') {
2448 return MakeSpaceTextRun(aParams
, aFlags
, aFlags2
);
2451 aFlags
|= ShapedTextFlags::TEXT_IS_8BIT
;
2453 if (MOZ_UNLIKELY(GetStyle()->AdjustedSizeMustBeZero())) {
2454 // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
2455 // them, and always create at least size 1 fonts, i.e. they still
2456 // render something for size 0 fonts.
2457 return MakeBlankTextRun(aString
, aLength
, aParams
, aFlags
, aFlags2
);
2460 RefPtr
<gfxTextRun
> textRun
=
2461 gfxTextRun::Create(aParams
, aLength
, this, aFlags
, aFlags2
);
2466 InitTextRun(aParams
->mDrawTarget
, textRun
.get(), aString
, aLength
, aMFR
);
2468 textRun
->FetchGlyphExtents(aParams
->mDrawTarget
);
2470 return textRun
.forget();
2473 already_AddRefed
<gfxTextRun
> gfxFontGroup::MakeTextRun(
2474 const char16_t
* aString
, uint32_t aLength
, const Parameters
* aParams
,
2475 gfx::ShapedTextFlags aFlags
, nsTextFrameUtils::Flags aFlags2
,
2476 gfxMissingFontRecorder
* aMFR
) {
2478 return MakeEmptyTextRun(aParams
, aFlags
, aFlags2
);
2480 if (aLength
== 1 && aString
[0] == ' ') {
2481 return MakeSpaceTextRun(aParams
, aFlags
, aFlags2
);
2483 if (MOZ_UNLIKELY(GetStyle()->AdjustedSizeMustBeZero())) {
2484 return MakeBlankTextRun(aString
, aLength
, aParams
, aFlags
, aFlags2
);
2487 RefPtr
<gfxTextRun
> textRun
=
2488 gfxTextRun::Create(aParams
, aLength
, this, aFlags
, aFlags2
);
2493 InitTextRun(aParams
->mDrawTarget
, textRun
.get(), aString
, aLength
, aMFR
);
2495 textRun
->FetchGlyphExtents(aParams
->mDrawTarget
);
2497 return textRun
.forget();
2500 template <typename T
>
2501 void gfxFontGroup::InitTextRun(DrawTarget
* aDrawTarget
, gfxTextRun
* aTextRun
,
2502 const T
* aString
, uint32_t aLength
,
2503 gfxMissingFontRecorder
* aMFR
) {
2504 NS_ASSERTION(aLength
> 0, "don't call InitTextRun for a zero-length run");
2506 // we need to do numeral processing even on 8-bit text,
2507 // in case we're converting Western to Hindi/Arabic digits
2508 int32_t numOption
= gfxPlatform::GetPlatform()->GetBidiNumeralOption();
2509 UniquePtr
<char16_t
[]> transformedString
;
2510 if (numOption
!= IBMBIDI_NUMERAL_NOMINAL
) {
2511 // scan the string for numerals that may need to be transformed;
2512 // if we find any, we'll make a local copy here and use that for
2513 // font matching and glyph generation/shaping
2515 !!(aTextRun
->GetFlags() & ShapedTextFlags::TEXT_INCOMING_ARABICCHAR
);
2516 for (uint32_t i
= 0; i
< aLength
; ++i
) {
2517 char16_t origCh
= aString
[i
];
2518 char16_t newCh
= HandleNumberInChar(origCh
, prevIsArabic
, numOption
);
2519 if (newCh
!= origCh
) {
2520 if (!transformedString
) {
2521 transformedString
= MakeUnique
<char16_t
[]>(aLength
);
2522 if constexpr (sizeof(T
) == sizeof(char16_t
)) {
2523 memcpy(transformedString
.get(), aString
, i
* sizeof(char16_t
));
2525 for (uint32_t j
= 0; j
< i
; ++j
) {
2526 transformedString
[j
] = aString
[j
];
2531 if (transformedString
) {
2532 transformedString
[i
] = newCh
;
2534 prevIsArabic
= IS_ARABIC_CHAR(newCh
);
2538 LogModule
* log
= mStyle
.systemFont
? gfxPlatform::GetLog(eGfxLog_textrunui
)
2539 : gfxPlatform::GetLog(eGfxLog_textrun
);
2541 // variant fallback handling may end up passing through this twice
2546 if (sizeof(T
) == sizeof(uint8_t) && !transformedString
) {
2547 if (MOZ_UNLIKELY(MOZ_LOG_TEST(log
, LogLevel::Warning
))) {
2549 mLanguage
->ToUTF8String(lang
);
2550 nsAutoCString
str((const char*)aString
, aLength
);
2551 nsAutoString styleString
;
2552 nsStyleUtil::AppendFontSlantStyle(mStyle
.style
, styleString
);
2553 auto defaultLanguageGeneric
= GetDefaultGeneric(mLanguage
);
2555 log
, LogLevel::Warning
,
2556 ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
2557 "len %d weight: %g stretch: %g%% style: %s size: %6.2f %zu-byte "
2558 "TEXTRUN [%s] ENDTEXTRUN\n",
2559 (mStyle
.systemFont
? "textrunui" : "textrun"),
2560 FamilyListToString(mFamilyList
).get(),
2561 (defaultLanguageGeneric
== StyleGenericFontFamily::Serif
2563 : (defaultLanguageGeneric
== StyleGenericFontFamily::SansSerif
2566 lang
.get(), static_cast<int>(Script::LATIN
), aLength
,
2567 mStyle
.weight
.ToFloat(), mStyle
.stretch
.Percentage(),
2568 NS_ConvertUTF16toUTF8(styleString
).get(), mStyle
.size
, sizeof(T
),
2572 // the text is still purely 8-bit; bypass the script-run itemizer
2573 // and treat it as a single Latin run
2574 InitScriptRun(aDrawTarget
, aTextRun
, aString
, 0, aLength
, Script::LATIN
,
2577 const char16_t
* textPtr
;
2578 if (transformedString
) {
2579 textPtr
= transformedString
.get();
2581 // typecast to avoid compilation error for the 8-bit version,
2582 // even though this is dead code in that case
2583 textPtr
= reinterpret_cast<const char16_t
*>(aString
);
2586 // split into script runs so that script can potentially influence
2587 // the font matching process below
2588 gfxScriptItemizer
scriptRuns(textPtr
, aLength
);
2590 uint32_t runStart
= 0, runLimit
= aLength
;
2591 Script runScript
= Script::LATIN
;
2592 while (scriptRuns
.Next(runStart
, runLimit
, runScript
)) {
2593 if (MOZ_UNLIKELY(MOZ_LOG_TEST(log
, LogLevel::Warning
))) {
2595 mLanguage
->ToUTF8String(lang
);
2596 nsAutoString styleString
;
2597 nsStyleUtil::AppendFontSlantStyle(mStyle
.style
, styleString
);
2598 auto defaultLanguageGeneric
= GetDefaultGeneric(mLanguage
);
2599 uint32_t runLen
= runLimit
- runStart
;
2601 log
, LogLevel::Warning
,
2602 ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
2603 "len %d weight: %g stretch: %g%% style: %s size: %6.2f "
2604 "%zu-byte TEXTRUN [%s] ENDTEXTRUN\n",
2605 (mStyle
.systemFont
? "textrunui" : "textrun"),
2606 FamilyListToString(mFamilyList
).get(),
2607 (defaultLanguageGeneric
== StyleGenericFontFamily::Serif
2609 : (defaultLanguageGeneric
==
2610 StyleGenericFontFamily::SansSerif
2613 lang
.get(), static_cast<int>(runScript
), runLen
,
2614 mStyle
.weight
.ToFloat(), mStyle
.stretch
.Percentage(),
2615 NS_ConvertUTF16toUTF8(styleString
).get(), mStyle
.size
, sizeof(T
),
2616 NS_ConvertUTF16toUTF8(textPtr
+ runStart
, runLen
).get()));
2619 InitScriptRun(aDrawTarget
, aTextRun
, textPtr
+ runStart
, runStart
,
2620 runLimit
- runStart
, runScript
, aMFR
);
2624 // if shaping was aborted due to lack of feature support, clear out
2625 // glyph runs and redo shaping with fallback forced on
2626 if (aTextRun
->GetShapingState() == gfxTextRun::eShapingState_Aborted
) {
2628 aTextRun
->SetShapingState(gfxTextRun::eShapingState_ForceFallbackFeature
);
2629 aTextRun
->ClearGlyphsAndCharacters();
2634 if (sizeof(T
) == sizeof(char16_t
) && aLength
> 0) {
2635 gfxTextRun::CompressedGlyph
* glyph
= aTextRun
->GetCharacterGlyphs();
2636 if (!glyph
->IsSimpleGlyph()) {
2637 glyph
->SetClusterStart(true);
2641 // It's possible for CoreText to omit glyph runs if it decides they contain
2642 // only invisibles (e.g., U+FEFF, see reftest 474417-1). In this case, we
2643 // need to eliminate them from the glyph run array to avoid drawing "partial
2644 // ligatures" with the wrong font.
2645 // We don't do this during InitScriptRun (or gfxFont::InitTextRun) because
2646 // it will iterate back over all glyphruns in the textrun, which leads to
2647 // pathologically-bad perf in the case where a textrun contains many script
2648 // changes (see bug 680402) - we'd end up re-sanitizing all the earlier runs
2649 // every time a new script subrun is processed.
2650 aTextRun
->SanitizeGlyphRuns();
2652 aTextRun
->SortGlyphRuns();
2655 static inline bool IsPUA(uint32_t aUSV
) {
2656 // We could look up the General Category of the codepoint here,
2657 // but it's simpler to check PUA codepoint ranges.
2658 return (aUSV
>= 0xE000 && aUSV
<= 0xF8FF) || (aUSV
>= 0xF0000);
2661 template <typename T
>
2662 void gfxFontGroup::InitScriptRun(DrawTarget
* aDrawTarget
, gfxTextRun
* aTextRun
,
2663 const T
* aString
, // text for this script run,
2664 // not the entire textrun
2665 uint32_t aOffset
, // position of the script
2666 // run within the textrun
2667 uint32_t aLength
, // length of the script run
2669 gfxMissingFontRecorder
* aMFR
) {
2670 NS_ASSERTION(aLength
> 0, "don't call InitScriptRun for a 0-length run");
2671 NS_ASSERTION(aTextRun
->GetShapingState() != gfxTextRun::eShapingState_Aborted
,
2672 "don't call InitScriptRun with aborted shaping state");
2674 // confirm the load state of userfonts in the list
2675 if (mUserFontSet
&& mCurrGeneration
!= mUserFontSet
->GetGeneration()) {
2679 gfxFont
* mainFont
= GetFirstValidFont();
2681 ShapedTextFlags orientation
=
2682 aTextRun
->GetFlags() & ShapedTextFlags::TEXT_ORIENT_MASK
;
2684 if (orientation
!= ShapedTextFlags::TEXT_ORIENT_HORIZONTAL
&&
2685 (aRunScript
== Script::MONGOLIAN
|| aRunScript
== Script::PHAGS_PA
)) {
2686 // Mongolian and Phags-pa text should ignore text-orientation and
2687 // always render in its "native" vertical mode, implemented by fonts
2688 // as sideways-right (i.e as if shaped horizontally, and then the
2689 // entire line is rotated to render vertically). Therefore, we ignore
2690 // the aOrientation value from the textrun's flags, and make all
2691 // vertical Mongolian/Phags-pa use sideways-right.
2692 orientation
= ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT
;
2695 uint32_t runStart
= 0;
2696 AutoTArray
<TextRange
, 3> fontRanges
;
2697 ComputeRanges(fontRanges
, aString
, aLength
, aRunScript
, orientation
);
2698 uint32_t numRanges
= fontRanges
.Length();
2699 bool missingChars
= false;
2700 bool isCJK
= gfxTextRun::IsCJKScript(aRunScript
);
2702 for (uint32_t r
= 0; r
< numRanges
; r
++) {
2703 const TextRange
& range
= fontRanges
[r
];
2704 uint32_t matchedLength
= range
.Length();
2705 gfxFont
* matchedFont
= range
.font
;
2706 // create the glyph run for this range
2707 if (matchedFont
&& mStyle
.noFallbackVariantFeatures
) {
2708 // common case - just do glyph layout and record the
2709 // resulting positioned glyphs
2710 aTextRun
->AddGlyphRun(matchedFont
, range
.matchType
, aOffset
+ runStart
,
2711 (matchedLength
> 0), range
.orientation
, isCJK
);
2712 if (!matchedFont
->SplitAndInitTextRun(
2713 aDrawTarget
, aTextRun
, aString
+ runStart
, aOffset
+ runStart
,
2714 matchedLength
, aRunScript
, mLanguage
, range
.orientation
)) {
2715 // glyph layout failed! treat as missing glyphs
2716 matchedFont
= nullptr;
2718 } else if (matchedFont
) {
2719 // shape with some variant feature that requires fallback handling
2720 bool petiteToSmallCaps
= false;
2721 bool syntheticLower
= false;
2722 bool syntheticUpper
= false;
2724 if (mStyle
.variantSubSuper
!= NS_FONT_VARIANT_POSITION_NORMAL
&&
2725 (aTextRun
->GetShapingState() ==
2726 gfxTextRun::eShapingState_ForceFallbackFeature
||
2727 !matchedFont
->SupportsSubSuperscript(mStyle
.variantSubSuper
, aString
,
2728 aLength
, aRunScript
))) {
2729 // fallback for subscript/superscript variant glyphs
2731 // if the feature was already used, abort and force
2732 // fallback across the entire textrun
2733 gfxTextRun::ShapingState ss
= aTextRun
->GetShapingState();
2735 if (ss
== gfxTextRun::eShapingState_Normal
) {
2736 aTextRun
->SetShapingState(
2737 gfxTextRun::eShapingState_ShapingWithFallback
);
2738 } else if (ss
== gfxTextRun::eShapingState_ShapingWithFeature
) {
2739 aTextRun
->SetShapingState(gfxTextRun::eShapingState_Aborted
);
2743 RefPtr
<gfxFont
> subSuperFont
= matchedFont
->GetSubSuperscriptFont(
2744 aTextRun
->GetAppUnitsPerDevUnit());
2745 aTextRun
->AddGlyphRun(subSuperFont
, range
.matchType
, aOffset
+ runStart
,
2746 (matchedLength
> 0), range
.orientation
, isCJK
);
2747 if (!subSuperFont
->SplitAndInitTextRun(
2748 aDrawTarget
, aTextRun
, aString
+ runStart
, aOffset
+ runStart
,
2749 matchedLength
, aRunScript
, mLanguage
, range
.orientation
)) {
2750 // glyph layout failed! treat as missing glyphs
2751 matchedFont
= nullptr;
2753 } else if (mStyle
.variantCaps
!= NS_FONT_VARIANT_CAPS_NORMAL
&&
2754 mStyle
.allowSyntheticSmallCaps
&&
2755 !matchedFont
->SupportsVariantCaps(
2756 aRunScript
, mStyle
.variantCaps
, petiteToSmallCaps
,
2757 syntheticLower
, syntheticUpper
)) {
2758 // fallback for small-caps variant glyphs
2759 if (!matchedFont
->InitFakeSmallCapsRun(
2760 mPresContext
, aDrawTarget
, aTextRun
, aString
+ runStart
,
2761 aOffset
+ runStart
, matchedLength
, range
.matchType
,
2762 range
.orientation
, aRunScript
,
2763 mExplicitLanguage
? mLanguage
.get() : nullptr, syntheticLower
,
2765 matchedFont
= nullptr;
2768 // shape normally with variant feature enabled
2769 gfxTextRun::ShapingState ss
= aTextRun
->GetShapingState();
2771 // adjust the shaping state if necessary
2772 if (ss
== gfxTextRun::eShapingState_Normal
) {
2773 aTextRun
->SetShapingState(
2774 gfxTextRun::eShapingState_ShapingWithFeature
);
2775 } else if (ss
== gfxTextRun::eShapingState_ShapingWithFallback
) {
2776 // already have shaping results using fallback, need to redo
2777 aTextRun
->SetShapingState(gfxTextRun::eShapingState_Aborted
);
2781 // do glyph layout and record the resulting positioned glyphs
2782 aTextRun
->AddGlyphRun(matchedFont
, range
.matchType
, aOffset
+ runStart
,
2783 (matchedLength
> 0), range
.orientation
, isCJK
);
2784 if (!matchedFont
->SplitAndInitTextRun(
2785 aDrawTarget
, aTextRun
, aString
+ runStart
, aOffset
+ runStart
,
2786 matchedLength
, aRunScript
, mLanguage
, range
.orientation
)) {
2787 // glyph layout failed! treat as missing glyphs
2788 matchedFont
= nullptr;
2792 aTextRun
->AddGlyphRun(mainFont
, FontMatchType::Kind::kFontGroup
,
2793 aOffset
+ runStart
, (matchedLength
> 0),
2794 range
.orientation
, isCJK
);
2798 // We need to set cluster boundaries (and mark spaces) so that
2799 // surrogate pairs, combining characters, etc behave properly,
2800 // even if we don't have glyphs for them
2801 aTextRun
->SetupClusterBoundaries(aOffset
+ runStart
, aString
+ runStart
,
2804 // various "missing" characters may need special handling,
2805 // so we check for them here
2806 uint32_t runLimit
= runStart
+ matchedLength
;
2807 for (uint32_t index
= runStart
; index
< runLimit
; index
++) {
2808 T ch
= aString
[index
];
2810 // tab and newline are not to be displayed as hexboxes,
2811 // but do need to be recorded in the textrun
2813 aTextRun
->SetIsNewline(aOffset
+ index
);
2817 aTextRun
->SetIsTab(aOffset
+ index
);
2821 // for 16-bit textruns only, check for surrogate pairs and
2822 // special Unicode spaces; omit these checks in 8-bit runs
2823 if constexpr (sizeof(T
) == sizeof(char16_t
)) {
2824 if (index
+ 1 < aLength
&&
2825 NS_IS_SURROGATE_PAIR(ch
, aString
[index
+ 1])) {
2826 uint32_t usv
= SURROGATE_TO_UCS4(ch
, aString
[index
+ 1]);
2827 aTextRun
->SetMissingGlyph(aOffset
+ index
, usv
, mainFont
);
2829 if (!mSkipDrawing
&& !IsPUA(usv
)) {
2830 missingChars
= true;
2835 // check if this is a known Unicode whitespace character that
2836 // we can render using the space glyph with a custom width
2837 gfxFloat wid
= mainFont
->SynthesizeSpaceWidth(ch
);
2840 aTextRun
->GetAppUnitsPerDevUnit() * floor(wid
+ 0.5);
2841 if (gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance
)) {
2842 aTextRun
->GetCharacterGlyphs()[aOffset
+ index
].SetSimpleGlyph(
2843 advance
, mainFont
->GetSpaceGlyph());
2845 gfxTextRun::DetailedGlyph detailedGlyph
;
2846 detailedGlyph
.mGlyphID
= mainFont
->GetSpaceGlyph();
2847 detailedGlyph
.mAdvance
= advance
;
2848 aTextRun
->SetDetailedGlyphs(aOffset
+ index
, 1, &detailedGlyph
);
2854 if (IsInvalidChar(ch
)) {
2855 // invalid chars are left as zero-width/invisible
2859 // record char code so we can draw a box with the Unicode value
2860 aTextRun
->SetMissingGlyph(aOffset
+ index
, ch
, mainFont
);
2861 if (!mSkipDrawing
&& !IsPUA(ch
)) {
2862 missingChars
= true;
2867 runStart
+= matchedLength
;
2870 if (aMFR
&& missingChars
) {
2871 aMFR
->RecordScript(aRunScript
);
2875 gfxTextRun
* gfxFontGroup::GetEllipsisTextRun(
2876 int32_t aAppUnitsPerDevPixel
, gfx::ShapedTextFlags aFlags
,
2877 LazyReferenceDrawTargetGetter
& aRefDrawTargetGetter
) {
2878 MOZ_ASSERT(!(aFlags
& ~ShapedTextFlags::TEXT_ORIENT_MASK
),
2879 "flags here should only be used to specify orientation");
2880 if (mCachedEllipsisTextRun
&&
2881 (mCachedEllipsisTextRun
->GetFlags() &
2882 ShapedTextFlags::TEXT_ORIENT_MASK
) == aFlags
&&
2883 mCachedEllipsisTextRun
->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel
) {
2884 return mCachedEllipsisTextRun
.get();
2887 // Use a Unicode ellipsis if the font supports it,
2888 // otherwise use three ASCII periods as fallback.
2889 gfxFont
* firstFont
= GetFirstValidFont(uint32_t(kEllipsisChar
[0]));
2891 firstFont
->HasCharacter(kEllipsisChar
[0])
2892 ? nsDependentString(kEllipsisChar
, ArrayLength(kEllipsisChar
) - 1)
2893 : nsDependentString(kASCIIPeriodsChar
,
2894 ArrayLength(kASCIIPeriodsChar
) - 1);
2896 RefPtr
<DrawTarget
> refDT
= aRefDrawTargetGetter
.GetRefDrawTarget();
2897 Parameters params
= {refDT
, nullptr, nullptr,
2898 nullptr, 0, aAppUnitsPerDevPixel
};
2899 mCachedEllipsisTextRun
=
2900 MakeTextRun(ellipsis
.get(), ellipsis
.Length(), ¶ms
, aFlags
,
2901 nsTextFrameUtils::Flags(), nullptr);
2902 if (!mCachedEllipsisTextRun
) {
2905 // don't let the presence of a cached ellipsis textrun prolong the
2907 mCachedEllipsisTextRun
->ReleaseFontGroup();
2908 return mCachedEllipsisTextRun
.get();
2911 gfxFont
* gfxFontGroup::FindFallbackFaceForChar(
2912 gfxFontFamily
* aFamily
, uint32_t aCh
, uint32_t aNextCh
,
2913 eFontPresentation aPresentation
) {
2914 GlobalFontMatch
data(aCh
, aNextCh
, mStyle
, aPresentation
);
2915 aFamily
->SearchAllFontsForChar(&data
);
2916 gfxFontEntry
* fe
= data
.mBestMatch
;
2920 return fe
->FindOrMakeFont(&mStyle
);
2923 gfxFont
* gfxFontGroup::FindFallbackFaceForChar(
2924 fontlist::Family
* aFamily
, uint32_t aCh
, uint32_t aNextCh
,
2925 eFontPresentation aPresentation
) {
2926 auto* pfl
= gfxPlatformFontList::PlatformFontList();
2927 auto* list
= pfl
->SharedFontList();
2929 // If async fallback is enabled, and the family isn't fully initialized yet,
2930 // just start the async cmap loading and return.
2931 if (!aFamily
->IsFullyInitialized() &&
2932 StaticPrefs::gfx_font_rendering_fallback_async() &&
2933 !XRE_IsParentProcess()) {
2934 pfl
->StartCmapLoadingFromFamily(aFamily
- list
->Families());
2938 GlobalFontMatch
data(aCh
, aNextCh
, mStyle
, aPresentation
);
2939 aFamily
->SearchAllFontsForChar(list
, &data
);
2940 gfxFontEntry
* fe
= data
.mBestMatch
;
2944 return fe
->FindOrMakeFont(&mStyle
);
2947 gfxFont
* gfxFontGroup::FindFallbackFaceForChar(
2948 const FamilyFace
& aFamily
, uint32_t aCh
, uint32_t aNextCh
,
2949 eFontPresentation aPresentation
) {
2950 if (aFamily
.IsSharedFamily()) {
2951 return FindFallbackFaceForChar(aFamily
.SharedFamily(), aCh
, aNextCh
,
2954 return FindFallbackFaceForChar(aFamily
.OwnedFamily(), aCh
, aNextCh
,
2958 gfxFloat
gfxFontGroup::GetUnderlineOffset() {
2959 if (mUnderlineOffset
== UNDERLINE_OFFSET_NOT_SET
) {
2960 // if the fontlist contains a bad underline font, make the underline
2961 // offset the min of the first valid font and bad font underline offsets
2962 uint32_t len
= mFonts
.Length();
2963 for (uint32_t i
= 0; i
< len
; i
++) {
2964 FamilyFace
& ff
= mFonts
[i
];
2965 gfxFontEntry
* fe
= ff
.FontEntry();
2969 if (!fe
->mIsUserFontContainer
&& !fe
->IsUserFont() &&
2970 ((ff
.IsSharedFamily() && ff
.SharedFamily() &&
2971 ff
.SharedFamily()->IsBadUnderlineFamily()) ||
2972 (!ff
.IsSharedFamily() && ff
.OwnedFamily() &&
2973 ff
.OwnedFamily()->IsBadUnderlineFamily()))) {
2974 gfxFont
* font
= GetFontAt(i
);
2979 font
->GetMetrics(nsFontMetrics::eHorizontal
).underlineOffset
;
2980 gfxFloat first
= GetFirstValidFont()
2981 ->GetMetrics(nsFontMetrics::eHorizontal
)
2983 mUnderlineOffset
= std::min(first
, bad
);
2984 return mUnderlineOffset
;
2988 // no bad underline fonts, use the first valid font's metric
2989 mUnderlineOffset
= GetFirstValidFont()
2990 ->GetMetrics(nsFontMetrics::eHorizontal
)
2994 return mUnderlineOffset
;
2997 #define NARROW_NO_BREAK_SPACE 0x202fu
2999 gfxFont
* gfxFontGroup::FindFontForChar(uint32_t aCh
, uint32_t aPrevCh
,
3000 uint32_t aNextCh
, Script aRunScript
,
3001 gfxFont
* aPrevMatchedFont
,
3002 FontMatchType
* aMatchType
) {
3003 // If the char is a cluster extender, we want to use the same font as the
3004 // preceding character if possible. This is preferable to using the font
3005 // group because it avoids breaks in shaping within a cluster.
3006 if (aPrevMatchedFont
&& IsClusterExtender(aCh
)) {
3007 if (aPrevMatchedFont
->HasCharacter(aCh
) || IsDefaultIgnorable(aCh
)) {
3008 return aPrevMatchedFont
;
3010 // Get the singleton NFC normalizer; this does not need to be deleted.
3011 static UErrorCode err
= U_ZERO_ERROR
;
3012 static const UNormalizer2
* nfc
= unorm2_getNFCInstance(&err
);
3013 // Check if this char and preceding char can compose; if so, is the
3014 // combination supported by the current font.
3015 int32_t composed
= unorm2_composePair(nfc
, aPrevCh
, aCh
);
3016 if (composed
> 0 && aPrevMatchedFont
->HasCharacter(composed
)) {
3017 return aPrevMatchedFont
;
3021 // Special cases for NNBSP (as used in Mongolian):
3022 if (aCh
== NARROW_NO_BREAK_SPACE
) {
3023 // If there is no preceding character, try the font that we'd use
3024 // for the next char (unless it's just another NNBSP; we don't try
3025 // to look ahead through a whole run of them).
3026 if (!aPrevCh
&& aNextCh
&& aNextCh
!= NARROW_NO_BREAK_SPACE
) {
3027 gfxFont
* nextFont
= FindFontForChar(aNextCh
, 0, 0, aRunScript
,
3028 aPrevMatchedFont
, aMatchType
);
3029 if (nextFont
&& nextFont
->HasCharacter(aCh
)) {
3033 // Otherwise, treat NNBSP like a cluster extender (as above) and try
3034 // to continue the preceding font run.
3035 if (aPrevMatchedFont
&& aPrevMatchedFont
->HasCharacter(aCh
)) {
3036 return aPrevMatchedFont
;
3040 // To optimize common cases, try the first font in the font-group
3041 // before going into the more detailed checks below
3042 uint32_t fontListLength
= mFonts
.Length();
3043 uint32_t nextIndex
= 0;
3044 bool isJoinControl
= gfxFontUtils::IsJoinControl(aCh
);
3045 bool wasJoinCauser
= gfxFontUtils::IsJoinCauser(aPrevCh
);
3046 bool isVarSelector
= gfxFontUtils::IsVarSelector(aCh
);
3047 bool nextIsVarSelector
= gfxFontUtils::IsVarSelector(aNextCh
);
3049 // For Unicode hyphens, if not supported in the font then we'll try for
3050 // the ASCII hyphen-minus as a fallback.
3051 // Similarly, for NBSP we try normal <space> as a fallback.
3052 uint32_t fallbackChar
= (aCh
== 0x2010 || aCh
== 0x2011) ? '-'
3053 : (aCh
== 0x00A0) ? ' '
3056 // Whether we've seen a font that is currently loading a resource that may
3057 // provide this character (so we should not start a new load).
3058 bool loading
= false;
3060 // Do we need to explicitly look for a font that does or does not provide a
3061 // color glyph for the given character?
3062 // For characters with no `EMOJI` property, we'll use whatever the family
3063 // list calls for; but if it's a potential emoji codepoint, we need to check
3064 // if there's a variation selector specifically asking for Text-style or
3065 // Emoji-style rendering and look for a suitable font.
3066 eFontPresentation presentation
= eFontPresentation::Any
;
3067 EmojiPresentation emojiPresentation
= GetEmojiPresentation(aCh
);
3068 if (emojiPresentation
!= TextOnly
) {
3069 // If the prefer-emoji selector is present, or if it's a default-emoji char
3070 // and the prefer-text selector is NOT present, or if there's a skin-tone
3071 // modifier, we specifically look for a font with a color glyph.
3072 // If the prefer-text selector is present, we specifically look for a font
3073 // that will provide a monochrome glyph.
3074 // Otherwise, we'll accept either color or monochrome font-family entries,
3075 // so that a color font can be explicitly applied via font-family even to
3076 // characters that are not inherently emoji-style.
3077 if (aNextCh
== kVariationSelector16
||
3078 (aNextCh
>= kEmojiSkinToneFirst
&& aNextCh
<= kEmojiSkinToneLast
)) {
3079 // Emoji presentation is explicitly requested by a variation selector or
3080 // the presence of a skin-tone codepoint.
3081 presentation
= eFontPresentation::EmojiExplicit
;
3082 } else if (emojiPresentation
== EmojiPresentation::EmojiDefault
&&
3083 aNextCh
!= kVariationSelector15
) {
3084 // Emoji presentation is the default for this Unicode character. but we
3085 // will allow an explicitly-specified webfont to apply to it, regardless
3086 // of its glyph type.
3087 presentation
= eFontPresentation::EmojiDefault
;
3088 } else if (aNextCh
== kVariationSelector15
) {
3089 // Text presentation is explicitly requested.
3090 presentation
= eFontPresentation::Text
;
3094 if (!isJoinControl
&& !wasJoinCauser
&& !isVarSelector
&&
3095 !nextIsVarSelector
&& presentation
== eFontPresentation::Any
) {
3096 gfxFont
* firstFont
= GetFontAt(0, aCh
, &loading
);
3098 if (firstFont
->HasCharacter(aCh
) ||
3099 (fallbackChar
&& firstFont
->HasCharacter(fallbackChar
))) {
3100 *aMatchType
= {FontMatchType::Kind::kFontGroup
, mFonts
[0].Generic()};
3104 gfxFont
* font
= nullptr;
3105 if (mFonts
[0].CheckForFallbackFaces()) {
3106 font
= FindFallbackFaceForChar(mFonts
[0], aCh
, aNextCh
, presentation
);
3107 } else if (!firstFont
->GetFontEntry()->IsUserFont()) {
3108 // For platform fonts (but not userfonts), we may need to do
3109 // fallback within the family to handle cases where some faces
3110 // such as Italic or Black have reduced character sets compared
3111 // to the family's Regular face.
3112 font
= FindFallbackFaceForChar(mFonts
[0], aCh
, aNextCh
, presentation
);
3115 *aMatchType
= {FontMatchType::Kind::kFontGroup
, mFonts
[0].Generic()};
3119 if (fontListLength
> 0) {
3120 loading
= loading
|| mFonts
[0].IsLoadingFor(aCh
);
3124 // we don't need to check the first font again below
3128 if (aPrevMatchedFont
) {
3129 // Don't switch fonts for control characters, regardless of
3130 // whether they are present in the current font, as they won't
3131 // actually be rendered (see bug 716229)
3132 if (isJoinControl
||
3133 GetGeneralCategory(aCh
) == HB_UNICODE_GENERAL_CATEGORY_CONTROL
) {
3134 return aPrevMatchedFont
;
3137 // if previous character was a join-causer (ZWJ),
3138 // use the same font as the previous range if we can
3139 if (wasJoinCauser
) {
3140 if (aPrevMatchedFont
->HasCharacter(aCh
)) {
3141 return aPrevMatchedFont
;
3146 // If this character is a variation selector or default-ignorable, use the
3147 // previous font regardless of whether it supports the codepoint or not.
3148 // (We don't want to unnecessarily split glyph runs, and the character will
3149 // not be visibly rendered.)
3150 if (isVarSelector
|| IsDefaultIgnorable(aCh
)) {
3151 return aPrevMatchedFont
;
3154 // Used to remember the first "candidate" font that would provide a fallback
3155 // text-style rendering if no color glyph can be found.
3156 // If we decide NOT to return this font, we must AddRef/Release it to ensure
3157 // that it goes into the global font cache as a candidate for deletion.
3158 // This is done for us by CheckCandidate, but any code path that returns
3159 // WITHOUT calling CheckCandidate needs to handle it explicitly.
3160 gfxFont
* candidateFont
= nullptr;
3161 FontMatchType candidateMatchType
;
3163 // Handle a candidate font that could support the character, returning true
3164 // if we should go ahead and return |f|, false to continue searching.
3165 // If there is already a saved candidate font, and the new candidate is
3166 // accepted, we AddRef/Release the existing candidate so it won't leak.
3167 auto CheckCandidate
= [&](gfxFont
* f
, FontMatchType t
) -> bool {
3168 // If no preference, then just accept the font.
3169 if (presentation
== eFontPresentation::Any
||
3170 (presentation
== eFontPresentation::EmojiDefault
&&
3171 f
->GetFontEntry()->IsUserFont())) {
3172 RefPtr
<gfxFont
> autoRefDeref(candidateFont
);
3176 // Does the candidate font provide a color glyph for the current character?
3177 bool hasColorGlyph
= f
->HasColorGlyphFor(aCh
, aNextCh
);
3178 // If the provided glyph matches the preference, accept the font.
3179 if (hasColorGlyph
== PrefersColor(presentation
)) {
3180 RefPtr
<gfxFont
> autoRefDeref(candidateFont
);
3184 // Otherwise, remember the first potential fallback, but keep searching.
3185 if (!candidateFont
) {
3187 candidateMatchType
= t
;
3192 // 1. check remaining fonts in the font group
3193 for (uint32_t i
= nextIndex
; i
< fontListLength
; i
++) {
3194 FamilyFace
& ff
= mFonts
[i
];
3195 if (ff
.IsInvalid() || ff
.IsLoading()) {
3196 if (ff
.IsLoadingFor(aCh
)) {
3202 gfxFont
* font
= ff
.Font();
3204 // if available, use already-made gfxFont and check for character
3205 if (font
->HasCharacter(aCh
) ||
3206 (fallbackChar
&& font
->HasCharacter(fallbackChar
))) {
3207 if (CheckCandidate(font
,
3208 {FontMatchType::Kind::kFontGroup
, ff
.Generic()})) {
3213 // don't have a gfxFont yet, test charmap before instantiating
3214 gfxFontEntry
* fe
= ff
.FontEntry();
3215 if (fe
&& fe
->mIsUserFontContainer
) {
3216 // for userfonts, need to test both the unicode range map and
3217 // the cmap of the platform font entry
3218 gfxUserFontEntry
* ufe
= static_cast<gfxUserFontEntry
*>(fe
);
3220 // never match a character outside the defined unicode range
3221 if (!ufe
->CharacterInUnicodeRange(aCh
)) {
3225 // Load if not already loaded, unless we've already seen an in-
3226 // progress load that is expected to satisfy this request.
3228 ufe
->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED
) {
3230 ff
.CheckState(mSkipDrawing
);
3233 if (ff
.IsLoading()) {
3237 gfxFontEntry
* pfe
= ufe
->GetPlatformFontEntry();
3238 if (pfe
&& (pfe
->HasCharacter(aCh
) ||
3239 (fallbackChar
&& pfe
->HasCharacter(fallbackChar
)))) {
3240 font
= GetFontAt(i
, aCh
, &loading
);
3242 if (CheckCandidate(font
, {FontMatchType::Kind::kFontGroup
,
3243 mFonts
[i
].Generic()})) {
3248 } else if (fe
&& (fe
->HasCharacter(aCh
) ||
3249 (fallbackChar
&& fe
->HasCharacter(fallbackChar
)))) {
3250 // for normal platform fonts, after checking the cmap
3251 // build the font via GetFontAt
3252 font
= GetFontAt(i
, aCh
, &loading
);
3254 if (CheckCandidate(font
, {FontMatchType::Kind::kFontGroup
,
3255 mFonts
[i
].Generic()})) {
3262 // check other family faces if needed
3263 if (ff
.CheckForFallbackFaces()) {
3266 fontlist::FontList
* list
=
3267 gfxPlatformFontList::PlatformFontList()->SharedFontList();
3268 nsCString s1
= mFonts
[i
- 1].IsSharedFamily()
3269 ? mFonts
[i
- 1].SharedFamily()->Key().AsString(list
)
3270 : mFonts
[i
- 1].OwnedFamily()->Name();
3271 nsCString s2
= ff
.IsSharedFamily()
3272 ? ff
.SharedFamily()->Key().AsString(list
)
3273 : ff
.OwnedFamily()->Name();
3274 MOZ_ASSERT(!mFonts
[i
- 1].CheckForFallbackFaces() || !s1
.Equals(s2
),
3275 "should only do fallback once per font family");
3278 font
= FindFallbackFaceForChar(ff
, aCh
, aNextCh
, presentation
);
3280 if (CheckCandidate(font
,
3281 {FontMatchType::Kind::kFontGroup
, ff
.Generic()})) {
3286 // For platform fonts, but not user fonts, consider intra-family
3287 // fallback to handle styles with reduced character sets (see
3289 gfxFontEntry
* fe
= ff
.FontEntry();
3290 if (fe
&& !fe
->mIsUserFontContainer
&& !fe
->IsUserFont()) {
3291 font
= FindFallbackFaceForChar(ff
, aCh
, aNextCh
, presentation
);
3293 if (CheckCandidate(font
,
3294 {FontMatchType::Kind::kFontGroup
, ff
.Generic()})) {
3302 if (fontListLength
== 0) {
3303 gfxFont
* defaultFont
= GetDefaultFont();
3304 if (defaultFont
->HasCharacter(aCh
) ||
3305 (fallbackChar
&& defaultFont
->HasCharacter(fallbackChar
))) {
3306 if (CheckCandidate(defaultFont
, FontMatchType::Kind::kFontGroup
)) {
3312 // If character is in Private Use Area, or is unassigned in Unicode, don't do
3313 // matching against pref or system fonts. We only support such codepoints
3314 // when used with an explicitly-specified font, as they have no standard/
3315 // interoperable meaning.
3316 // Also don't attempt any fallback for control characters or noncharacters,
3317 // where we won't be rendering a glyph anyhow, or for codepoints where global
3318 // fallback has already noted a failure.
3319 FontVisibility level
=
3320 mPresContext
? mPresContext
->GetFontVisibility() : FontVisibility::User
;
3321 if (gfxPlatformFontList::PlatformFontList()->SkipFontFallbackForChar(level
,
3323 GetGeneralCategory(aCh
) == HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED
) {
3324 if (candidateFont
) {
3325 *aMatchType
= candidateMatchType
;
3327 return candidateFont
;
3330 // 2. search pref fonts
3331 gfxFont
* font
= WhichPrefFontSupportsChar(aCh
, aNextCh
, presentation
);
3333 if (PrefersColor(presentation
) &&
3334 gfxPlatformFontList::PlatformFontList()->EmojiPrefHasUserValue()) {
3335 // For emoji, always accept the font from preferences if it's explicitly
3336 // user-set, even if it isn't actually a color-emoji font, as some users
3337 // may want to set their emoji font preference to a monochrome font like
3339 // So a user-provided font.name-list.emoji preference takes precedence
3340 // over the Unicode presentation style here.
3341 RefPtr
<gfxFont
> autoRefDeref(candidateFont
);
3342 *aMatchType
= FontMatchType::Kind::kPrefsFallback
;
3345 if (CheckCandidate(font
, FontMatchType::Kind::kPrefsFallback
)) {
3348 // Don't leak `font` if we decided not to return it.
3349 RefPtr
<gfxFont
> autoRefDeref(font
);
3352 // For fallback searches, we don't want to use a color-emoji font unless
3353 // emoji-style presentation is specifically required, so we map Any to
3355 if (presentation
== eFontPresentation::Any
) {
3356 presentation
= eFontPresentation::Text
;
3359 // 3. use fallback fonts
3360 // -- before searching for something else check the font used for the
3361 // previous character
3362 if (aPrevMatchedFont
&&
3363 (aPrevMatchedFont
->HasCharacter(aCh
) ||
3364 (fallbackChar
&& aPrevMatchedFont
->HasCharacter(fallbackChar
)))) {
3365 if (CheckCandidate(aPrevMatchedFont
,
3366 FontMatchType::Kind::kSystemFallback
)) {
3367 return aPrevMatchedFont
;
3371 // for known "space" characters, don't do a full system-fallback search;
3372 // we'll synthesize appropriate-width spaces instead of missing-glyph boxes
3373 if (GetGeneralCategory(aCh
) == HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR
&&
3374 GetFirstValidFont()->SynthesizeSpaceWidth(aCh
) >= 0.0) {
3375 RefPtr
<gfxFont
> autoRefDeref(candidateFont
);
3379 // -- otherwise look for other stuff
3380 font
= WhichSystemFontSupportsChar(aCh
, aNextCh
, aRunScript
, presentation
);
3382 if (CheckCandidate(font
, FontMatchType::Kind::kSystemFallback
)) {
3385 RefPtr
<gfxFont
> autoRefDeref(font
);
3387 if (candidateFont
) {
3388 *aMatchType
= candidateMatchType
;
3390 return candidateFont
;
3393 template <typename T
>
3394 void gfxFontGroup::ComputeRanges(nsTArray
<TextRange
>& aRanges
, const T
* aString
,
3395 uint32_t aLength
, Script aRunScript
,
3396 gfx::ShapedTextFlags aOrientation
) {
3397 NS_ASSERTION(aRanges
.Length() == 0, "aRanges must be initially empty");
3398 NS_ASSERTION(aLength
> 0, "don't call ComputeRanges for zero-length text");
3400 uint32_t prevCh
= 0;
3401 uint32_t nextCh
= aString
[0];
3402 if constexpr (sizeof(T
) == sizeof(char16_t
)) {
3403 if (aLength
> 1 && NS_IS_SURROGATE_PAIR(nextCh
, aString
[1])) {
3404 nextCh
= SURROGATE_TO_UCS4(nextCh
, aString
[1]);
3407 int32_t lastRangeIndex
= -1;
3409 // initialize prevFont to the group's primary font, so that this will be
3410 // used for string-initial control chars, etc rather than risk hitting font
3411 // fallback for these (bug 716229)
3412 StyleGenericFontFamily generic
= StyleGenericFontFamily::None
;
3413 gfxFont
* prevFont
= GetFirstValidFont(' ', &generic
);
3415 // if we use the initial value of prevFont, we treat this as a match from
3416 // the font group; fixes bug 978313
3417 FontMatchType matchType
= {FontMatchType::Kind::kFontGroup
, generic
};
3419 for (uint32_t i
= 0; i
< aLength
; i
++) {
3420 const uint32_t origI
= i
; // save off in case we increase for surrogate
3422 // set up current ch
3423 uint32_t ch
= nextCh
;
3425 // Get next char (if any) so that FindFontForChar can look ahead
3426 // for a possible variation selector.
3428 if constexpr (sizeof(T
) == sizeof(char16_t
)) {
3429 // In 16-bit case only, check for surrogate pairs.
3433 if (i
< aLength
- 1) {
3434 nextCh
= aString
[i
+ 1];
3435 if (i
+ 2 < aLength
&& NS_IS_SURROGATE_PAIR(nextCh
, aString
[i
+ 2])) {
3436 nextCh
= SURROGATE_TO_UCS4(nextCh
, aString
[i
+ 2]);
3442 // 8-bit case is trivial.
3443 nextCh
= i
< aLength
- 1 ? aString
[i
+ 1] : 0;
3448 // Find the font for this char; but try to avoid calling the expensive
3449 // FindFontForChar method for the most common case, where the first
3450 // font in the list supports the current char, and it is not one of
3451 // the special cases where FindFontForChar will attempt to propagate
3452 // the font selected for an adjacent character.
3453 if ((font
= GetFontAt(0, ch
)) != nullptr && font
->HasCharacter(ch
) &&
3454 (sizeof(T
) == sizeof(uint8_t) ||
3455 (!IsClusterExtender(ch
) && ch
!= NARROW_NO_BREAK_SPACE
&&
3456 !gfxFontUtils::IsJoinControl(ch
) &&
3457 !gfxFontUtils::IsJoinCauser(prevCh
) &&
3458 !gfxFontUtils::IsVarSelector(ch
) &&
3459 GetEmojiPresentation(ch
) == TextOnly
))) {
3460 matchType
= {FontMatchType::Kind::kFontGroup
, mFonts
[0].Generic()};
3463 FindFontForChar(ch
, prevCh
, nextCh
, aRunScript
, prevFont
, &matchType
);
3466 #ifndef RELEASE_OR_BETA
3467 if (MOZ_UNLIKELY(mTextPerf
)) {
3468 if (matchType
.kind
== FontMatchType::Kind::kPrefsFallback
) {
3469 mTextPerf
->current
.fallbackPrefs
++;
3470 } else if (matchType
.kind
== FontMatchType::Kind::kSystemFallback
) {
3471 mTextPerf
->current
.fallbackSystem
++;
3478 ShapedTextFlags orient
= aOrientation
;
3479 if (aOrientation
== ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED
) {
3480 // For CSS text-orientation:mixed, we need to resolve orientation
3481 // on a per-character basis using the UTR50 orientation property.
3482 switch (GetVerticalOrientation(ch
)) {
3483 case VERTICAL_ORIENTATION_U
:
3484 case VERTICAL_ORIENTATION_Tu
:
3485 orient
= ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT
;
3487 case VERTICAL_ORIENTATION_Tr
: {
3488 // We check for a vertical presentation form first as that's
3489 // likely to be cheaper than inspecting lookups to see if the
3490 // 'vert' feature is going to handle this character, and if the
3491 // presentation form is available then it will be used as
3492 // fallback if needed, so it's OK if the feature is missing.
3494 // Because "common" CJK punctuation characters in isolation will be
3495 // resolved to Bopomofo script (as the first script listed in their
3496 // ScriptExtensions property), but this is not always well supported
3497 // by fonts' OpenType tables, we also try Han script; harfbuzz will
3498 // apply a 'vert' feature from any available script (see
3499 // https://github.com/harfbuzz/harfbuzz/issues/63) when shaping,
3500 // so this is OK. It's not quite as general as what harfbuzz does
3501 // (it will find the feature in *any* script), but should be enough
3502 // for likely real-world examples.
3503 uint32_t v
= gfxHarfBuzzShaper::GetVerticalPresentationForm(ch
);
3504 const uint32_t kVert
= HB_TAG('v', 'e', 'r', 't');
3505 orient
= (!font
|| (v
&& font
->HasCharacter(v
)) ||
3506 font
->FeatureWillHandleChar(aRunScript
, kVert
, ch
) ||
3507 (aRunScript
== Script::BOPOMOFO
&&
3508 font
->FeatureWillHandleChar(Script::HAN
, kVert
, ch
)))
3509 ? ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT
3510 : ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT
;
3513 case VERTICAL_ORIENTATION_R
:
3514 orient
= ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT
;
3519 if (lastRangeIndex
== -1) {
3520 // first char ==> make a new range
3521 aRanges
.AppendElement(TextRange(0, 1, font
, matchType
, orient
));
3525 // if font or orientation has changed, make a new range...
3526 // unless ch is a variation selector (bug 1248248)
3527 TextRange
& prevRange
= aRanges
[lastRangeIndex
];
3528 if (prevRange
.font
!= font
||
3529 (prevRange
.orientation
!= orient
&& !IsClusterExtender(ch
))) {
3530 // close out the previous range
3531 prevRange
.end
= origI
;
3532 aRanges
.AppendElement(TextRange(origI
, i
+ 1, font
, matchType
, orient
));
3535 // update prevFont for the next match, *unless* we switched
3536 // fonts on a ZWJ, in which case propagating the changed font
3537 // is probably not a good idea (see bug 619511)
3538 if (sizeof(T
) == sizeof(uint8_t) || !gfxFontUtils::IsJoinCauser(ch
)) {
3542 prevRange
.matchType
|= matchType
;
3547 aRanges
[lastRangeIndex
].end
= aLength
;
3549 #ifndef RELEASE_OR_BETA
3550 LogModule
* log
= mStyle
.systemFont
? gfxPlatform::GetLog(eGfxLog_textrunui
)
3551 : gfxPlatform::GetLog(eGfxLog_textrun
);
3553 if (MOZ_UNLIKELY(MOZ_LOG_TEST(log
, LogLevel::Debug
))) {
3555 mLanguage
->ToUTF8String(lang
);
3556 auto defaultLanguageGeneric
= GetDefaultGeneric(mLanguage
);
3558 // collect the font matched for each range
3559 nsAutoCString fontMatches
;
3560 for (size_t i
= 0, i_end
= aRanges
.Length(); i
< i_end
; i
++) {
3561 const TextRange
& r
= aRanges
[i
];
3562 nsAutoCString matchTypes
;
3563 if (r
.matchType
.kind
& FontMatchType::Kind::kFontGroup
) {
3564 matchTypes
.AppendLiteral("list");
3566 if (r
.matchType
.kind
& FontMatchType::Kind::kPrefsFallback
) {
3567 if (!matchTypes
.IsEmpty()) {
3568 matchTypes
.AppendLiteral(",");
3570 matchTypes
.AppendLiteral("prefs");
3572 if (r
.matchType
.kind
& FontMatchType::Kind::kSystemFallback
) {
3573 if (!matchTypes
.IsEmpty()) {
3574 matchTypes
.AppendLiteral(",");
3576 matchTypes
.AppendLiteral("sys");
3578 fontMatches
.AppendPrintf(
3579 " [%u:%u] %.200s (%s)", r
.start
, r
.end
,
3580 (r
.font
.get() ? r
.font
->GetName().get() : "<null>"),
3583 MOZ_LOG(log
, LogLevel::Debug
,
3584 ("(%s-fontmatching) fontgroup: [%s] default: %s lang: %s script: %d"
3586 (mStyle
.systemFont
? "textrunui" : "textrun"),
3587 FamilyListToString(mFamilyList
).get(),
3588 (defaultLanguageGeneric
== StyleGenericFontFamily::Serif
3590 : (defaultLanguageGeneric
== StyleGenericFontFamily::SansSerif
3593 lang
.get(), static_cast<int>(aRunScript
), fontMatches
.get()));
3598 gfxUserFontSet
* gfxFontGroup::GetUserFontSet() { return mUserFontSet
; }
3600 void gfxFontGroup::SetUserFontSet(gfxUserFontSet
* aUserFontSet
) {
3601 if (aUserFontSet
== mUserFontSet
) {
3604 mUserFontSet
= aUserFontSet
;
3605 mCurrGeneration
= GetGeneration() - 1;
3609 uint64_t gfxFontGroup::GetGeneration() {
3610 if (!mUserFontSet
) return 0;
3611 return mUserFontSet
->GetGeneration();
3614 uint64_t gfxFontGroup::GetRebuildGeneration() {
3615 if (!mUserFontSet
) return 0;
3616 return mUserFontSet
->GetRebuildGeneration();
3619 void gfxFontGroup::UpdateUserFonts() {
3620 if (mCurrGeneration
< GetRebuildGeneration()) {
3621 // fonts in userfont set changed, need to redo the fontlist
3625 mCurrGeneration
= GetGeneration();
3626 } else if (mCurrGeneration
!= GetGeneration()) {
3627 // load state change occurred, verify load state and validity of fonts
3630 uint32_t len
= mFonts
.Length();
3631 for (uint32_t i
= 0; i
< len
; i
++) {
3632 FamilyFace
& ff
= mFonts
[i
];
3633 if (ff
.Font() || !ff
.IsUserFontContainer()) {
3636 ff
.CheckState(mSkipDrawing
);
3639 mCurrGeneration
= GetGeneration();
3643 bool gfxFontGroup::ContainsUserFont(const gfxUserFontEntry
* aUserFont
) {
3645 // search through the fonts list for a specific user font
3646 uint32_t len
= mFonts
.Length();
3647 for (uint32_t i
= 0; i
< len
; i
++) {
3648 FamilyFace
& ff
= mFonts
[i
];
3649 if (ff
.EqualsUserFont(aUserFont
)) {
3656 gfxFont
* gfxFontGroup::WhichPrefFontSupportsChar(
3657 uint32_t aCh
, uint32_t aNextCh
, eFontPresentation aPresentation
) {
3658 eFontPrefLang charLang
;
3659 gfxPlatformFontList
* pfl
= gfxPlatformFontList::PlatformFontList();
3661 if (PrefersColor(aPresentation
)) {
3662 charLang
= eFontPrefLang_Emoji
;
3664 // get the pref font list if it hasn't been set up already
3665 charLang
= pfl
->GetFontPrefLangFor(aCh
);
3668 // if the last pref font was the first family in the pref list, no need to
3669 // recheck through a list of families
3670 if (mLastPrefFont
&& charLang
== mLastPrefLang
&& mLastPrefFirstFont
&&
3671 mLastPrefFont
->HasCharacter(aCh
)) {
3672 return mLastPrefFont
;
3675 // based on char lang and page lang, set up list of pref lang fonts to check
3676 eFontPrefLang prefLangs
[kMaxLenPrefLangList
];
3677 uint32_t i
, numLangs
= 0;
3679 pfl
->GetLangPrefs(prefLangs
, numLangs
, charLang
, mPageLang
);
3681 for (i
= 0; i
< numLangs
; i
++) {
3682 eFontPrefLang currentLang
= prefLangs
[i
];
3683 StyleGenericFontFamily generic
=
3684 mFallbackGeneric
!= StyleGenericFontFamily::None
3686 : pfl
->GetDefaultGeneric(currentLang
);
3687 gfxPlatformFontList::PrefFontList
* families
=
3688 pfl
->GetPrefFontsLangGroup(mPresContext
, generic
, currentLang
);
3689 NS_ASSERTION(families
, "no pref font families found");
3691 // find the first pref font that includes the character
3692 uint32_t j
, numPrefs
;
3693 numPrefs
= families
->Length();
3694 for (j
= 0; j
< numPrefs
; j
++) {
3695 // look up the appropriate face
3696 FontFamily family
= (*families
)[j
];
3697 if (family
.IsNull()) {
3701 // if a pref font is used, it's likely to be used again in the same text
3702 // run. the style doesn't change so the face lookup can be cached rather
3703 // than calling FindOrMakeFont repeatedly. speeds up FindFontForChar
3704 // lookup times for subsequent pref font lookups
3705 if (family
== mLastPrefFamily
&& mLastPrefFont
->HasCharacter(aCh
)) {
3706 return mLastPrefFont
;
3709 gfxFontEntry
* fe
= nullptr;
3710 if (family
.mIsShared
) {
3711 fontlist::Family
* fam
= family
.mShared
;
3712 if (!fam
->IsInitialized()) {
3713 Unused
<< pfl
->InitializeFamily(fam
);
3715 fontlist::Face
* face
=
3716 fam
->FindFaceForStyle(pfl
->SharedFontList(), mStyle
);
3718 fe
= pfl
->GetOrCreateFontEntry(face
, fam
);
3721 fe
= family
.mUnshared
->FindFontForStyle(mStyle
);
3727 // if ch in cmap, create and return a gfxFont
3728 gfxFont
* prefFont
= nullptr;
3729 if (fe
->HasCharacter(aCh
)) {
3730 prefFont
= fe
->FindOrMakeFont(&mStyle
);
3736 // If the char was not available, see if we can fall back to an
3737 // alternative face in the same family.
3739 prefFont
= family
.mIsShared
3740 ? FindFallbackFaceForChar(family
.mShared
, aCh
, aNextCh
,
3742 : FindFallbackFaceForChar(family
.mUnshared
, aCh
, aNextCh
,
3746 mLastPrefFamily
= family
;
3747 mLastPrefFont
= prefFont
;
3748 mLastPrefLang
= charLang
;
3749 mLastPrefFirstFont
= (i
== 0 && j
== 0);
3758 gfxFont
* gfxFontGroup::WhichSystemFontSupportsChar(
3759 uint32_t aCh
, uint32_t aNextCh
, Script aRunScript
,
3760 eFontPresentation aPresentation
) {
3761 FontVisibility visibility
;
3763 gfxPlatformFontList::PlatformFontList()->SystemFindFontForChar(
3764 mPresContext
, aCh
, aNextCh
, aRunScript
, aPresentation
, &mStyle
,
3773 void gfxMissingFontRecorder::Flush() {
3774 static bool mNotifiedFontsInitialized
= false;
3775 static uint32_t mNotifiedFonts
[gfxMissingFontRecorder::kNumScriptBitsWords
];
3776 if (!mNotifiedFontsInitialized
) {
3777 memset(&mNotifiedFonts
, 0, sizeof(mNotifiedFonts
));
3778 mNotifiedFontsInitialized
= true;
3781 nsAutoString fontNeeded
;
3782 for (uint32_t i
= 0; i
< kNumScriptBitsWords
; ++i
) {
3783 mMissingFonts
[i
] &= ~mNotifiedFonts
[i
];
3784 if (!mMissingFonts
[i
]) {
3787 for (uint32_t j
= 0; j
< 32; ++j
) {
3788 if (!(mMissingFonts
[i
] & (1 << j
))) {
3791 mNotifiedFonts
[i
] |= (1 << j
);
3792 if (!fontNeeded
.IsEmpty()) {
3793 fontNeeded
.Append(char16_t(','));
3795 uint32_t sc
= i
* 32 + j
;
3796 MOZ_ASSERT(sc
< static_cast<uint32_t>(Script::NUM_SCRIPT_CODES
),
3797 "how did we set the bit for an invalid script code?");
3798 uint32_t tag
= GetScriptTagForCode(static_cast<Script
>(sc
));
3799 fontNeeded
.Append(char16_t(tag
>> 24));
3800 fontNeeded
.Append(char16_t((tag
>> 16) & 0xff));
3801 fontNeeded
.Append(char16_t((tag
>> 8) & 0xff));
3802 fontNeeded
.Append(char16_t(tag
& 0xff));
3804 mMissingFonts
[i
] = 0;
3806 if (!fontNeeded
.IsEmpty()) {
3807 nsCOMPtr
<nsIObserverService
> service
= GetObserverService();
3808 service
->NotifyObservers(nullptr, "font-needed", fontNeeded
.get());