@@ -4752,11 +4752,27 @@ nsresult nsTextFrame::CharacterDataChanged(
47524752 return NS_OK;
47534753}
47544754
4755- NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE (TextCombineScaleFactorProperty, float )
4755+ struct TextCombineData {
4756+ // Measured advance of the text before any text-combine scaling is applied.
4757+ nscoord mNaturalWidth = 0 ;
4758+ // Inline offset to place this text within the 1-em block of the upright-
4759+ // combined cell.
4760+ nscoord mOffset = 0 ;
4761+ // Inline scaling factor to apply (always <= 1.0, as the text may be
4762+ // compressed but is never expanded to fit the 1-em cell).
4763+ float mScale = 1 .0f ;
4764+ };
4765+
4766+ NS_DECLARE_FRAME_PROPERTY_DELETABLE (TextCombineDataProperty, TextCombineData)
4767+
4768+ float nsTextFrame::GetTextCombineScale() const {
4769+ const auto * data = GetProperty (TextCombineDataProperty ());
4770+ return data ? data->mScale : 1 .0f ;
4771+ }
47564772
4757- float nsTextFrame::GetTextCombineScaleFactor(nsTextFrame* aFrame) {
4758- float factor = aFrame-> GetProperty (TextCombineScaleFactorProperty ());
4759- return factor ? factor : 1 .0f ;
4773+ std::pair<nscoord, float > nsTextFrame::GetTextCombineOffsetAndScale () const {
4774+ const auto * data = GetProperty (TextCombineDataProperty ());
4775+ return data ? std::pair (data-> mOffset , data-> mScale ) : std::pair ( 0 , 1 .0f ) ;
47604776}
47614777
47624778void nsTextFrame::BuildDisplayList (nsDisplayListBuilder* aBuilder,
@@ -6352,9 +6368,13 @@ bool nsTextFrame::PaintTextWithSelectionColors(
63526368 nscoord (aParams.framePt .y + offs), GetSize ().width ,
63536369 nscoord (advance));
63546370 } else {
6371+ // For text-combine-upright, we may have collapsed the frame height,
6372+ // so we instead use 1em to ensure the selection is visible.
6373+ nscoord height = Style ()->IsTextCombined ()
6374+ ? aParams.provider ->GetFontMetrics ()->EmHeight ()
6375+ : GetSize ().height ;
63556376 bgRect = nsRect (nscoord (aParams.framePt .x + offs),
6356- nscoord (aParams.framePt .y ), nscoord (advance),
6357- GetSize ().height );
6377+ nscoord (aParams.framePt .y ), nscoord (advance), height);
63586378 }
63596379
63606380 LayoutDeviceRect selectionRect =
@@ -7158,7 +7178,7 @@ void nsTextFrame::DrawTextRunAndDecorations(
71587178 // we need to revert the scaling here.
71597179 gfxContextMatrixAutoSaveRestore scaledRestorer;
71607180 if (Style ()->IsTextCombined ()) {
7161- float scaleFactor = GetTextCombineScaleFactor ( this );
7181+ float scaleFactor = GetTextCombineScale ( );
71627182 if (scaleFactor != 1 .0f ) {
71637183 scaledRestorer.SetContext (aParams.context );
71647184 gfxMatrix unscaled = aParams.context ->CurrentMatrixDouble ();
@@ -7414,7 +7434,7 @@ nsIFrame::ContentOffsets nsTextFrame::GetCharacterOffsetAtFramePointInternal(
74147434 ? (mTextRun ->IsInlineReversed () ? mRect .height - aPoint.y : aPoint.y )
74157435 : (mTextRun ->IsInlineReversed () ? mRect .width - aPoint.x : aPoint.x );
74167436 if (Style ()->IsTextCombined ()) {
7417- width /= GetTextCombineScaleFactor ( this );
7437+ width /= GetTextCombineScale ( );
74187438 }
74197439 gfxFloat fitWidth;
74207440 Range skippedRange = ComputeTransformedRange (provider);
@@ -7722,7 +7742,7 @@ nsPoint nsTextFrame::GetPointFromIterator(const gfxSkipCharsIterator& aIter,
77227742 } else {
77237743 point.y = 0 ;
77247744 if (Style ()->IsTextCombined ()) {
7725- iSize *= GetTextCombineScaleFactor ( this );
7745+ iSize *= GetTextCombineScale ( );
77267746 }
77277747 if (mTextRun ->IsInlineReversed ()) {
77287748 point.x = mRect .width - iSize;
@@ -7851,7 +7871,7 @@ nsresult nsTextFrame::GetCharacterRectsInRange(int32_t aInOffset,
78517871 if (Style ()->IsTextCombined ()) {
78527872 // The scale factor applies to the inline advance of the glyphs, so it
78537873 // affects both the rect width and the origin point for the next glyph.
7854- iSize *= GetTextCombineScaleFactor ( this );
7874+ iSize *= GetTextCombineScale ( );
78557875 }
78567876 rect.width = iSize;
78577877 rect.height = mRect .height ;
@@ -8883,12 +8903,15 @@ void nsTextFrame::AddInlineMinISizeForFlow(gfxContext* aRenderingContext,
88838903 uint32_t start = FindStartAfterSkippingWhitespace (&provider, aData, textStyle,
88848904 &iter, flowEndInTextRun);
88858905
8886- // text-combine-upright frame is constantly 1em on inline-axis.
8906+ // text-combine-upright frame is constantly 1em on inline-axis; but if it has
8907+ // a following sibling with the same style, it contributes nothing.
88878908 if (Style ()->IsTextCombined ()) {
88888909 if (start < flowEndInTextRun && textRun->CanBreakLineBefore (start)) {
88898910 aData->OptionallyBreak ();
88908911 }
8891- aData->mCurrentLine += provider.GetFontMetrics ()->EmHeight ();
8912+ if (!GetNextSibling () || GetNextSibling ()->Style () != Style ()) {
8913+ aData->mCurrentLine += provider.GetFontMetrics ()->EmHeight ();
8914+ }
88928915 aData->mTrailingWhitespace = 0 ;
88938916 return ;
88948917 }
@@ -9154,9 +9177,12 @@ void nsTextFrame::AddInlinePrefISizeForFlow(gfxContext* aRenderingContext,
91549177 PropertyProvider provider (textRun, textStyle, frag, this , iter, INT32_MAX,
91559178 nullptr , 0 , aTextRunType, aData->mLineIsEmpty );
91569179
9157- // text-combine-upright frame is constantly 1em on inline-axis.
9180+ // text-combine-upright frame is constantly 1em on inline-axis, or zero if
9181+ // there is a following sibling with the same style.
91589182 if (Style ()->IsTextCombined ()) {
9159- aData->mCurrentLine += provider.GetFontMetrics ()->EmHeight ();
9183+ if (!GetNextSibling () || GetNextSibling ()->Style () != Style ()) {
9184+ aData->mCurrentLine += provider.GetFontMetrics ()->EmHeight ();
9185+ }
91609186 aData->mTrailingWhitespace = 0 ;
91619187 aData->mLineIsEmpty = false ;
91629188 return ;
@@ -10159,30 +10185,88 @@ void nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
1015910185 nscoord width = finalSize.ISize (wm);
1016010186 nscoord em = fm->EmHeight ();
1016110187 // Compress the characters in horizontal axis if necessary.
10162- if (width <= em) {
10163- RemoveProperty (TextCombineScaleFactorProperty ());
10164- } else {
10165- SetProperty (TextCombineScaleFactorProperty (),
10166- static_cast <float >(em) / static_cast <float >(width));
10167- finalSize.ISize (wm) = em;
10168- }
10169- // Make the characters be in an 1em square.
10188+ auto * data = GetProperty (TextCombineDataProperty ());
10189+ if (!data) {
10190+ data = new TextCombineData;
10191+ SetProperty (TextCombineDataProperty (), data);
10192+ }
10193+ data->mNaturalWidth = width;
10194+ finalSize.ISize (wm) = em;
10195+ // If we're going to have to adjust the block-size to make it 1em, make an
10196+ // appropriate adjustment to the font baseline (determined by the ascent
10197+ // returned in aMetrics)
1017010198 if (finalSize.BSize (wm) != em) {
1017110199 fontBaseline =
1017210200 aMetrics.BlockStartAscent () + (em - finalSize.BSize (wm)) / 2 ;
1017310201 aMetrics.SetBlockStartAscent (fontBaseline);
10202+ }
10203+ // If we have a next-in-flow with the same style, remove our block-size
10204+ // so that they end up beside each other; only the last in the series
10205+ // gets to keep its block-axis size.
10206+ if (GetNextSibling () && GetNextSibling ()->Style () == Style ()) {
10207+ finalSize.BSize (wm) = 0 ;
10208+ } else {
10209+ // Make the characters be in an 1em square.
1017410210 finalSize.BSize (wm) = em;
1017510211 }
10212+ // If there are earlier sibling frames with the same style, and we have
10213+ // reached the end of the run of same-style siblings, recompute the scale
10214+ // as necessary to make them all fit. (This is a bit inefficient, but is
10215+ // such a rare case that we don't much care.)
10216+ nsIFrame* f = GetPrevSibling ();
10217+ if (f && f->Style () == Style () &&
10218+ (!GetNextSibling () || GetNextSibling ()->Style () != Style ())) {
10219+ // Collect the total "natural" width of the text.
10220+ while (f->Style () == Style ()) {
10221+ if (auto * fData = f->GetProperty (TextCombineDataProperty ())) {
10222+ width += fData ->mNaturalWidth ;
10223+ }
10224+ if (!f->GetPrevSibling ()) {
10225+ break ;
10226+ }
10227+ f = f->GetPrevSibling ();
10228+ }
10229+ } else {
10230+ // Not at the end of a run of frames; we're just going to handle `this`.
10231+ f = this ;
10232+ }
10233+ // Figure out scaling to apply to this frame (or to the run of same-style
10234+ // sibling frames), and the starting offset within the em square.
10235+ float scale;
10236+ nscoord offset;
10237+ if (width > em) {
10238+ scale = static_cast <float >(em) / width;
10239+ offset = 0 ;
10240+ } else {
10241+ scale = 1 .0f ;
10242+ offset = (em - width) / 2 ;
10243+ }
10244+ while (true ) {
10245+ if (auto * fData = f->GetProperty (TextCombineDataProperty ())) {
10246+ fData ->mScale = scale;
10247+ fData ->mOffset = offset;
10248+ offset += fData ->mNaturalWidth * scale;
10249+ }
10250+ if (f == this ) {
10251+ break ;
10252+ }
10253+ f = f->GetNextSibling ();
10254+ }
1017610255 }
1017710256 aMetrics.SetSize (wm, finalSize);
1017810257
1017910258 NS_ASSERTION (aMetrics.BlockStartAscent() >= 0, "Negative ascent???");
10180- NS_ASSERTION (
10259+ // The effective "descent" here will be negative in the case of a frame that
10260+ // is participating in text-combine-upright, but is not the last frame within
10261+ // the combined upright cell, because we zero out its block-size (see above).
10262+ // So this creates an exception to the non-negative assertion here.
10263+ DebugOnly<nscoord> descent =
1018110264 (Style ()->IsTextCombined () ? aMetrics.ISize (aMetrics.GetWritingMode ())
1018210265 : aMetrics.BSize (aMetrics.GetWritingMode ())) -
10183- aMetrics.BlockStartAscent() >=
10184- 0,
10185- "Negative descent???");
10266+ aMetrics.BlockStartAscent ();
10267+ NS_ASSERTION (descent >= 0 || (Style()->IsTextCombined() && GetNextSibling() &&
10268+ GetNextSibling()->Style() == Style()),
10269+ "Unexpected negative descent???");
1018610270
1018710271 mAscent = fontBaseline;
1018810272
0 commit comments