PrintTargetWindows.cpp (6135B)
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "PrintTargetWindows.h" 7 8 #include "cairo-win32.h" 9 #include "mozilla/gfx/HelpersCairo.h" 10 #include "mozilla/StaticPrefs_browser.h" 11 #include "mozilla/WidgetUtils.h" 12 #include "nsCoord.h" 13 #include "nsIContentAnalysis.h" 14 #include "nsIWidget.h" 15 #include "nsIWindowMediator.h" 16 #include "nsPIDOMWindow.h" 17 #include "nsServiceManagerUtils.h" 18 #include "nsString.h" 19 20 namespace mozilla { 21 namespace gfx { 22 23 PrintTargetWindows::PrintTargetWindows(cairo_surface_t* aCairoSurface, 24 const IntSize& aSize, HDC aDC) 25 : PrintTarget(aCairoSurface, aSize), mDC(aDC) { 26 // TODO: At least add basic memory reporting. 27 // 4 * mSize.width * mSize.height + sizeof(PrintTargetWindows) ? 28 } 29 30 /* static */ 31 already_AddRefed<PrintTargetWindows> PrintTargetWindows::CreateOrNull(HDC aDC) { 32 // Figure out the paper size, the actual surface size will be the printable 33 // area which is likely smaller, but the size here is later used to create the 34 // draw target where the full page size is needed. 35 // Note: we only scale the printing using the LOGPIXELSY, 36 // so we use that when calculating the surface width as well as the height. 37 int32_t heightDPI = ::GetDeviceCaps(aDC, LOGPIXELSY); 38 float width = 39 (::GetDeviceCaps(aDC, PHYSICALWIDTH) * POINTS_PER_INCH_FLOAT) / heightDPI; 40 float height = 41 (::GetDeviceCaps(aDC, PHYSICALHEIGHT) * POINTS_PER_INCH_FLOAT) / 42 heightDPI; 43 IntSize size = IntSize::Truncate(width, height); 44 45 if (!Factory::CheckSurfaceSize(size)) { 46 return nullptr; 47 } 48 49 // TODO(emilio): Use SkPDF rather than cairo if possible? See bug 1503537 and 50 // bug 2001909 for some EMF code which was supposed to deal with some of this. 51 cairo_surface_t* surface = cairo_win32_printing_surface_create(aDC); 52 53 if (cairo_surface_status(surface)) { 54 return nullptr; 55 } 56 57 // The new object takes ownership of our surface reference. 58 RefPtr<PrintTargetWindows> target = 59 new PrintTargetWindows(surface, size, aDC); 60 61 return target.forget(); 62 } 63 64 LazyLogModule gPrintingLog("printing"); 65 66 nsresult PrintTargetWindows::BeginPrinting(const nsAString& aTitle, 67 const nsAString& aPrintToFileName, 68 int32_t aStartPage, 69 int32_t aEndPage) { 70 const uint32_t DOC_TITLE_LENGTH = MAX_PATH - 1; 71 72 DOCINFOW docinfo; 73 74 nsString titleStr(aTitle); 75 if (titleStr.Length() > DOC_TITLE_LENGTH) { 76 titleStr.SetLength(DOC_TITLE_LENGTH - 3); 77 titleStr.AppendLiteral("..."); 78 } 79 80 nsString docName(aPrintToFileName); 81 docinfo.cbSize = sizeof(docinfo); 82 docinfo.lpszDocName = 83 titleStr.Length() > 0 ? titleStr.get() : L"Mozilla Document"; 84 docinfo.lpszOutput = docName.Length() > 0 ? docName.get() : nullptr; 85 docinfo.lpszDatatype = nullptr; 86 docinfo.fwType = 0; 87 88 // If we just did content analysis on this print request, it may have popped 89 // up a dialog, and this can prevent StartDocW() from working properly if the 90 // printer wants to pop up a dialog window (to get a file name to save to, for 91 // example). Setting the foreground window to any browser window seems to work 92 // around this. See bug 1980225. 93 if (nsIContentAnalysis::MightBeActive() && 94 StaticPrefs::browser_contentanalysis_print_set_foreground_window()) { 95 nsCOMPtr<nsIWindowMediator> winMediator = 96 do_GetService(NS_WINDOWMEDIATOR_CONTRACTID); 97 if (winMediator) { 98 nsCOMPtr<mozIDOMWindowProxy> domWindow; 99 nsresult rv = 100 winMediator->GetMostRecentBrowserWindow(getter_AddRefs(domWindow)); 101 if (NS_SUCCEEDED(rv) && domWindow) { 102 nsPIDOMWindowOuter* win = nsPIDOMWindowOuter::From(domWindow); 103 if (win) { 104 nsCOMPtr<nsIWidget> widget = 105 widget::WidgetUtils::DOMWindowToWidget(win); 106 if (widget) { 107 HWND hwnd = 108 static_cast<HWND>(widget->GetNativeData(NS_NATIVE_WINDOW)); 109 BOOL foregroundReturn = ::SetForegroundWindow(hwnd); 110 MOZ_LOG(gPrintingLog, mozilla::LogLevel::Debug, 111 ("Called SetForegroundWindow(), which returned %d", 112 foregroundReturn)); 113 } 114 } 115 } 116 } 117 } 118 // If the user selected Microsoft Print to PDF or XPS Document Printer, then 119 // the following StartDoc call will put up a dialog window to prompt the 120 // user to provide the name and location of the file to be saved. A zero or 121 // negative return value indicates failure. In that case we want to check 122 // whether that is because the user hit Cancel, since we want to treat that 123 // specially to avoid notifying the user that the print "failed" in that 124 // case. 125 // XXX We should perhaps introduce a new NS_ERROR_USER_CANCELLED errer. 126 int result = ::StartDocW(mDC, &docinfo); 127 if (result <= 0) { 128 DWORD lastError = ::GetLastError(); 129 if (lastError == ERROR_CANCELLED) { 130 return NS_ERROR_ABORT; 131 } 132 return NS_ERROR_FAILURE; 133 } 134 return NS_OK; 135 } 136 137 nsresult PrintTargetWindows::EndPrinting() { 138 int result = ::EndDoc(mDC); 139 return (result <= 0) ? NS_ERROR_FAILURE : NS_OK; 140 } 141 142 nsresult PrintTargetWindows::AbortPrinting() { 143 PrintTarget::AbortPrinting(); 144 int result = ::AbortDoc(mDC); 145 return (result <= 0) ? NS_ERROR_FAILURE : NS_OK; 146 } 147 148 nsresult PrintTargetWindows::BeginPage(const IntSize& aSizeInPoints) { 149 MOZ_ALWAYS_SUCCEEDS(PrintTarget::BeginPage(aSizeInPoints)); 150 int result = ::StartPage(mDC); 151 return (result <= 0) ? NS_ERROR_FAILURE : NS_OK; 152 } 153 154 nsresult PrintTargetWindows::EndPage() { 155 cairo_surface_show_page(mCairoSurface); 156 bool cairoFailure = cairo_surface_status(mCairoSurface); 157 MOZ_ALWAYS_SUCCEEDS(PrintTarget::EndPage()); 158 int result = ::EndPage(mDC); 159 return (result <= 0 || cairoFailure) ? NS_ERROR_FAILURE : NS_OK; 160 } 161 162 } // namespace gfx 163 } // namespace mozilla