Bug 1097499 part 9 - Transform full-width characters to non-full-width correspondents for combined text. r=jfkthame draft
authorXidorn Quan <quanxunzhen@gmail.com>
Fri, 15 Apr 2016 15:57:33 +1000
changeset 354604 36338bcfa45d6503fe7af92fbf817cc81199da0e
parent 354603 ba323d6b6c32d9fc502edee4de49b1fd1970af3b
child 354605 63720a875ef7009bdc0f698f0c152ff9246a4ba0
push id16138
push userxquan@mozilla.com
push dateThu, 21 Apr 2016 11:18:53 +0000
reviewersjfkthame
bugs1097499
milestone48.0a1
Bug 1097499 part 9 - Transform full-width characters to non-full-width correspondents for combined text. r=jfkthame MozReview-Commit-ID: CXntBz9HPJu
layout/generic/nsTextFrame.cpp
layout/generic/nsTextFrame.h
layout/generic/nsTextRunTransformations.cpp
layout/generic/nsTextRunTransformations.h
--- 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 {