Bug 1111944 - part 1 - Make nsLayoutUtils::DrawString (and the nsFontMetrics methods it calls) handle vertical text. r=smontagu
authorJonathan Kew <jkew@mozilla.com>
Tue, 06 Jan 2015 20:56:03 +0000
changeset 239008 646e58995d5356cdc21cb64d909dfa8f3fd8ba44
parent 239007 1e64da07365b304b6819a7f06dd76458b6c7ee1f
child 239009 603b4a6ccd6a9334dfc693e5cac3886b374a2faf
push id7472
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 20:36:27 +0000
treeherdermozilla-aurora@300ca104f8fb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmontagu
bugs1111944
milestone37.0a1
Bug 1111944 - part 1 - Make nsLayoutUtils::DrawString (and the nsFontMetrics methods it calls) handle vertical text. r=smontagu
gfx/src/nsFontMetrics.cpp
gfx/src/nsFontMetrics.h
layout/base/nsBidiPresUtils.cpp
layout/base/nsLayoutUtils.cpp
--- a/gfx/src/nsFontMetrics.cpp
+++ b/gfx/src/nsFontMetrics.cpp
@@ -52,16 +52,29 @@ public:
     gfxTextRun *operator->() { return mTextRun; }
 
 private:
     static uint32_t ComputeFlags(nsFontMetrics* aMetrics) {
         uint32_t flags = 0;
         if (aMetrics->GetTextRunRTL()) {
             flags |= gfxTextRunFactory::TEXT_IS_RTL;
         }
+        if (aMetrics->GetVertical()) {
+            switch (aMetrics->GetTextOrientation()) {
+            case NS_STYLE_TEXT_ORIENTATION_MIXED:
+                flags |= gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED;
+                break;
+            case NS_STYLE_TEXT_ORIENTATION_UPRIGHT:
+                flags |= gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
+                break;
+            case NS_STYLE_TEXT_ORIENTATION_SIDEWAYS_RIGHT:
+                flags |= gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
+                break;
+            }
+        }
         return flags;
     }
 
     nsAutoPtr<gfxTextRun> mTextRun;
 };
 
 class StubPropertyProvider : public gfxTextRun::PropertyProvider {
 public:
@@ -90,16 +103,17 @@ public:
         NS_ERROR("This shouldn't be called because we never enable spacing");
     }
 };
 
 } // anon namespace
 
 nsFontMetrics::nsFontMetrics()
     : mDeviceContext(nullptr), mP2A(0), mTextRunRTL(false)
+    , mVertical(false), mTextOrientation(0)
 {
 }
 
 nsFontMetrics::~nsFontMetrics()
 {
     if (mDeviceContext)
         mDeviceContext->FontMetricsDeleted(this);
 }
@@ -146,19 +160,20 @@ nsFontMetrics::Destroy()
 {
     mDeviceContext = nullptr;
 }
 
 // XXXTODO get rid of this macro
 #define ROUND_TO_TWIPS(x) (nscoord)floor(((x) * mP2A) + 0.5)
 #define CEIL_TO_TWIPS(x) (nscoord)ceil((x) * mP2A)
 
-const gfxFont::Metrics& nsFontMetrics::GetMetrics() const
+const gfxFont::Metrics&
+nsFontMetrics::GetMetrics(gfxFont::Orientation aOrientation) const
 {
-    return mFontGroup->GetFirstValidFont()->GetMetrics(mOrientation);
+    return mFontGroup->GetFirstValidFont()->GetMetrics(aOrientation);
 }
 
 nscoord
 nsFontMetrics::XHeight()
 {
     return ROUND_TO_TWIPS(GetMetrics().xHeight);
 }
 
