Bug 978044: Center print-previewed pages inside the viewport. r=mats
authorDaniel Holbert <dholbert@cs.stanford.edu>
Tue, 07 Oct 2014 18:37:50 -0700
changeset 232470 04c92cdfcde4efe80f94d70928c852c9e03b87d0
parent 232469 3955cb15faf74718746fb36bc20d78ae5164107d
child 232471 1a860977f8c7c33f4d7b01d9b80606d990224d33
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmats
bugs978044
milestone35.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 978044: Center print-previewed pages inside the viewport. r=mats
layout/generic/nsSimplePageSequenceFrame.cpp
layout/generic/nsSimplePageSequenceFrame.h
--- a/layout/generic/nsSimplePageSequenceFrame.cpp
+++ b/layout/generic/nsSimplePageSequenceFrame.cpp
@@ -112,16 +112,53 @@ nsSimplePageSequenceFrame::SetDesiredSiz
     // in child page frames correctly.
     // Use availableWidth so we don't cause a needless horizontal scrollbar.
     aDesiredSize.Width() = std::max(aReflowState.AvailableWidth(),
                                 nscoord(aWidth * PresContext()->GetPrintPreviewScale()));
     aDesiredSize.Height() = std::max(aReflowState.ComputedHeight(),
                                  nscoord(aHeight * PresContext()->GetPrintPreviewScale()));
 }
 
+// Helper function to compute the offset needed to center a child
+// page-frame's margin-box inside our content-box.
+nscoord
+nsSimplePageSequenceFrame::ComputeCenteringMargin(
+  nscoord aContainerContentBoxWidth,
+  nscoord aChildPaddingBoxWidth,
+  const nsMargin& aChildPhysicalMargin)
+{
+  // We'll be centering our child's margin-box, so get the size of that:
+  nscoord childMarginBoxWidth =
+    aChildPaddingBoxWidth + aChildPhysicalMargin.LeftRight();
+
+  // When rendered, our child's rect will actually be scaled up by the
+  // print-preview scale factor, via ComputePageSequenceTransform().
+  // We really want to center *that scaled-up rendering* inside of
+  // aContainerContentBoxWidth.  So, we scale up its margin-box here...
+  auto ppScale = PresContext()->GetPrintPreviewScale();
+  nscoord scaledChildMarginBoxWidth =
+    NSToCoordRound(childMarginBoxWidth * ppScale);
+
+  // ...and see we how much space is left over, when we subtract that scaled-up
+  // size from the container width:
+  nscoord scaledExtraSpace =
+    aContainerContentBoxWidth - scaledChildMarginBoxWidth;
+
+  if (scaledExtraSpace <= 0) {
+    // (Don't bother centering if there's zero/negative space.)
+    return 0;
+  }
+
+  // To center the child, we want to give it an additional left-margin of half
+  // of the extra space.  And then, we have to scale that space back down, so
+  // that it'll produce the correct scaled-up amount when we render (because
+  // rendering will scale it back up):
+  return NSToCoordRound(scaledExtraSpace * 0.5 / ppScale);
+}
+
 void
 nsSimplePageSequenceFrame::Reflow(nsPresContext*          aPresContext,
                                   nsHTMLReflowMetrics&     aDesiredSize,
                                   const nsHTMLReflowState& aReflowState,
                                   nsReflowStatus&          aStatus)
 {
   NS_PRECONDITION(aPresContext->IsRootPaginatedDocument(),
                   "A Page Sequence is only for real pages");
@@ -133,16 +170,32 @@ nsSimplePageSequenceFrame::Reflow(nsPres
 
   // Don't do incremental reflow until we've taught tables how to do
   // it right in paginated mode.
   if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     // Return our desired size
     SetDesiredSize(aDesiredSize, aReflowState, mSize.width, mSize.height);
     aDesiredSize.SetOverflowAreasToDesiredBounds();
     FinishAndStoreOverflow(&aDesiredSize);
+
+    if (GetRect().Width() != aDesiredSize.Width()) {
+      // Our width is changing; we need to re-center our children (our pages).
+      for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
+        nsIFrame* child = e.get();
+        nsMargin pageCSSMargin = child->GetUsedMargin();
+        nscoord centeringMargin =
+          ComputeCenteringMargin(aReflowState.ComputedWidth(),
+                                 child->GetRect().width,
+                                 pageCSSMargin);
+        nscoord newX = pageCSSMargin.left + centeringMargin;
+
+        // Adjust the child's x-position:
+        child->MovePositionBy(nsPoint(newX - child->GetNormalPosition().x, 0));
+      }
+    }
     return;
   }
 
   // See if we can get a Print Settings from the Context
   if (!mPageData->mPrintSettings &&
       aPresContext->Medium() == nsGkAtoms::print) {
       mPageData->mPrintSettings = aPresContext->GetPrintSettings();
   }
