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