Bug 221706 - Use unwritable region when printing on Windows. r=jimm
authorBrendan Dahl <bdahl@mozilla.com>
Wed, 28 Mar 2018 17:13:10 -0700
changeset 417822 29e58ed3f991b1887d743d49237b65b51db15c5d
parent 417821 33ae17f18193912b12e7a890175a4d8749d75d9a
child 417823 e1754110f8a687420e9196b7f6166129631335c9
push id33980
push userebalazs@mozilla.com
push dateFri, 11 May 2018 09:35:12 +0000
treeherdermozilla-central@8e9a4a323f0c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs221706
milestone62.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 221706 - Use unwritable region when printing on Windows. r=jimm Read more information from the printing device to setup the unwritable region. Translate the printing context's coordinate system so that the point (0,0) refers to the top-left of the physical paper instead of the top-left of the printable region. MozReview-Commit-ID: 9ei2FgEUDyO
gfx/src/nsDeviceContext.cpp
gfx/src/nsDeviceContext.h
gfx/thebes/PrintTargetWindows.cpp
widget/nsDeviceContextSpecProxy.cpp
widget/nsDeviceContextSpecProxy.h
widget/nsIDeviceContextSpec.h
widget/windows/nsDeviceContextSpecWin.cpp
widget/windows/nsDeviceContextSpecWin.h
widget/windows/nsPrintSettingsWin.cpp
widget/windows/nsPrintSettingsWin.h
--- a/gfx/src/nsDeviceContext.cpp
+++ b/gfx/src/nsDeviceContext.cpp
@@ -199,16 +199,17 @@ nsFontCache::Flush()
     mFontMetrics.Clear();
 }
 
 nsDeviceContext::nsDeviceContext()
     : mWidth(0), mHeight(0),
       mAppUnitsPerDevPixel(-1), mAppUnitsPerDevPixelAtUnitFullZoom(-1),
       mAppUnitsPerPhysicalInch(-1),
       mFullZoom(1.0f), mPrintingScale(1.0f),
+      mPrintingTranslate(gfxPoint(0, 0)),
       mIsCurrentlyPrintingDoc(false)
 #ifdef DEBUG
     , mIsInitialized(false)
 #endif
 {
     MOZ_ASSERT(NS_IsMainThread(), "nsDeviceContext created off main thread");
 }
 