@@ -214,22 +267,26 @@ nsSimplePageSequenceFrame::Reflow(nsPres
     nsReflowStatus  status;
 
     kidReflowState.SetComputedWidth(kidReflowState.AvailableWidth());
     //kidReflowState.SetComputedHeight(kidReflowState.AvailableHeight());
     PR_PL(("AV W: %d   H: %d\n", kidReflowState.AvailableWidth(), kidReflowState.AvailableHeight()));
 
     nsMargin pageCSSMargin = kidReflowState.ComputedPhysicalMargin();
     y += pageCSSMargin.top;
-    const nscoord x = pageCSSMargin.left;
+
+    nscoord x = pageCSSMargin.left;
 
-    // Place and size the page. If the page is narrower than our
-    // max width then center it horizontally
+    // Place and size the page.
     ReflowChild(kidFrame, aPresContext, kidSize, kidReflowState, x, y, 0, status);
 
+    // If the page is narrower than our width, then center it horizontally:
+    x += ComputeCenteringMargin(aReflowState.ComputedWidth(),
+                                kidSize.Width(), pageCSSMargin);
+
     FinishReflowChild(kidFrame, aPresContext, kidSize, nullptr, x, y, 0);
     y += kidSize.Height();
     y += pageCSSMargin.bottom;
 
     maxXMost = std::max(maxXMost, x + kidSize.Width() + pageCSSMargin.right);
 
     // Is the page complete?
     nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow();
@@ -758,17 +815,17 @@ nsSimplePageSequenceFrame::DoPageEnd()
 
   ResetPrintCanvasList();
 
   mPageNum++;
   
   return rv;
 }
 
-static gfx::Matrix4x4
+inline gfx::Matrix4x4
 ComputePageSequenceTransform(nsIFrame* aFrame, float aAppUnitsPerPixel)
 {
   float scale = aFrame->PresContext()->GetPrintPreviewScale();
   return gfx::Matrix4x4().Scale(scale, scale, 1);
 }
 
 void
 nsSimplePageSequenceFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
--- a/layout/generic/nsSimplePageSequenceFrame.h
+++ b/layout/generic/nsSimplePageSequenceFrame.h
@@ -124,16 +124,23 @@ protected:
   void SetPageNumberFormat(const nsAString& aFormatStr, bool aForPageNumOnly);
 
   // Sets the frame desired size to the size of the viewport, or the given
   // nscoords, whichever is larger. Print scaling is applied in this function.
   void SetDesiredSize(nsHTMLReflowMetrics& aDesiredSize,
                       const nsHTMLReflowState& aReflowState,
                       nscoord aWidth, nscoord aHeight);
 
+  // Helper function to compute the offset needed to center a child
+  // page-frame's margin-box inside our content-box.
+  nscoord ComputeCenteringMargin(nscoord aContainerContentBoxWidth,
+                                 nscoord aChildPaddingBoxWidth,
+                                 const nsMargin& aChildPhysicalMargin);
+
+
   void DetermineWhetherToPrintPage();
   nsIFrame* GetCurrentPageFrame();
 
   nsMargin mMargin;
 
   // I18N date formatter service which we'll want to cache locally.
   nsCOMPtr<nsIDateTimeFormat> mDateFormatter;