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 209255 04c92cdfcde4efe80f94d70928c852c9e03b87d0
parent 209254 3955cb15faf74718746fb36bc20d78ae5164107d
child 209256 1a860977f8c7c33f4d7b01d9b80606d990224d33
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersmats
bugs978044
milestone35.0a1
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;