@@ -271,16 +272,17 @@ void
 nsDeviceContext::SetDPI(double* aScale)
 {
     float dpi = -1.0f;
 
     // Use the printing DC to determine DPI values, if we have one.
     if (mDeviceContextSpec) {
         dpi = mDeviceContextSpec->GetDPI();
         mPrintingScale = mDeviceContextSpec->GetPrintingScale();
+        mPrintingTranslate = mDeviceContextSpec->GetPrintingTranslate();
         mAppUnitsPerDevPixelAtUnitFullZoom =
             NS_lround((AppUnitsPerCSSPixel() * 96) / dpi);
     } else {
         nsCOMPtr<nsIScreen> primaryScreen;
         ScreenManager& screenManager = ScreenManager::GetSingleton();
         screenManager.GetPrimaryScreen(getter_AddRefs(primaryScreen));
         MOZ_ASSERT(primaryScreen);
 
@@ -409,16 +411,17 @@ nsDeviceContext::CreateRenderingContextC
     dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr);
 #endif
     dt->AddUserData(&sDisablePixelSnapping, (void*)0x1, nullptr);
 
     RefPtr<gfxContext> pContext = gfxContext::CreateOrNull(dt);
     MOZ_ASSERT(pContext); // already checked draw target above
 
     gfxMatrix transform;
+    transform.PreTranslate(mPrintingTranslate);
     if (mPrintTarget->RotateNeededForLandscape()) {
       // Rotate page 90 degrees to draw landscape page on portrait paper
       IntSize size = mPrintTarget->GetSize();
       transform.PreTranslate(gfxPoint(0, size.width));
       gfxMatrix rotate(0, -1,
                        1,  0,
                        0,  0);
       transform = rotate * transform;
--- a/gfx/src/nsDeviceContext.h
+++ b/gfx/src/nsDeviceContext.h
@@ -300,16 +300,17 @@ private:
 
     nscoord  mWidth;
     nscoord  mHeight;
     int32_t  mAppUnitsPerDevPixel;
     int32_t  mAppUnitsPerDevPixelAtUnitFullZoom;
     int32_t  mAppUnitsPerPhysicalInch;
     float    mFullZoom;
     float    mPrintingScale;
+    gfxPoint  mPrintingTranslate;
 
     RefPtr<nsFontCache>            mFontCache;
     nsCOMPtr<nsIWidget>            mWidget;
     nsCOMPtr<nsIScreenManager>     mScreenManager;
     nsCOMPtr<nsIDeviceContextSpec> mDeviceContextSpec;
     RefPtr<PrintTarget>            mPrintTarget;
     bool                           mIsCurrentlyPrintingDoc;
 #ifdef DEBUG
--- a/gfx/thebes/PrintTargetWindows.cpp
+++ b/gfx/thebes/PrintTargetWindows.cpp
@@ -21,24 +21,26 @@ PrintTargetWindows::PrintTargetWindows(c
 {
   // TODO: At least add basic memory reporting.
   // 4 * mSize.width * mSize.height + sizeof(PrintTargetWindows) ?
 }
 
 /* static */ already_AddRefed<PrintTargetWindows>
 PrintTargetWindows::CreateOrNull(HDC aDC)
 {
-  // Figure out the cairo surface size - Windows we need to use the printable
-  // area of the page.  Note: we only scale the printing using the LOGPIXELSY,
+  // Figure out the paper size, the actual surface size will be the printable
+  // area which is likely smaller, but the size here is later used to create the
+  // draw target where the full page size is needed.
+  // Note: we only scale the printing using the LOGPIXELSY,
   // so we use that when calculating the surface width as well as the height.
   int32_t heightDPI = ::GetDeviceCaps(aDC, LOGPIXELSY);
   float width =
-    (::GetDeviceCaps(aDC, HORZRES) * POINTS_PER_INCH_FLOAT) / heightDPI;
+    (::GetDeviceCaps(aDC, PHYSICALWIDTH) * POINTS_PER_INCH_FLOAT) / heightDPI;
   float height =
-    (::GetDeviceCaps(aDC, VERTRES) * POINTS_PER_INCH_FLOAT) / heightDPI;
+    (::GetDeviceCaps(aDC, PHYSICALHEIGHT) * POINTS_PER_INCH_FLOAT) / heightDPI;
   IntSize size = IntSize::Truncate(width, height);
 
   if (!Factory::CheckSurfaceSize(size)) {
     return nullptr;
   }
 
   cairo_surface_t* surface = cairo_win32_printing_surface_create(aDC);
 
--- a/widget/nsDeviceContextSpecProxy.cpp
+++ b/widget/nsDeviceContextSpecProxy.cpp
@@ -128,16 +128,24 @@ nsDeviceContextSpecProxy::GetDPI()
 float
 nsDeviceContextSpecProxy::GetPrintingScale()
 {
   MOZ_ASSERT(mRealDeviceContextSpec);
 
   return mRealDeviceContextSpec->GetPrintingScale();
 }
 
+gfxPoint
+nsDeviceContextSpecProxy::GetPrintingTranslate()
+{
+  MOZ_ASSERT(mRealDeviceContextSpec);
+
+  return mRealDeviceContextSpec->GetPrintingTranslate();
+}
+
 NS_IMETHODIMP
 nsDeviceContextSpecProxy::BeginDocument(const nsAString& aTitle,
                                         const nsAString& aPrintToFileName,
                                         int32_t aStartPage, int32_t aEndPage)
 {
   mRecorder = new mozilla::layout::DrawEventRecorderPRFileDesc();
   nsresult rv = mRemotePrintJob->InitializePrint(nsString(aTitle),
                                                  nsString(aPrintToFileName),
--- a/widget/nsDeviceContextSpecProxy.h
+++ b/widget/nsDeviceContextSpecProxy.h
@@ -33,16 +33,18 @@ public:
   already_AddRefed<PrintTarget> MakePrintTarget() final;
 
   NS_IMETHOD GetDrawEventRecorder(mozilla::gfx::DrawEventRecorder** aDrawEventRecorder) final;
 
   float GetDPI() final;
 
   float GetPrintingScale() final;
 
+  gfxPoint GetPrintingTranslate() final;
+
 
   NS_IMETHOD BeginDocument(const nsAString& aTitle,
                            const nsAString& aPrintToFileName,
                            int32_t aStartPage, int32_t aEndPage) final;
 
   NS_IMETHOD EndDocument() final;
 
   NS_IMETHOD AbortDocument() final;
--- a/widget/nsIDeviceContextSpec.h
+++ b/widget/nsIDeviceContextSpec.h
@@ -64,16 +64,23 @@ public:
 
    /**
     * Override to return something other than the default.
     *
     * @return the printing scale to be applied to the context for printing.
     */
    virtual float GetPrintingScale() { return 1.0f;  }
 
+   /**
+    * Override to return something other than the default.
+    *
+    * @return the point to translate the context to for printing.
+    */
+   virtual gfxPoint GetPrintingTranslate() { return gfxPoint(0, 0);  }
+
    NS_IMETHOD BeginDocument(const nsAString& aTitle,
                             const nsAString& aPrintToFileName,
                             int32_t          aStartPage,
                             int32_t          aEndPage) = 0;
 
    NS_IMETHOD EndDocument() = 0;
    NS_IMETHOD AbortDocument() { return EndDocument(); }
    NS_IMETHOD BeginPage() = 0;
--- a/widget/windows/nsDeviceContextSpecWin.cpp
+++ b/widget/windows/nsDeviceContextSpecWin.cpp
@@ -316,16 +316,32 @@ nsDeviceContextSpecWin::GetPrintingScale
   }
 
   // The print settings will have the resolution stored from the real device.
   int32_t resolution;
   mPrintSettings->GetResolution(&resolution);
   return float(resolution) / GetDPI();
 }
 
+gfxPoint
+nsDeviceContextSpecWin::GetPrintingTranslate()
+{
+  // The underlying surface on windows is the size of the printable region. When
+  // the region is smaller than the actual paper size the (0, 0) coordinate
+  // refers top-left of that unwritable region. To instead have (0, 0) become
+  // the top-left of the actual paper, translate it's coordinate system by the
+  // unprintable region's width.
+  double marginTop, marginLeft;
+  mPrintSettings->GetUnwriteableMarginTop(&marginTop);
+  mPrintSettings->GetUnwriteableMarginLeft(&marginLeft);
+  int32_t resolution;
+  mPrintSettings->GetResolution(&resolution);
+  return gfxPoint(-marginLeft * resolution, -marginTop * resolution);
+}
+
 //----------------------------------------------------------------------------------
 void nsDeviceContextSpecWin::SetDeviceName(const nsAString& aDeviceName)
 {
   mDeviceName = aDeviceName;
 }
 
 //----------------------------------------------------------------------------------
 void nsDeviceContextSpecWin::SetDriverName(const nsAString& aDriverName)
--- a/widget/windows/nsDeviceContextSpecWin.h
+++ b/widget/windows/nsDeviceContextSpecWin.h
@@ -33,16 +33,17 @@ public:
   NS_IMETHOD BeginPage() override { return NS_OK; }
   NS_IMETHOD EndPage() override { return NS_OK; }
 
   NS_IMETHOD Init(nsIWidget* aWidget, nsIPrintSettings* aPS, bool aIsPrintPreview) override;
 
   float GetDPI() final;
 
   float GetPrintingScale() final;
+  gfxPoint GetPrintingTranslate() final;
 
   void GetDriverName(nsAString& aDriverName) const { aDriverName = mDriverName; }
   void GetDeviceName(nsAString& aDeviceName) const { aDeviceName = mDeviceName; }
 
   // The GetDevMode will return a pointer to a DevMode
   // whether it is from the Global memory handle or just the DevMode
   // To get the DevMode from the Global memory Handle it must lock it 
   // So this call must be paired with a call to UnlockGlobalHandle
--- a/widget/windows/nsPrintSettingsWin.cpp
+++ b/widget/windows/nsPrintSettingsWin.cpp
@@ -271,16 +271,51 @@ nsPrintSettingsWin::GetEffectivePageSize
   } else {
     *aHeight = NS_INCHES_TO_TWIPS(mPrintableWidthInInches);
     *aWidth = NS_INCHES_TO_TWIPS(mPrintableHeightInInches);
   }
   return NS_OK;
 }
 
 void
+nsPrintSettingsWin::InitUnwriteableMargin(HDC aHdc)
+{
+  int32_t pixelsPerInchY = GetDeviceCaps(aHdc, LOGPIXELSY);
+  int32_t pixelsPerInchX = GetDeviceCaps(aHdc, LOGPIXELSX);
+
+  int32_t marginLeft = GetDeviceCaps(aHdc, PHYSICALOFFSETX);
+  int32_t marginTop  = GetDeviceCaps(aHdc, PHYSICALOFFSETY);
+
+  double marginLeftInch = double(marginLeft) / pixelsPerInchX;
+  double marginTopInch  = double(marginTop) / pixelsPerInchY;
+
+  int32_t printableAreaWidth = GetDeviceCaps(aHdc, HORZRES);
+  int32_t printableAreaHeight  = GetDeviceCaps(aHdc, VERTRES);
+
+  double printableAreaWidthInch = double(printableAreaWidth) / pixelsPerInchX;
+  double printableAreaHeightInch = double(printableAreaHeight) / pixelsPerInchY;
+
+  int32_t physicalWidth = GetDeviceCaps(aHdc, PHYSICALWIDTH);
+  int32_t physicalHeight = GetDeviceCaps(aHdc, PHYSICALHEIGHT);
+
+  double physicalWidthInch = double(physicalWidth) / pixelsPerInchX;
+  double physicalHeightInch = double(physicalHeight) / pixelsPerInchY;
+
+  double marginBottomInch = physicalHeightInch - printableAreaHeightInch - marginTopInch;
+  double marginRightInch = physicalWidthInch - printableAreaWidthInch - marginLeftInch;
+
+  mUnwriteableMargin.SizeTo(
+     NS_INCHES_TO_INT_TWIPS(marginTopInch),
+     NS_INCHES_TO_INT_TWIPS(marginRightInch),
+     NS_INCHES_TO_INT_TWIPS(marginBottomInch),
+     NS_INCHES_TO_INT_TWIPS(marginLeftInch)
+  );
+}
+
+void
 nsPrintSettingsWin::CopyFromNative(HDC aHdc, DEVMODEW* aDevMode)
 {
   MOZ_ASSERT(aHdc);
   MOZ_ASSERT(aDevMode);
 
   mIsInitedFromPrinter = true;
   if (aDevMode->dmFields & DM_ORIENTATION) {
     mOrientation = int32_t(aDevMode->dmOrientation == DMORIENT_PORTRAIT
@@ -306,39 +341,37 @@ nsPrintSettingsWin::CopyFromNative(HDC a
     if (mPaperData > 0 &&
         mPaperData < int32_t(mozilla::ArrayLength(kPaperSizeUnits))) {
       mPaperSizeUnit = kPaperSizeUnits[mPaperData];
     }
   } else {
     mPaperData = -1;
   }
 
+  InitUnwriteableMargin(aHdc);
+
   // The length and width in DEVMODE are always in tenths of a millimeter.
   double sizeUnitToTenthsOfAmm =
     10L * (mPaperSizeUnit == kPaperSizeInches ? MM_PER_INCH_FLOAT : 1L);
   if (aDevMode->dmFields & DM_PAPERLENGTH) {
     mPaperHeight = aDevMode->dmPaperLength / sizeUnitToTenthsOfAmm;
   } else {
     mPaperHeight = -1l;
   }
 
   if (aDevMode->dmFields & DM_PAPERWIDTH) {
     mPaperWidth = aDevMode->dmPaperWidth / sizeUnitToTenthsOfAmm;
   } else {
     mPaperWidth = -1l;
   }
 
-  // On Windows we currently create a surface using the printable area of the
-  // page and don't set the unwriteable [sic] margins. Using the unwriteable
-  // margins doesn't appear to work on Windows, but I am not sure if this is a
-  // bug elsewhere in our code or a Windows quirk.
   // Note: we only scale the printing using the LOGPIXELSY, so we use that
   // when calculating the surface width as well as the height.
-  int32_t printableWidthInDots = GetDeviceCaps(aHdc, HORZRES);
-  int32_t printableHeightInDots = GetDeviceCaps(aHdc, VERTRES);
+  int32_t printableWidthInDots = GetDeviceCaps(aHdc, PHYSICALWIDTH);
+  int32_t printableHeightInDots = GetDeviceCaps(aHdc, PHYSICALHEIGHT);
   int32_t heightDPI = GetDeviceCaps(aHdc, LOGPIXELSY);
 
   // Keep these values in portrait format, so we can reflect our own changes
   // to mOrientation.
   if (mOrientation == kPortraitOrientation) {
     mPrintableWidthInInches = double(printableWidthInDots) / heightDPI;
     mPrintableHeightInInches = double(printableHeightInDots) / heightDPI;
   } else {
--- a/widget/windows/nsPrintSettingsWin.h
+++ b/widget/windows/nsPrintSettingsWin.h
@@ -41,16 +41,17 @@ public:
    * Assignment
    */
   nsPrintSettingsWin& operator=(const nsPrintSettingsWin& rhs);
 
   NS_IMETHOD GetEffectivePageSize(double *aWidth, double *aHeight) override;
 
 protected:
   void CopyDevMode(DEVMODEW* aInDevMode, DEVMODEW *& aOutDevMode);
+  void InitUnwriteableMargin(HDC aHdc);
 
   nsString      mDeviceName;
   nsString      mDriverName;
   LPDEVMODEW mDevMode;
   double mPrintableWidthInInches = 0l;
   double mPrintableHeightInInches = 0l;
 };