Bug 1111944 - part 1 - Make nsLayoutUtils::DrawString (and the nsFontMetrics methods it calls) handle vertical text. r=smontagu
--- 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);