1 /* -*- Mode: C++; tab-width: 2; 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/. */
6 #include "nsDeviceContextSpecWin.h"
8 #include "mozilla/ArrayUtils.h"
9 #include "mozilla/gfx/PrintPromise.h"
10 #include "mozilla/gfx/PrintTargetPDF.h"
11 #include "mozilla/gfx/PrintTargetWindows.h"
12 #include "mozilla/Logging.h"
13 #include "mozilla/Preferences.h"
14 #include "mozilla/RefPtr.h"
15 #include "mozilla/Telemetry.h"
16 #include "nsAnonymousTemporaryFile.h"
22 #include "nsIWidget.h"
25 #include "nsIPrintSettingsWin.h"
27 #include "nsComponentManagerUtils.h"
28 #include "nsPrinterWin.h"
29 #include "nsReadableUtils.h"
32 #include "gfxWindowsSurface.h"
34 #include "nsIFileStreams.h"
35 #include "nsWindowsHelpers.h"
37 #include "mozilla/gfx/Logging.h"
39 #ifdef MOZ_ENABLE_SKIA_PDF
40 # include "mozilla/gfx/PrintTargetSkPDF.h"
41 # include "mozilla/gfx/PrintTargetEMF.h"
42 # include "nsIUUIDGenerator.h"
43 # include "nsDirectoryServiceDefs.h"
44 # include "nsPrintfCString.h"
45 # include "nsThreadUtils.h"
48 extern mozilla::LazyLogModule gPrintingLog
;
49 #define PR_PL(_p1) MOZ_LOG(gPrintingLog, mozilla::LogLevel::Debug, _p1)
51 using namespace mozilla
;
52 using namespace mozilla::gfx
;
54 #ifdef MOZ_ENABLE_SKIA_PDF
55 using namespace mozilla::widget
;
58 static const wchar_t kDriverName
[] = L
"WINSPOOL";
60 //----------------------------------------------------------------------------------
63 //----------------------------------------------------------------------------------
64 nsDeviceContextSpecWin::nsDeviceContextSpecWin() = default;
66 //----------------------------------------------------------------------------------
68 NS_IMPL_ISUPPORTS(nsDeviceContextSpecWin
, nsIDeviceContextSpec
)
70 nsDeviceContextSpecWin::~nsDeviceContextSpecWin() {
74 mTempFile
->Remove(/* recursive = */ false);
77 if (nsCOMPtr
<nsIPrintSettingsWin
> ps
= do_QueryInterface(mPrintSettings
)) {
78 ps
->SetDeviceName(u
""_ns
);
79 ps
->SetDriverName(u
""_ns
);
80 ps
->SetDevMode(nullptr);
84 static bool GetDefaultPrinterName(nsAString
& aDefaultPrinterName
) {
86 GetDefaultPrinterW(nullptr, &length
);
89 aDefaultPrinterName
.SetLength(length
);
90 if (GetDefaultPrinterW((LPWSTR
)aDefaultPrinterName
.BeginWriting(),
92 // `length` includes the terminating null, so we subtract that from our
94 aDefaultPrinterName
.SetLength(length
- 1);
95 PR_PL(("DEFAULT PRINTER [%s]\n",
96 NS_ConvertUTF16toUTF8(aDefaultPrinterName
).get()));
101 aDefaultPrinterName
.Truncate();
102 PR_PL(("NO DEFAULT PRINTER\n"));
106 //----------------------------------------------------------------------------------
107 NS_IMETHODIMP
nsDeviceContextSpecWin::Init(nsIPrintSettings
* aPrintSettings
,
108 bool aIsPrintPreview
) {
109 mPrintSettings
= aPrintSettings
;
111 // Get the Printer Name to be used and output format.
112 nsAutoString printerName
;
113 if (mPrintSettings
) {
114 mOutputFormat
= mPrintSettings
->GetOutputFormat();
115 mPrintSettings
->GetPrinterName(printerName
);
118 // If there is no name then use the default printer
119 if (printerName
.IsEmpty()) {
120 GetDefaultPrinterName(printerName
);
123 // Gather telemetry on the print target type.
125 // Unfortunately, if we're not using our own internal save-to-pdf codepaths,
126 // there isn't a good way to determine whether a print is going to be to a
127 // physical printer or to a file or some other non-physical output. We do our
128 // best by checking for what seems to be the most common save-to-PDF virtual
131 // We use StringBeginsWith below, since printer names are often followed by a
132 // version number or other product differentiating string. (True for doPDF,
133 // novaPDF, PDF-XChange and Soda PDF, for example.)
134 if (mOutputFormat
== nsIPrintSettings::kOutputFormatPDF
) {
135 Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_TARGET_TYPE
,
137 } else if (StringBeginsWith(printerName
, u
"Microsoft Print to PDF"_ns
) ||
138 StringBeginsWith(printerName
, u
"Adobe PDF"_ns
) ||
139 StringBeginsWith(printerName
, u
"Bullzip PDF Printer"_ns
) ||
140 StringBeginsWith(printerName
, u
"CutePDF Writer"_ns
) ||
141 StringBeginsWith(printerName
, u
"doPDF"_ns
) ||
142 StringBeginsWith(printerName
, u
"Foxit Reader PDF Printer"_ns
) ||
143 StringBeginsWith(printerName
, u
"Nitro PDF Creator"_ns
) ||
144 StringBeginsWith(printerName
, u
"novaPDF"_ns
) ||
145 StringBeginsWith(printerName
, u
"PDF-XChange"_ns
) ||
146 StringBeginsWith(printerName
, u
"PDF24 PDF"_ns
) ||
147 StringBeginsWith(printerName
, u
"PDFCreator"_ns
) ||
148 StringBeginsWith(printerName
, u
"PrimoPDF"_ns
) ||
149 StringBeginsWith(printerName
, u
"Soda PDF"_ns
) ||
150 StringBeginsWith(printerName
, u
"Solid PDF Creator"_ns
) ||
151 StringBeginsWith(printerName
,
152 u
"Universal Document Converter"_ns
)) {
153 Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_TARGET_TYPE
,
155 } else if (printerName
.EqualsLiteral("Microsoft XPS Document Writer")) {
156 Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_TARGET_TYPE
,
159 nsAString::const_iterator start
, end
;
160 printerName
.BeginReading(start
);
161 printerName
.EndReading(end
);
162 if (CaseInsensitiveFindInReadable(u
"pdf"_ns
, start
, end
)) {
163 Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_TARGET_TYPE
,
164 u
"pdf_unknown"_ns
, 1);
166 Telemetry::ScalarAdd(Telemetry::ScalarID::PRINTING_TARGET_TYPE
,
171 nsresult rv
= NS_ERROR_GFX_PRINTER_NO_PRINTER_AVAILABLE
;
172 if (aPrintSettings
) {
173 #ifdef MOZ_ENABLE_SKIA_PDF
174 nsAutoString printViaPdf
;
175 Preferences::GetString("print.print_via_pdf_encoder", printViaPdf
);
176 if (printViaPdf
.EqualsLiteral("skia-pdf")) {
177 mPrintViaSkPDF
= true;
181 // If we're in the child or we're printing to PDF we only need information
182 // from the print settings.
183 if (XRE_IsContentProcess() ||
184 mOutputFormat
== nsIPrintSettings::kOutputFormatPDF
) {
188 nsCOMPtr
<nsIPrintSettingsWin
> psWin(do_QueryInterface(aPrintSettings
));
190 nsAutoString deviceName
;
191 nsAutoString driverName
;
192 psWin
->GetDeviceName(deviceName
);
193 psWin
->GetDriverName(driverName
);
196 psWin
->GetDevMode(&devMode
); // creates new memory (makes a copy)
198 if (!deviceName
.IsEmpty() && !driverName
.IsEmpty() && devMode
) {
199 // Scaling is special, it is one of the few
200 // devMode items that we control in layout
201 if (devMode
->dmFields
& DM_SCALE
) {
202 double scale
= double(devMode
->dmScale
) / 100.0f
;
204 aPrintSettings
->SetScaling(scale
);
205 devMode
->dmScale
= 100;
209 SetDeviceName(deviceName
);
210 SetDriverName(driverName
);
216 ("***** nsDeviceContextSpecWin::Init - "
217 "deviceName/driverName/devMode was NULL!\n"));
218 if (devMode
) ::HeapFree(::GetProcessHeap(), 0, devMode
);
222 PR_PL(("***** nsDeviceContextSpecWin::Init - aPrintSettingswas NULL!\n"));
225 if (printerName
.IsEmpty()) {
229 return GetDataFromPrinter(printerName
, mPrintSettings
);
232 //----------------------------------------------------------
234 already_AddRefed
<PrintTarget
> nsDeviceContextSpecWin::MakePrintTarget() {
235 NS_ASSERTION(mDevMode
|| mOutputFormat
== nsIPrintSettings::kOutputFormatPDF
,
236 "DevMode can't be NULL here unless we're printing to PDF.");
238 #ifdef MOZ_ENABLE_SKIA_PDF
239 if (mPrintViaSkPDF
) {
240 double width
, height
;
241 mPrintSettings
->GetEffectivePageSize(&width
, &height
);
242 if (width
<= 0 || height
<= 0) {
246 // convert twips to points
247 width
/= TWIPS_PER_POINT_FLOAT
;
248 height
/= TWIPS_PER_POINT_FLOAT
;
249 IntSize size
= IntSize::Ceil(width
, height
);
251 if (mOutputFormat
== nsIPrintSettings::kOutputFormatPDF
) {
254 // - Does this handle bug 1659470?
255 // - Should this code path be enabled, we should use temporary files and
256 // then move the file in `EndDocument()`.
257 mPrintSettings
->GetToFileName(filename
);
259 nsAutoCString
printFile(NS_ConvertUTF16toUTF8(filename
).get());
260 auto skStream
= MakeUnique
<SkFILEWStream
>(printFile
.get());
261 return PrintTargetSkPDF::CreateOrNull(std::move(skStream
), size
);
265 NS_WARNING_ASSERTION(!mDriverName
.IsEmpty(), "No driver!");
267 ::CreateDCW(mDriverName
.get(), mDeviceName
.get(), nullptr, mDevMode
);
269 gfxCriticalError(gfxCriticalError::DefaultOptions(false))
270 << "Failed to create device context in GetSurfaceForPrinter";
273 return PrintTargetEMF::CreateOrNull(dc
, size
);
278 if (mOutputFormat
== nsIPrintSettings::kOutputFormatPDF
) {
279 double width
, height
;
280 mPrintSettings
->GetEffectiveSheetSize(&width
, &height
);
281 if (width
<= 0 || height
<= 0) {
285 // convert twips to points
286 width
/= TWIPS_PER_POINT_FLOAT
;
287 height
/= TWIPS_PER_POINT_FLOAT
;
289 auto stream
= [&]() -> nsCOMPtr
<nsIOutputStream
> {
290 if (mPrintSettings
->GetOutputDestination() ==
291 nsIPrintSettings::kOutputDestinationStream
) {
292 nsCOMPtr
<nsIOutputStream
> out
;
293 mPrintSettings
->GetOutputStream(getter_AddRefs(out
));
297 // Even if the destination may be a named path, write to a temp file -
298 // this is consistent with behaviour of `PrintTarget` on other platforms.
299 nsCOMPtr
<nsIFile
> file
;
300 nsresult rv
= NS_OpenAnonymousTemporaryNsIFile(getter_AddRefs(mTempFile
));
303 if (NS_WARN_IF(NS_FAILED(rv
))) {
307 nsCOMPtr
<nsIFileOutputStream
> stream
=
308 do_CreateInstance("@mozilla.org/network/file-output-stream;1");
309 if (NS_FAILED(stream
->Init(file
, -1, -1, 0))) {
315 return PrintTargetPDF::CreateOrNull(stream
, IntSize::Ceil(width
, height
));
319 NS_WARNING_ASSERTION(!mDriverName
.IsEmpty(), "No driver!");
321 ::CreateDCW(mDriverName
.get(), mDeviceName
.get(), nullptr, mDevMode
);
323 gfxCriticalError(gfxCriticalError::DefaultOptions(false))
324 << "Failed to create device context in GetSurfaceForPrinter";
328 // The PrintTargetWindows takes over ownership of this DC
329 return PrintTargetWindows::CreateOrNull(dc
);
335 RefPtr
<PrintEndDocumentPromise
> nsDeviceContextSpecWin::EndDocument() {
336 if (mPrintSettings
->GetOutputDestination() !=
337 nsIPrintSettings::kOutputDestinationFile
||
338 mOutputFormat
!= nsIPrintSettings::kOutputFormatPDF
) {
339 return PrintEndDocumentPromise::CreateAndResolve(true, __func__
);
342 #ifdef MOZ_ENABLE_SKIA_PDF
343 if (mPrintViaSkPDF
) {
344 return PrintEndDocumentPromise::CreateAndResolve(true, __func__
);
348 MOZ_ASSERT(mTempFile
, "No handle to temporary PDF file.");
350 nsAutoString targetPath
;
351 mPrintSettings
->GetToFileName(targetPath
);
353 if (targetPath
.IsEmpty()) {
354 return PrintEndDocumentPromise::CreateAndResolve(true, __func__
);
357 // We still need to move the file to its actual destination.
358 nsCOMPtr
<nsIFile
> destFile
;
359 auto rv
= NS_NewLocalFile(targetPath
, false, getter_AddRefs(destFile
));
361 return PrintEndDocumentPromise::CreateAndReject(rv
, __func__
);
364 return nsIDeviceContextSpec::EndDocumentAsync(
366 [destFile
= std::move(destFile
),
367 tempFile
= std::move(mTempFile
)]() -> nsresult
{
368 nsAutoString destLeafName
;
369 MOZ_TRY(destFile
->GetLeafName(destLeafName
));
371 nsCOMPtr
<nsIFile
> destDir
;
372 MOZ_TRY(destFile
->GetParent(getter_AddRefs(destDir
)));
374 // This should be fine - Windows API calls usually prevent moving
375 // between different volumes (See Win32 API's `MOVEFILE_COPY_ALLOWED`
376 // flag), but we handle that down this call.
377 MOZ_TRY(tempFile
->MoveTo(destDir
, destLeafName
));
382 //----------------------------------------------------------------------------------
383 void nsDeviceContextSpecWin::SetDeviceName(const nsAString
& aDeviceName
) {
384 mDeviceName
= aDeviceName
;
387 //----------------------------------------------------------------------------------
388 void nsDeviceContextSpecWin::SetDriverName(const nsAString
& aDriverName
) {
389 mDriverName
= aDriverName
;
392 //----------------------------------------------------------------------------------
393 void nsDeviceContextSpecWin::SetDevMode(LPDEVMODEW aDevMode
) {
395 ::HeapFree(::GetProcessHeap(), 0, mDevMode
);
401 //------------------------------------------------------------------
402 void nsDeviceContextSpecWin::GetDevMode(LPDEVMODEW
& aDevMode
) {
406 #define DISPLAY_LAST_ERROR
408 //----------------------------------------------------------------------------------
409 // Setup the object's data member with the selected printer's data
410 nsresult
nsDeviceContextSpecWin::GetDataFromPrinter(const nsAString
& aName
,
411 nsIPrintSettings
* aPS
) {
412 nsresult rv
= NS_ERROR_FAILURE
;
414 nsHPRINTER hPrinter
= nullptr;
415 const nsString
& flat
= PromiseFlatString(aName
);
417 (wchar_t*)flat
.get(); // Windows APIs use non-const name argument
419 BOOL status
= ::OpenPrinterW(name
, &hPrinter
, nullptr);
421 nsAutoPrinter
autoPrinter(hPrinter
);
425 // Allocate a buffer of the correct size.
427 ::DocumentPropertiesW(nullptr, hPrinter
, name
, nullptr, nullptr, 0);
430 ("**** nsDeviceContextSpecWin::GetDataFromPrinter - Couldn't get "
431 "size of DEVMODE using DocumentPropertiesW(pDeviceName = \"%s\"). "
432 "GetLastEror() = %08lx\n",
433 NS_ConvertUTF16toUTF8(aName
).get(), GetLastError()));
434 return NS_ERROR_FAILURE
;
437 // Some drivers do not return the correct size for their DEVMODE, so we
438 // over-allocate to try and compensate.
439 // (See https://bugzilla.mozilla.org/show_bug.cgi?id=1664530#c5)
442 (LPDEVMODEW
)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY
, needed
);
443 if (!pDevMode
) return NS_ERROR_FAILURE
;
445 // Get the default DevMode for the printer and modify it for our needs.
446 LONG ret
= ::DocumentPropertiesW(nullptr, hPrinter
, name
, pDevMode
, nullptr,
449 if (ret
== IDOK
&& aPS
) {
450 nsCOMPtr
<nsIPrintSettingsWin
> psWin
= do_QueryInterface(aPS
);
452 psWin
->CopyToNative(pDevMode
);
453 // Sets back the changes we made to the DevMode into the Printer Driver
454 ret
= ::DocumentPropertiesW(nullptr, hPrinter
, name
, pDevMode
, pDevMode
,
455 DM_IN_BUFFER
| DM_OUT_BUFFER
);
457 // We need to copy the final DEVMODE settings back to our print settings,
458 // because they may have been set from invalid prefs.
460 // We need to get information from the device as well.
461 nsAutoHDC
printerDC(::CreateICW(kDriverName
, name
, nullptr, pDevMode
));
462 if (NS_WARN_IF(!printerDC
)) {
463 ::HeapFree(::GetProcessHeap(), 0, pDevMode
);
464 return NS_ERROR_FAILURE
;
467 psWin
->CopyFromNative(printerDC
, pDevMode
);
472 ::HeapFree(::GetProcessHeap(), 0, pDevMode
);
474 ("***** nsDeviceContextSpecWin::GetDataFromPrinter - "
475 "DocumentProperties call failed code: %ld/0x%lx\n",
478 return NS_ERROR_FAILURE
;
482 pDevMode
); // cache the pointer and takes responsibility for the memory
484 SetDeviceName(aName
);
486 SetDriverName(nsDependentString(kDriverName
));
490 rv
= NS_ERROR_GFX_PRINTER_NAME_NOT_FOUND
;
492 ("***** nsDeviceContextSpecWin::GetDataFromPrinter - Couldn't open "
494 NS_ConvertUTF16toUTF8(aName
).get()));
500 //***********************************************************
502 //***********************************************************
504 nsPrinterListWin::~nsPrinterListWin() = default;
506 // Helper to get the array of PRINTER_INFO_4 records from the OS into a
507 // caller-supplied byte array; returns the number of records present.
508 static unsigned GetPrinterInfo4(nsTArray
<BYTE
>& aBuffer
) {
509 const DWORD kLevel
= 4;
512 const DWORD kFlags
= PRINTER_ENUM_LOCAL
| PRINTER_ENUM_CONNECTIONS
;
513 BOOL ok
= ::EnumPrintersW(kFlags
,
516 nullptr, // pPrinterEnum
517 0, // cbBuf (buffer size)
518 &needed
, // Bytes needed in buffer
521 if (!aBuffer
.SetLength(needed
, fallible
)) {
524 ok
= ::EnumPrintersW(kFlags
, nullptr, kLevel
, aBuffer
.Elements(),
525 aBuffer
.Length(), &needed
, &count
);
533 nsTArray
<nsPrinterListBase::PrinterInfo
> nsPrinterListWin::Printers() const {
534 PR_PL(("nsPrinterListWin::Printers\n"));
536 AutoTArray
<BYTE
, 1024> buffer
;
537 unsigned count
= GetPrinterInfo4(buffer
);
540 PR_PL(("[No printers found]\n"));
544 const auto* printers
=
545 reinterpret_cast<const _PRINTER_INFO_4W
*>(buffer
.Elements());
546 nsTArray
<PrinterInfo
> list
;
547 for (unsigned i
= 0; i
< count
; i
++) {
548 // For LOCAL printers, we check whether OpenPrinter succeeds, and omit
549 // them from the list if not. This avoids presenting printers that the
550 // user cannot actually use (e.g. due to Windows permissions).
551 // For NETWORK printers, this check may block for a long time (waiting for
552 // network timeout), so we skip it; if the user tries to access a printer
553 // that isn't available, we'll have to show an error later.
554 // (We always need to be able to handle an error, anyhow, as the printer
555 // could get disconnected after we've created the list, for example.)
556 bool isAvailable
= false;
557 if (printers
[i
].Attributes
& PRINTER_ATTRIBUTE_NETWORK
) {
559 } else if (printers
[i
].Attributes
& PRINTER_ATTRIBUTE_LOCAL
) {
561 if (::OpenPrinterW(printers
[i
].pPrinterName
, &handle
, nullptr)) {
562 ::ClosePrinter(handle
);
567 list
.AppendElement(PrinterInfo
{nsString(printers
[i
].pPrinterName
)});
568 PR_PL(("Printer Name: %s\n",
569 NS_ConvertUTF16toUTF8(printers
[i
].pPrinterName
).get()));
574 PR_PL(("[No usable printers found]\n"));
581 Maybe
<nsPrinterListBase::PrinterInfo
> nsPrinterListWin::PrinterByName(
582 nsString aName
) const {
583 Maybe
<PrinterInfo
> rv
;
585 AutoTArray
<BYTE
, 1024> buffer
;
586 unsigned count
= GetPrinterInfo4(buffer
);
588 const auto* printers
=
589 reinterpret_cast<const _PRINTER_INFO_4W
*>(buffer
.Elements());
590 for (unsigned i
= 0; i
< count
; ++i
) {
591 if (aName
.Equals(nsString(printers
[i
].pPrinterName
))) {
592 rv
.emplace(PrinterInfo
{aName
});
600 Maybe
<nsPrinterListBase::PrinterInfo
> nsPrinterListWin::PrinterBySystemName(
601 nsString aName
) const {
602 return PrinterByName(std::move(aName
));
605 RefPtr
<nsIPrinter
> nsPrinterListWin::CreatePrinter(PrinterInfo aInfo
) const {
606 return nsPrinterWin::Create(mCommonPaperInfo
, std::move(aInfo
.mName
));
609 nsresult
nsPrinterListWin::SystemDefaultPrinterName(nsAString
& aName
) const {
610 if (!GetDefaultPrinterName(aName
)) {
611 NS_WARNING("Uh oh, GetDefaultPrinterName failed");
612 // Indicate failure by leaving aName untouched, i.e. the empty string.
618 nsPrinterListWin::InitPrintSettingsFromPrinter(
619 const nsAString
& aPrinterName
, nsIPrintSettings
* aPrintSettings
) {
620 NS_ENSURE_ARG_POINTER(aPrintSettings
);
622 if (aPrinterName
.IsEmpty()) {
626 // When printing to PDF on Windows there is no associated printer driver.
627 int16_t outputFormat
= aPrintSettings
->GetOutputFormat();
628 if (outputFormat
== nsIPrintSettings::kOutputFormatPDF
) {
632 RefPtr
<nsDeviceContextSpecWin
> devSpecWin
= new nsDeviceContextSpecWin();
633 if (!devSpecWin
) return NS_ERROR_OUT_OF_MEMORY
;
635 // If the settings have already been initialized from prefs then pass these to
636 // GetDataFromPrinter, so that they are saved to the printer.
637 bool initializedFromPrefs
;
639 aPrintSettings
->GetIsInitializedFromPrefs(&initializedFromPrefs
);
640 if (NS_WARN_IF(NS_FAILED(rv
))) {
643 if (initializedFromPrefs
) {
644 // If we pass in print settings to GetDataFromPrinter it already copies
645 // things back to the settings, so we can return here.
646 return devSpecWin
->GetDataFromPrinter(aPrinterName
, aPrintSettings
);
649 devSpecWin
->GetDataFromPrinter(aPrinterName
);
652 devSpecWin
->GetDevMode(devmode
);
653 if (NS_WARN_IF(!devmode
)) {
654 return NS_ERROR_FAILURE
;
657 aPrintSettings
->SetPrinterName(aPrinterName
);
659 // We need to get information from the device as well.
660 const nsString
& flat
= PromiseFlatString(aPrinterName
);
661 char16ptr_t printerName
= flat
.get();
662 HDC dc
= ::CreateICW(kDriverName
, printerName
, nullptr, devmode
);
663 if (NS_WARN_IF(!dc
)) {
664 return NS_ERROR_FAILURE
;
667 nsCOMPtr
<nsIPrintSettingsWin
> psWin
= do_QueryInterface(aPrintSettings
);
669 psWin
->CopyFromNative(dc
, devmode
);