@@ -269,17 +284,24 @@ nsFontMetrics::AveCharWidth()
 {
     // Use CEIL instead of ROUND for consistency with GetMaxAdvance
     return CEIL_TO_TWIPS(GetMetrics().aveCharWidth);
 }
 
 nscoord
 nsFontMetrics::SpaceWidth()
 {
-    return CEIL_TO_TWIPS(GetMetrics().spaceWidth);
+    // For vertical text with mixed or sideways orientation, we want the
+    // width of a horizontal space (even if we're using vertical line-spacing
+    // metrics, as with "writing-mode:vertical-*;text-orientation:mixed").
+    return CEIL_TO_TWIPS(
+        GetMetrics(mVertical &&
+                   mTextOrientation == NS_STYLE_TEXT_ORIENTATION_UPRIGHT
+                       ? gfxFont::eVertical
+                       : gfxFont::eHorizontal).spaceWidth);
 }
 
 int32_t
 nsFontMetrics::GetMaxStringLength()
 {
     const gfxFont::Metrics& m = GetMetrics();
     const double x = 32767.0 / m.maxAdvance;
     int32_t len = (int32_t)floor(x);
@@ -329,17 +351,21 @@ nsFontMetrics::DrawString(const char *aS
 
     StubPropertyProvider provider;
     AutoTextRun textRun(this, aContext, aString, aLength);
     if (!textRun.get()) {
         return;
     }
     gfxPoint pt(aX, aY);
     if (mTextRunRTL) {
-        pt.x += textRun->GetAdvanceWidth(0, aLength, &provider);
+        if (mVertical) {
+            pt.y += textRun->GetAdvanceWidth(0, aLength, &provider);
+        } else {
+            pt.x += textRun->GetAdvanceWidth(0, aLength, &provider);
+        }
     }
     textRun->Draw(aContext->ThebesContext(), pt, DrawMode::GLYPH_FILL, 0, aLength,
                   &provider, nullptr, nullptr);
 }
 
 void
 nsFontMetrics::DrawString(const char16_t* aString, uint32_t aLength,
                           nscoord aX, nscoord aY,
@@ -351,17 +377,21 @@ nsFontMetrics::DrawString(const char16_t
 
     StubPropertyProvider provider;
     AutoTextRun textRun(this, aTextRunConstructionContext, aString, aLength);
     if (!textRun.get()) {
         return;
     }
     gfxPoint pt(aX, aY);
     if (mTextRunRTL) {
-        pt.x += textRun->GetAdvanceWidth(0, aLength, &provider);
+        if (mVertical) {
+            pt.y += textRun->GetAdvanceWidth(0, aLength, &provider);
+        } else {
+            pt.x += textRun->GetAdvanceWidth(0, aLength, &provider);
+        }
     }
     textRun->Draw(aContext->ThebesContext(), pt, DrawMode::GLYPH_FILL, 0, aLength,
                   &provider, nullptr, nullptr);
 }
 
 static nsBoundingMetrics
 GetTextBoundingMetrics(nsFontMetrics* aMetrics, const char16_t *aString, uint32_t aLength,
                        nsRenderingContext *aContext, gfxFont::BoundingBoxType aType)
--- a/gfx/src/nsFontMetrics.h
+++ b/gfx/src/nsFontMetrics.h
@@ -164,27 +164,27 @@ public:
      * Returns the often needed width of the space character
      */
     nscoord SpaceWidth();
 
     /**
      * Returns the font associated with these metrics. The return value
      * is only defined after Init() has been called.
      */
-    const nsFont &Font() { return mFont; }
+    const nsFont &Font() const { return mFont; }
 
     /**
      * Returns the language associated with these metrics
      */
-    nsIAtom* Language() { return mLanguage; }
+    nsIAtom* Language() const { return mLanguage; }
 
     /**
      * Returns the orientation (horizontal/vertical) of these metrics.
      */
-    gfxFont::Orientation Orientation() { return mOrientation; }
+    gfxFont::Orientation Orientation() const { return mOrientation; }
 
     int32_t GetMaxStringLength();
 
     // Get the width for this string.  aWidth will be updated with the
     // width in points, not twips.  Callers must convert it if they
     // want it in another format.
     nscoord GetWidth(const char* aString, uint32_t aLength,
                      nsRenderingContext *aContext);
@@ -206,31 +206,58 @@ public:
 
     // Returns the LOOSE_INK_EXTENTS bounds of the text for determing the
     // overflow area of the string.
     nsBoundingMetrics GetInkBoundsForVisualOverflow(const char16_t *aString,
                                                     uint32_t aLength,
                                                     nsRenderingContext *aContext);
 
     void SetTextRunRTL(bool aIsRTL) { mTextRunRTL = aIsRTL; }
-    bool GetTextRunRTL() { return mTextRunRTL; }
+    bool GetTextRunRTL() const { return mTextRunRTL; }
+
+    void SetVertical(bool aVertical) { mVertical = aVertical; }
+    bool GetVertical() const { return mVertical; }
 
-    gfxFontGroup* GetThebesFontGroup() { return mFontGroup; }
-    gfxUserFontSet* GetUserFontSet() { return mFontGroup->GetUserFontSet(); }
+    void SetTextOrientation(uint8_t aTextOrientation)
+    {
+      mTextOrientation = aTextOrientation;
+    }
+    uint8_t GetTextOrientation() const { return mTextOrientation; }
 
-    int32_t AppUnitsPerDevPixel() { return mP2A; }
+    gfxFontGroup* GetThebesFontGroup() const { return mFontGroup; }
+    gfxUserFontSet* GetUserFontSet() const
+    {
+      return mFontGroup->GetUserFontSet();
+    }
+
+    int32_t AppUnitsPerDevPixel() const { return mP2A; }
 
 private:
     // Private destructor, to discourage deletion outside of Release():
     ~nsFontMetrics();
 
-    const gfxFont::Metrics& GetMetrics() const;
+    const gfxFont::Metrics& GetMetrics() const {
+      return GetMetrics(mOrientation);
+    }
+
+    const gfxFont::Metrics&
+    GetMetrics(const gfxFont::Orientation aFontOrientation) const;
 
     nsFont mFont;
     nsRefPtr<gfxFontGroup> mFontGroup;
     nsCOMPtr<nsIAtom> mLanguage;
     nsDeviceContext *mDeviceContext;
     int32_t mP2A;
+
+    // The font orientation (horizontal or vertical) for which these metrics
+    // have been initialized. This determines which line metrics (ascent and
+    // descent) they will return.
+    gfxFont::Orientation mOrientation;
+
+    // These fields may be set by clients to control the behavior of methods
+    // like GetWidth and DrawString according to the writing mode, direction
+    // and text-orientation desired.
     bool mTextRunRTL;
-    gfxFont::Orientation mOrientation;
+    bool mVertical;
+    uint8_t mTextOrientation;
 };
 
 #endif /* NSFONTMETRICS__H__ */
--- a/layout/base/nsBidiPresUtils.cpp
+++ b/layout/base/nsBidiPresUtils.cpp
@@ -2085,20 +2085,26 @@ public:
   }
 
   virtual nscoord GetWidth() MOZ_OVERRIDE
   {
     return nsLayoutUtils::AppUnitWidthOfString(mText, mLength, *mFontMetrics,
                                                *mTextRunConstructionContext);
   }
 
