diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 05540292a..47b378954 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -2717,6 +2717,8 @@ gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext, aTextRun->SetIsTab(aOffset + i); } else if (ch == '\n') { aTextRun->SetIsNewline(aOffset + i); + } else if (GetGeneralCategory(ch) == HB_UNICODE_GENERAL_CATEGORY_FORMAT) { + aTextRun->SetIsFormattingControl(aOffset + i); } else if (IsInvalidControlChar(ch) && !(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) { if (GetFontEntry()->IsUserFont() && HasCharacter(ch)) { @@ -2931,6 +2933,8 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext, aTextRun->SetIsTab(aRunStart + i); } else if (ch == '\n') { aTextRun->SetIsNewline(aRunStart + i); + } else if (GetGeneralCategory(ch) == HB_UNICODE_GENERAL_CATEGORY_FORMAT) { + aTextRun->SetIsFormattingControl(aRunStart + i); } else if (IsInvalidControlChar(ch) && !(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) { if (GetFontEntry()->IsUserFont() && HasCharacter(ch)) { diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h index 09ff0df93..d9982cc38 100644 --- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -747,7 +747,12 @@ public: // which is not combined with any combining characters. This flag is // set for all those characters except 0x20 whitespace. FLAG_CHAR_NO_EMPHASIS_MARK = 0x20, - CHAR_TYPE_FLAGS_MASK = 0x38, + // Per CSS Text, letter-spacing is not applied to formatting chars + // (category Cf). We mark those in the textrun so as to be able to + // skip them when setting up spacing in nsTextFrame. + FLAG_CHAR_IS_FORMATTING_CONTROL = 0x40, + + CHAR_TYPE_FLAGS_MASK = 0x78, GLYPH_COUNT_MASK = 0x00FFFF00U, GLYPH_COUNT_SHIFT = 8 @@ -802,6 +807,10 @@ public: return !CharIsSpace() && (IsSimpleGlyph() || !(mValue & FLAG_CHAR_NO_EMPHASIS_MARK)); } + bool CharIsFormattingControl() const { + return !IsSimpleGlyph() && + (mValue & FLAG_CHAR_IS_FORMATTING_CONTROL) != 0; + } uint32_t CharTypeFlags() const { return IsSimpleGlyph() ? 0 : (mValue & CHAR_TYPE_FLAGS_MASK); @@ -880,6 +889,10 @@ public: NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph"); mValue |= FLAG_CHAR_NO_EMPHASIS_MARK; } + void SetIsFormattingControl() { + NS_ASSERTION(!IsSimpleGlyph(), "Expected non-simple-glyph"); + mValue |= FLAG_CHAR_IS_FORMATTING_CONTROL; + } private: uint32_t mValue; diff --git a/gfx/thebes/gfxTextRun.cpp b/gfx/thebes/gfxTextRun.cpp index 8698b7997..54ef1f069 100644 --- a/gfx/thebes/gfxTextRun.cpp +++ b/gfx/thebes/gfxTextRun.cpp @@ -1947,8 +1947,12 @@ gfxFontGroup::IsInvalidChar(char16_t ch) if (ch <= 0x9f) { return true; } + // Word-separating format/bidi control characters are not shaped as part + // of words. return (((ch & 0xFF00) == 0x2000 /* Unicode control character */ && - (ch == 0x200B/*ZWSP*/ || ch == 0x2028/*LSEP*/ || ch == 0x2029/*PSEP*/)) || + (ch == 0x200B/*ZWSP*/ || ch == 0x2028/*LSEP*/ || + ch == 0x2029/*PSEP*/ || ch == 0x2060/*WJ*/)) || + ch == 0xfeff/*ZWNBSP*/ || IsBidiControl(ch)); } diff --git a/gfx/thebes/gfxTextRun.h b/gfx/thebes/gfxTextRun.h index 8042393a2..d2fa28f4c 100644 --- a/gfx/thebes/gfxTextRun.h +++ b/gfx/thebes/gfxTextRun.h @@ -133,6 +133,10 @@ public: NS_ASSERTION(aPos < GetLength(), "aPos out of range"); return mCharacterGlyphs[aPos].CharMayHaveEmphasisMark(); } + bool CharIsFormattingControl(uint32_t aPos) const { + MOZ_ASSERT(aPos < GetLength()); + return mCharacterGlyphs[aPos].CharIsFormattingControl(); + } // All uint32_t aStart, uint32_t aLength ranges below are restricted to // grapheme cluster boundaries! All offsets are in terms of the string @@ -532,6 +536,9 @@ public: void SetNoEmphasisMark(uint32_t aIndex) { EnsureComplexGlyph(aIndex).SetNoEmphasisMark(); } + void SetIsFormattingControl(uint32_t aIndex) { + EnsureComplexGlyph(aIndex).SetIsFormattingControl(); + } /** * Prefetch all the glyph extents needed to ensure that Measure calls diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index 09bfa9548..4e345cb45 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -3218,12 +3218,15 @@ PropertyProvider::GetSpacing(uint32_t aStart, uint32_t aLength, } static bool -CanAddSpacingAfter(gfxTextRun* aTextRun, uint32_t aOffset) +CanAddSpacingAfter(gfxTextRun* aTextRun, uint32_t aOffset, + bool aNewlineIsSignificant) { if (aOffset + 1 >= aTextRun->GetLength()) return true; return aTextRun->IsClusterStart(aOffset + 1) && - aTextRun->IsLigatureGroupStart(aOffset + 1); + aTextRun->IsLigatureGroupStart(aOffset + 1) && + !aTextRun->CharIsFormattingControl(aOffset) && + !(aNewlineIsSignificant && aTextRun->CharIsNewline(aOffset)); } void @@ -3247,11 +3250,13 @@ PropertyProvider::GetSpacingInternal(uint32_t aStart, uint32_t aLength, // Iterate over non-skipped characters nsSkipCharsRunIterator run(start, nsSkipCharsRunIterator::LENGTH_UNSKIPPED_ONLY, aLength); + bool newlineIsSignificant = mTextStyle->NewlineIsSignificant(mFrame); while (run.NextRun()) { uint32_t runOffsetInSubstring = run.GetSkippedOffset() - aStart; gfxSkipCharsIterator iter = run.GetPos(); for (int32_t i = 0; i < run.GetRunLength(); ++i) { - if (CanAddSpacingAfter(mTextRun, run.GetSkippedOffset() + i)) { + if (CanAddSpacingAfter(mTextRun, run.GetSkippedOffset() + i, + newlineIsSignificant)) { // End of a cluster, not in a ligature: put letter-spacing after it aSpacing[runOffsetInSubstring + i].mAfter += mLetterSpacing; }