Bug 1310106 - sink initial letter to achieve a dropped initial (or drop cap). draft
authorJeremy Chen <jeremychen@mozilla.com>
Fri, 04 Nov 2016 14:41:04 +0800
changeset 433756 9bc3fc814749e7dfc7f6e74c36934ecef48c6c59
parent 433723 4f09d9469e73adf32c7db6720504fcbe580516b3
child 433757 8ff2397308f5429d6fe7951cab1a29947df69d90
push id34639
push userjichen@mozilla.com
push dateFri, 04 Nov 2016 06:42:08 +0000
bugs1310106
milestone52.0a1
Bug 1310106 - sink initial letter to achieve a dropped initial (or drop cap). MozReview-Commit-ID: 5FkMqS46PKW
layout/generic/nsFirstLetterFrame.cpp
--- a/layout/generic/nsFirstLetterFrame.cpp
+++ b/layout/generic/nsFirstLetterFrame.cpp
@@ -184,16 +184,17 @@ nsFirstLetterFrame::Reflow(nsPresContext
                "should no longer use unconstrained inline size");
   availSize.ISize(wm) -= bp.IStartEnd(wm);
   if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) {
     availSize.BSize(wm) -= bp.BStartEnd(wm);
   }
 
   WritingMode lineWM = aMetrics.GetWritingMode();
   ReflowOutput kidMetrics(lineWM);
+  const nsStyleTextReset* styleTR = mStyleContext->StyleTextReset();
 
   // Reflow the child
   if (!aReflowInput.mLineLayout) {
     // When there is no lineLayout provided, we provide our own. The
     // only time that the first-letter-frame is not reflowing in a
     // line context is when its floating.
     WritingMode kidWritingMode = GetWritingMode(kid);
     LogicalSize kidAvailSize = availSize.ConvertTo(kidWritingMode, wm);
@@ -209,32 +210,72 @@ nsFirstLetterFrame::Reflow(nsPresContext
     ll.SetInFirstLetter(true);
     ll.SetFirstLetterStyleOK(true);
 
     kid->Reflow(aPresContext, kidMetrics, rs, aReflowStatus);
 
     ll.EndLineReflow();
     ll.SetInFirstLetter(false);
 
+    // CSS Inline Layout Level 3 - 3.3 Creating Initial Letters
+    // According to the specification, the initial letter's baseline should
+    // sink to the Nth line, where N is the sink value of the initial-letter
+    // property. To achieve the alignment, we can either adjust the initial
+    // letter's baseline, or move the rest lines of texts. The point is that we
+    // should align the top of the block container with, either the top of the
+    // initial letter, or the top of the first line. So, if moving the rest
+    // lines of texts would break this rule, we should adjust the initial
+    // letter's baseline instead.
+    // Here, the initial letter's baseline, i.e., initialBaseline, and the Nth
+    // line's baseline, i.e., targetBaseline, are both evaluated. If
+    // targetBaseline > initialBaseline, we should adjust initialBaseline
+    // downwards to be aligned with targetBaseline. As to the logic for moving
+    // the rest lines of texts, please see nsLineLayout::ReflowFrame().
+    nscoord offset = 0;
+    if (styleTR->mInitialLetterSink != 0) {
+      nsStyleContext* containerSC = mStyleContext->GetParent();
+      const nsStyleDisplay* containerDisp = containerSC->StyleDisplay();
+      while (containerDisp->mDisplay == mozilla::StyleDisplay::Contents) {
+        if (!containerSC->GetParent()) {
+          break;
+        }
+        containerSC = containerSC->GetParent();
+        containerDisp = containerSC->StyleDisplay();
+      }
+      nscoord containerLH =
+        ReflowInput::CalcLineHeight(nullptr, containerSC, NS_AUTOHEIGHT, 1.0f);
+      RefPtr<nsFontMetrics> containerFM =
+        nsLayoutUtils::GetFontMetricsForStyleContext(containerSC);
+      MOZ_ASSERT(containerFM, "Should have fontMetrics!!");
+      nscoord topLeading = (containerLH - containerFM->MaxHeight()) / 2;
+      nscoord lineAscent = topLeading + containerFM->MaxAscent();
+
+      nscoord targetBaseline =
+        (styleTR->mInitialLetterSink - 1) * containerLH + lineAscent;
+      nscoord initialBaseline = kidMetrics.BlockStartAscent();
+      if (targetBaseline > initialBaseline) {
+        offset = targetBaseline - initialBaseline;
+      }
+    }
     // In the floating first-letter case, we need to set this ourselves;
     // nsLineLayout::BeginSpan will set it in the other case
-    mBaseline = kidMetrics.BlockStartAscent();
+    mBaseline = kidMetrics.BlockStartAscent() + offset;
 
     // Place and size the child and update the output metrics
     LogicalSize convertedSize = kidMetrics.Size(lineWM).ConvertTo(wm, lineWM);
-    kid->SetRect(nsRect(bp.IStart(wm), bp.BStart(wm),
+    kid->SetRect(nsRect(bp.IStart(wm), bp.BStart(wm) + offset,
                         convertedSize.ISize(wm), convertedSize.BSize(wm)));
     kid->FinishAndStoreOverflow(&kidMetrics);
     kid->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED);
 
     convertedSize.ISize(wm) += bp.IStartEnd(wm);
-    convertedSize.BSize(wm) += bp.BStartEnd(wm);
+    convertedSize.BSize(wm) += bp.BStartEnd(wm) + offset;
     aMetrics.SetSize(wm, convertedSize);
     aMetrics.SetBlockStartAscent(kidMetrics.BlockStartAscent() +
-                                 bp.BStart(wm));
+                                 bp.BStart(wm) + offset);
 
     // Ensure that the overflow rect contains the child textframe's
     // overflow rect.
     // Note that if this is floating, the overline/underline drawable
     // area is in the overflow rect of the child textframe.
     aMetrics.UnionOverflowAreasWithDesiredBounds();
     ConsiderChildOverflow(aMetrics.mOverflowAreas, kid);
 
@@ -251,17 +292,17 @@ nsFirstLetterFrame::Reflow(nsPresContext
     ll->ReflowFrame(kid, aReflowStatus, &kidMetrics, pushedFrame);
     NS_ASSERTION(lineWM.IsVertical() == wm.IsVertical(),
                  "we're assuming we can mix sizes between lineWM and wm "
                  "since we shouldn't have orthogonal writing modes within "
                  "a line.");
     aMetrics.ISize(lineWM) = ll->EndSpan(this) + bp.IStartEnd(wm);
     ll->SetInFirstLetter(false);
 
-    if (mStyleContext->StyleTextReset()->mInitialLetterSize != 0.0f) {
+    if (styleTR->mInitialLetterSize != 0.0f) {
       aMetrics.SetBlockStartAscent(kidMetrics.BlockStartAscent() +
                                    bp.BStart(wm));
       aMetrics.BSize(lineWM) = kidMetrics.BSize(lineWM) + bp.BStartEnd(wm);
     } else {
       nsLayoutUtils::SetBSizeFromFontMetrics(this, aMetrics, bp, lineWM, wm);
     }
   }