-  virtual void DrawText(nscoord aXOffset,
+  virtual void DrawText(nscoord aIOffset,
                         nscoord) MOZ_OVERRIDE
   {
-    mFontMetrics->DrawString(mText, mLength, mPt.x + aXOffset, mPt.y,
+    nsPoint pt(mPt);
+    if (mFontMetrics->GetVertical()) {
+      pt.y += aIOffset;
+    } else {
+      pt.x += aIOffset;
+    }
+    mFontMetrics->DrawString(mText, mLength, pt.x, pt.y,
                              mCtx, mTextRunConstructionContext);
   }
 
 private:
   nsRenderingContext* mCtx;
   nsRenderingContext* mTextRunConstructionContext;
   nsFontMetrics* mFontMetrics;
   nsPoint mPt;
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -5110,16 +5110,18 @@ nsLayoutUtils::AppUnitWidthOfStringBidi(
   if (presContext->BidiEnabled()) {
     nsBidiLevel level =
       nsBidiPresUtils::BidiLevelFromStyle(aFrame->StyleContext());
     return nsBidiPresUtils::MeasureTextWidth(aString, aLength, level,
                                              presContext, aContext,
                                              aFontMetrics);
   }
   aFontMetrics.SetTextRunRTL(false);
+  aFontMetrics.SetVertical(aFrame->GetWritingMode().IsVertical());
+  aFontMetrics.SetTextOrientation(aFrame->StyleVisibility()->mTextOrientation);
   return nsLayoutUtils::AppUnitWidthOfString(aString, aLength, aFontMetrics,
                                              aContext);
 }
 
 nsBoundingMetrics
 nsLayoutUtils::AppUnitBoundsOfString(const char16_t* aString,
                                      uint32_t aLength,
                                      nsFontMetrics& aFontMetrics,
@@ -5151,21 +5153,29 @@ nsLayoutUtils::DrawString(const nsIFrame
                           nsFontMetrics&        aFontMetrics,
                           nsRenderingContext*   aContext,
                           const char16_t*      aString,
                           int32_t               aLength,
                           nsPoint               aPoint,
                           nsStyleContext*       aStyleContext)
 {
   nsresult rv = NS_ERROR_FAILURE;
+
+  // If caller didn't pass a style context, use the frame's.
+  if (!aStyleContext) {
+    aStyleContext = aFrame->StyleContext();
+  }
+  aFontMetrics.SetVertical(WritingMode(aStyleContext).IsVertical());
+  aFontMetrics.SetTextOrientation(
+    aStyleContext->StyleVisibility()->mTextOrientation);
+
   nsPresContext* presContext = aFrame->PresContext();
   if (presContext->BidiEnabled()) {
     nsBidiLevel level =
-      nsBidiPresUtils::BidiLevelFromStyle(aStyleContext ?
-                                          aStyleContext : aFrame->StyleContext());
+      nsBidiPresUtils::BidiLevelFromStyle(aStyleContext);
     rv = nsBidiPresUtils::RenderText(aString, aLength, level,
                                      presContext, *aContext, *aContext,
                                      aFontMetrics,
                                      aPoint.x, aPoint.y);
   }
   if (NS_FAILED(rv))
   {
     aFontMetrics.SetTextRunRTL(false);