Bug 1097499 part 9 - Transform full-width characters to non-full-width correspondents for combined text. r=jfkthame
MozReview-Commit-ID: CXntBz9HPJu
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -1898,17 +1898,20 @@ BuildTextRunsScanner::BuildTextRunForFra
nsStyleContext* lastStyleContext = nullptr;
for (uint32_t i = 0; i < mMappedFlows.Length(); ++i) {
MappedFlow* mappedFlow = &mMappedFlows[i];
nsTextFrame* f = mappedFlow->mStartFrame;
lastStyleContext = f->StyleContext();
// Detect use of text-transform or font-variant anywhere in the run
textStyle = f->StyleText();
- if (NS_STYLE_TEXT_TRANSFORM_NONE != textStyle->mTextTransform) {
+ if (NS_STYLE_TEXT_TRANSFORM_NONE != textStyle->mTextTransform ||
+ // text-combine-upright requires converting from full-width
+ // characters to non-full-width correspendent in some cases.
+ lastStyleContext->IsTextCombined()) {
anyTextTransformStyle = true;
}
if (textStyle->HasTextEmphasis()) {
anyTextEmphasis = true;
}
textFlags |= GetSpacingFlags(f);
nsTextFrameUtils::CompressionMode compression =
GetCSSWhitespaceToCompressionMode(f, textStyle);
@@ -2129,19 +2132,24 @@ BuildTextRunsScanner::BuildTextRunForFra
nsTextFrame* f;
nsStyleContext* sc = nullptr;
RefPtr<nsTransformedCharStyle> charStyle;
for (f = mappedFlow->mStartFrame; f != mappedFlow->mEndFrame;
f = static_cast<nsTextFrame*>(f->GetNextContinuation())) {
uint32_t offset = iter.GetSkippedOffset();
iter.AdvanceOriginal(f->GetContentLength());
uint32_t end = iter.GetSkippedOffset();
- if (sc != f->StyleContext()) {
+ // Text-combined frames have content-dependent transform, so we
+ // want to create new nsTransformedCharStyle for them anyway.
+ if (sc != f->StyleContext() || sc->IsTextCombined()) {
sc = f->StyleContext();
charStyle = new nsTransformedCharStyle(sc);
+ if (sc->IsTextCombined() && f->CountGraphemeClusters() > 1) {
+ charStyle->mForceNonFullWidth = true;
+ }
}
uint32_t j;
for (j = offset; j < end; ++j) {
styles.AppendElement(charStyle);
}
}
}
textFlags |= nsTextFrameUtils::TEXT_IS_TRANSFORMED;
@@ -9535,8 +9543,18 @@ mozilla::JustificationAssignment
nsTextFrame::GetJustificationAssignment() const
{
int32_t encoded = Properties().Get(JustificationAssignmentProperty());
mozilla::JustificationAssignment result;
result.mGapsAtStart = encoded >> 8;
result.mGapsAtEnd = encoded & 0xFF;
return result;
}
+
+uint32_t
+nsTextFrame::CountGraphemeClusters() const
+{
+ const nsTextFragment* frag = GetContent()->GetText();
+ MOZ_ASSERT(frag, "Text frame must have text fragment");
+ nsAutoString content;
+ frag->AppendTo(content, GetContentOffset(), GetContentLength());
+ return unicode::CountGraphemeClusters(content.Data(), content.Length());
+}
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -563,16 +563,18 @@ public:
bool IsFloatingFirstLetterChild() const;
virtual bool UpdateOverflow() override;
void AssignJustificationGaps(const mozilla::JustificationAssignment& aAssign);
mozilla::JustificationAssignment GetJustificationAssignment() const;
+ uint32_t CountGraphemeClusters() const;
+
protected:
virtual ~nsTextFrame();
gfxTextRun* mTextRun;
nsIFrame* mNextContinuation;
// The key invariant here is that mContentOffset never decreases along
// a next-continuation chain. And of course mContentOffset is always <= the
// the text node's content length, and the mContentOffset for the first frame
--- a/layout/generic/nsTextRunTransformations.cpp
+++ b/layout/generic/nsTextRunTransformations.cpp
@@ -294,31 +294,33 @@ nsCaseTransformTextRunFactory::Transform
bool capitalizeDutchIJ = false;
bool prevIsLetter = false;
bool ntPrefix = false; // true immediately after a word-initial 'n' or 't'
// when doing Irish lowercasing
uint32_t sigmaIndex = uint32_t(-1);
nsIUGenCategory::nsUGenCategory cat;
uint8_t style = aAllUppercase ? NS_STYLE_TEXT_TRANSFORM_UPPERCASE : 0;
+ bool forceNonFullWidth = false;
const nsIAtom* lang = aLanguage;
LanguageSpecificCasingBehavior languageSpecificCasing = GetCasingFor(lang);
mozilla::GreekCasing::State greekState;
mozilla::IrishCasing::State irishState;
uint32_t irishMark = uint32_t(-1); // location of possible prefix letter(s)
for (uint32_t i = 0; i < length; ++i) {
uint32_t ch = str[i];
RefPtr<nsTransformedCharStyle> charStyle;
if (aTextRun) {
charStyle = aTextRun->mStyles[i];
style = aAllUppercase ? NS_STYLE_TEXT_TRANSFORM_UPPERCASE :
charStyle->mTextTransform;
+ forceNonFullWidth = charStyle->mForceNonFullWidth;
nsIAtom* newLang = charStyle->mExplicitLanguage
? charStyle->mLanguage : nullptr;
if (lang != newLang) {
lang = newLang;
languageSpecificCasing = GetCasingFor(lang);
greekState.Reset();
irishState.Reset();
@@ -555,16 +557,20 @@ nsCaseTransformTextRunFactory::Transform
case NS_STYLE_TEXT_TRANSFORM_FULLWIDTH:
ch = mozilla::unicode::GetFullWidth(ch);
break;
default:
break;
}
+ if (forceNonFullWidth) {
+ ch = mozilla::unicode::GetFullWidthInverse(ch);
+ }
+
if (ch == uint32_t(-1)) {
aDeletedCharsArray.AppendElement(true);
mergeNeeded = true;
} else {
aDeletedCharsArray.AppendElement(false);
aCharsToMergeArray.AppendElement(false);
if (aTextRun) {
aStyleArray->AppendElement(charStyle);
--- a/layout/generic/nsTextRunTransformations.h
+++ b/layout/generic/nsTextRunTransformations.h
@@ -27,16 +27,17 @@ struct nsTransformedCharStyle final {
nsFont mFont;
nsCOMPtr<nsIAtom> mLanguage;
RefPtr<nsPresContext> mPresContext;
float mScriptSizeMultiplier;
uint8_t mTextTransform;
uint8_t mMathVariant;
bool mExplicitLanguage;
+ bool mForceNonFullWidth = false;
private:
~nsTransformedCharStyle() {}
nsTransformedCharStyle(const nsTransformedCharStyle& aOther) = delete;
nsTransformedCharStyle& operator=(const nsTransformedCharStyle& aOther) = delete;
};
class nsTransformingTextRunFactory {