--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -62,16 +62,17 @@
#include "nsLineBreaker.h"
#include "nsIWordBreaker.h"
#include "nsGenericDOMDataNode.h"
#include "nsIFrameInlines.h"
#include "mozilla/StyleSetHandle.h"
#include "mozilla/StyleSetHandleInlines.h"
#include <algorithm>
+#include <limits>
#ifdef ACCESSIBILITY
#include "nsAccessibilityService.h"
#endif
#include "nsAutoPtr.h"
#include "nsPrintfCString.h"
#include "gfxContext.h"
@@ -4704,27 +4705,37 @@ nsDisplayText::ComputeInvalidationRegion
mVisIEndEdge != geometry->mVisIEndEdge ||
!oldRect.IsEqualInterior(newRect) ||
!geometry->mBorderRect.IsEqualInterior(GetBorderRect()) ||
mOpacity != geometry->mOpacity) {
aInvalidRegion->Or(oldRect, newRect);
}
}
+NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(TextCombineScaleFactorProperty, float)
+
+static float
+GetTextCombineScaleFactor(nsTextFrame* aFrame)
+{
+ float factor = aFrame->Properties().Get(TextCombineScaleFactorProperty());
+ return factor ? factor : 1.0f;
+}
+
void
nsDisplayText::Paint(nsDisplayListBuilder* aBuilder,
nsRenderingContext* aCtx) {
PROFILER_LABEL("nsDisplayText", "Paint",
js::ProfileEntry::Category::GRAPHICS);
// Add 1 pixel of dirty area around mVisibleRect to allow us to paint
// antialiased pixels beyond the measured text extents.
// This is temporary until we do this in the actual calculation of text extents.
- LayoutDeviceRect extraVisible = LayoutDeviceRect::FromAppUnits(
- mVisibleRect, mFrame->PresContext()->AppUnitsPerDevPixel());
+ auto A2D = mFrame->PresContext()->AppUnitsPerDevPixel();
+ LayoutDeviceRect extraVisible =
+ LayoutDeviceRect::FromAppUnits(mVisibleRect, A2D);
extraVisible.Inflate(1);
nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
DrawTargetAutoDisableSubpixelAntialiasing disable(aCtx->GetDrawTarget(),
mDisableSubpixelAA);
gfxContext* ctx = aCtx->ThebesContext();
gfxContextAutoSaveRestore save(ctx);
@@ -4738,16 +4749,28 @@ nsDisplayText::Paint(nsDisplayListBuilde
ctx->Rectangle(pixelVisible);
ctx->Clip();
}
NS_ASSERTION(mVisIStartEdge >= 0, "illegal start edge");
NS_ASSERTION(mVisIEndEdge >= 0, "illegal end edge");
nsPoint framePt = ToReferenceFrame();
+ if (f->StyleContext()->IsTextCombined()) {
+ float scaleFactor = GetTextCombineScaleFactor(f);
+ if (scaleFactor != 1.0f) {
+ // Setup matrix to compress text for text-combine-upright if
+ // necessary. This is done here because we want selection be
+ // compressed at the same time as text.
+ gfxPoint pt = nsLayoutUtils::PointToGfxPoint(framePt, A2D);
+ gfxMatrix mat = ctx->CurrentMatrix()
+ .Translate(pt).Scale(scaleFactor, 1.0).Translate(-pt);
+ ctx->SetMatrix(mat);
+ }
+ }
nsTextFrame::PaintTextParams params(aCtx->ThebesContext());
params.framePt = gfxPoint(framePt.x, framePt.y);
params.dirtyRect = extraVisible;
nsTextFrame::DrawPathCallbacks callbacks;
if (aBuilder->IsForGenerateGlyphPath()) {
params.callbacks = &callbacks;
}
@@ -6801,16 +6824,19 @@ nsTextFrame::GetCharacterOffsetAtFramePo
return offsets;
PropertyProvider provider(this, iter, nsTextFrame::eInflated);
// Trim leading but not trailing whitespace if possible
provider.InitializeForDisplay(false);
gfxFloat width = mTextRun->IsVertical()
? (mTextRun->IsInlineReversed() ? mRect.height - aPoint.y : aPoint.y)
: (mTextRun->IsInlineReversed() ? mRect.width - aPoint.x : aPoint.x);
+ if (StyleContext()->IsTextCombined()) {
+ width /= GetTextCombineScaleFactor(this);
+ }
gfxFloat fitWidth;
Range skippedRange = ComputeTransformedRange(provider);
uint32_t charsFit = CountCharsFit(mTextRun, skippedRange,
width, &provider, &fitWidth);
int32_t selectedOffset;
if (charsFit < skippedRange.Length()) {
@@ -7062,16 +7088,19 @@ nsTextFrame::GetPointFromOffset(int32_t
outPoint->y = iSize;
}
} else {
if (mTextRun->IsInlineReversed()) {
outPoint->x = mRect.width - iSize;
} else {
outPoint->x = iSize;
}
+ if (StyleContext()->IsTextCombined()) {
+ outPoint->x *= GetTextCombineScaleFactor(this);
+ }
}
return NS_OK;
}
nsresult
nsTextFrame::GetChildFrameContainingOffset(int32_t aContentOffset,
bool aHint,
@@ -7743,16 +7772,26 @@ nsTextFrame::AddInlineMinISizeForFlow(ns
bool collapseWhitespace = !textStyle->WhiteSpaceIsSignificant();
bool preformatNewlines = textStyle->NewlineIsSignificant(this);
bool preformatTabs = textStyle->WhiteSpaceIsSignificant();
gfxFloat tabWidth = -1;
uint32_t start =
FindStartAfterSkippingWhitespace(&provider, aData, textStyle, &iter, flowEndInTextRun);
+ // text-combine-upright frame is constantly 1em on inline-axis.
+ if (StyleContext()->IsTextCombined()) {
+ if (textRun->CanBreakLineBefore(start)) {
+ aData->OptionallyBreak();
+ }
+ aData->mCurrentLine += provider.GetFontMetrics()->EmHeight();
+ aData->mTrailingWhitespace = 0;
+ return;
+ }
+
AutoTArray<bool,BIG_TEXT_NODE_SIZE> hyphBuffer;
bool *hyphBreakBefore = nullptr;
if (hyphenating) {
hyphBreakBefore = hyphBuffer.AppendElements(flowEndInTextRun - start,
fallible);
if (hyphBreakBefore) {
provider.GetHyphenationBreaks(Range(start, flowEndInTextRun),
hyphBreakBefore);
@@ -7897,16 +7936,23 @@ nsTextFrame::AddInlinePrefISizeForFlow(n
// Pass null for the line container. This will disable tab spacing, but that's
// OK since we can't really handle tabs for intrinsic sizing anyway.
const nsStyleText* textStyle = StyleText();
const nsTextFragment* frag = mContent->GetText();
PropertyProvider provider(textRun, textStyle, frag, this,
iter, INT32_MAX, nullptr, 0, aTextRunType);
+ // text-combine-upright frame is constantly 1em on inline-axis.
+ if (StyleContext()->IsTextCombined()) {
+ aData->mCurrentLine += provider.GetFontMetrics()->EmHeight();
+ aData->mTrailingWhitespace = 0;
+ return;
+ }
+
bool collapseWhitespace = !textStyle->WhiteSpaceIsSignificant();
bool preformatNewlines = textStyle->NewlineIsSignificant(this);
bool preformatTabs = textStyle->TabIsSignificant();
gfxFloat tabWidth = -1;
uint32_t start =
FindStartAfterSkippingWhitespace(&provider, aData, textStyle, &iter, flowEndInTextRun);
// XXX Should we consider hyphenation here?
@@ -8630,16 +8676,21 @@ nsTextFrame::ReflowText(nsLineLayout& aL
gfxSkipCharsIterator iter(provider.GetStart());
iter.SetOriginalOffset(offset + limitLength);
transformedLength = iter.GetSkippedOffset() - transformedOffset;
}
uint32_t transformedLastBreak = 0;
bool usedHyphenation;
gfxFloat trimmedWidth = 0;
gfxFloat availWidth = aAvailableWidth;
+ if (StyleContext()->IsTextCombined()) {
+ // If text-combine-upright is 'all', we would compress whatever long
+ // text into ~1em width, so there is no limited on the avail width.
+ availWidth = std::numeric_limits<gfxFloat>::infinity();
+ }
bool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant() ||
(GetStateBits() & TEXT_IS_IN_TOKEN_MATHML);
gfxBreakPriority breakPriority = aLineLayout.LastOptionalBreakPriority();
gfxTextRun::SuppressBreak suppressBreak = gfxTextRun::eNoSuppressBreak;
bool shouldSuppressLineBreak = ShouldSuppressLineBreak();
if (shouldSuppressLineBreak) {
suppressBreak = gfxTextRun::eSuppressAllBreaks;
} else if (!aLineLayout.LineIsBreakable()) {
@@ -8737,17 +8788,17 @@ nsTextFrame::ReflowText(nsLineLayout& aL
textMetrics.mBoundingBox.MoveBy(gfxPoint(trimmedWidth, 0));
}
}
}
if (!brokeText && lastBreak >= 0) {
// Since everything fit and no break was forced,
// record the last break opportunity
- NS_ASSERTION(textMetrics.mAdvanceWidth - trimmableWidth <= aAvailableWidth,
+ NS_ASSERTION(textMetrics.mAdvanceWidth - trimmableWidth <= availWidth,
"If the text doesn't fit, and we have a break opportunity, why didn't MeasureText use it?");
MOZ_ASSERT(lastBreak >= offset, "Strange break position");
aLineLayout.NotifyOptionalBreakPosition(this, lastBreak - offset,
true, breakPriority);
}
int32_t contentLength = offset + charsFit - GetContentOffset();
@@ -8785,21 +8836,41 @@ nsTextFrame::ReflowText(nsLineLayout& aL
nscoord fontAscent =
wm.IsLineInverted() ? fm->MaxDescent() : fm->MaxAscent();
nscoord fontDescent =
wm.IsLineInverted() ? fm->MaxAscent() : fm->MaxDescent();
aMetrics.SetBlockStartAscent(std::max(NSToCoordCeil(textMetrics.mAscent), fontAscent));
nscoord descent = std::max(NSToCoordCeil(textMetrics.mDescent), fontDescent);
finalSize.BSize(wm) = aMetrics.BlockStartAscent() + descent;
}
+ if (StyleContext()->IsTextCombined()) {
+ nsFontMetrics* fm = provider.GetFontMetrics();
+ gfxFloat width = finalSize.ISize(wm);
+ gfxFloat em = fm->EmHeight();
+ // Compress the characters in horizontal axis if necessary.
+ if (width <= em) {
+ Properties().Remove(TextCombineScaleFactorProperty());
+ } else {
+ Properties().Set(TextCombineScaleFactorProperty(), em / width);
+ finalSize.ISize(wm) = em;
+ }
+ // Make the characters be in an 1em square.
+ if (finalSize.BSize(wm) != em) {
+ aMetrics.SetBlockStartAscent(aMetrics.BlockStartAscent() +
+ (em - finalSize.BSize(wm)) / 2);
+ finalSize.BSize(wm) = em;
+ }
+ }
aMetrics.SetSize(wm, finalSize);
NS_ASSERTION(aMetrics.BlockStartAscent() >= 0,
"Negative ascent???");
- NS_ASSERTION(aMetrics.BSize(aMetrics.GetWritingMode()) -
+ NS_ASSERTION((StyleContext()->IsTextCombined()
+ ? aMetrics.ISize(aMetrics.GetWritingMode())
+ : aMetrics.BSize(aMetrics.GetWritingMode())) -
aMetrics.BlockStartAscent() >= 0,
"Negative descent???");
mAscent = aMetrics.BlockStartAscent();
// Handle text that runs outside its normal bounds.
nsRect boundingBox = RoundOut(textMetrics.mBoundingBox);
if (mTextRun->IsVertical()) {