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 248096 646e58995d5356cdc21cb64d909dfa8f3fd8ba44
parent 248095 1e64da07365b304b6819a7f06dd76458b6c7ee1f
child 248097 603b4a6ccd6a9334dfc693e5cac3886b374a2faf
push id4489
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 15:17:55 +0000
treeherdermozilla-beta@fd7c3dc24146 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmontagu
bugs1111944
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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);