/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef MOZ_ENABLE_SKIA_PDF # include "mozilla/gfx/PrintTargetSkPDF.h" # include "mozilla/gfx/PrintTargetEMF.h" # include "nsIUUIDGenerator.h" # include "nsDirectoryServiceDefs.h" # include "nsPrintfCString.h" # include "nsThreadUtils.h" #endif
if (length) {
aDefaultPrinterName.SetLength(length); if (GetDefaultPrinterW((LPWSTR)aDefaultPrinterName.BeginWriting(),
&length)) { // `length` includes the terminating null, so we subtract that from our // string length.
aDefaultPrinterName.SetLength(length - 1);
PR_PL(("DEFAULT PRINTER [%s]\n",
NS_ConvertUTF16toUTF8(aDefaultPrinterName).get())); returntrue;
}
}
// Get the Printer Name to be used and output format.
nsAutoString printerName; if (mPrintSettings) {
mOutputFormat = mPrintSettings->GetOutputFormat();
mPrintSettings->GetPrinterName(printerName);
}
// If there is no name then use the default printer if (printerName.IsEmpty()) {
GetDefaultPrinterName(printerName);
}
// Gather telemetry on the print target type. // // Unfortunately, if we're not using our own internal save-to-pdf codepaths, // there isn't a good way to determine whether a print is going to be to a // physical printer or to a file or some other non-physical output. We do our // best by checking for what seems to be the most common save-to-PDF virtual // printers. // // We use StringBeginsWith below, since printer names are often followed by a // version number or other product differentiating string. (True for doPDF, // novaPDF, PDF-XChange and Soda PDF, for example.) if (mOutputFormat == nsIPrintSettings::kOutputFormatPDF) {
glean::printing::target_type
.EnumGet(glean::printing::TargetTypeLabel::ePdfFile)
.Add(1);
} elseif (StringBeginsWith(printerName, u"Microsoft Print to PDF"_ns) ||
StringBeginsWith(printerName, u"Adobe PDF"_ns) ||
StringBeginsWith(printerName, u"Bullzip PDF Printer"_ns) ||
StringBeginsWith(printerName, u"CutePDF Writer"_ns) ||
StringBeginsWith(printerName, u"doPDF"_ns) ||
StringBeginsWith(printerName, u"Foxit Reader PDF Printer"_ns) ||
StringBeginsWith(printerName, u"Nitro PDF Creator"_ns) ||
StringBeginsWith(printerName, u"novaPDF"_ns) ||
StringBeginsWith(printerName, u"PDF-XChange"_ns) ||
StringBeginsWith(printerName, u"PDF24 PDF"_ns) ||
StringBeginsWith(printerName, u"PDFCreator"_ns) ||
StringBeginsWith(printerName, u"PrimoPDF"_ns) ||
StringBeginsWith(printerName, u"Soda PDF"_ns) ||
StringBeginsWith(printerName, u"Solid PDF Creator"_ns) ||
StringBeginsWith(printerName,
u"Universal Document Converter"_ns)) {
glean::printing::target_type
.EnumGet(glean::printing::TargetTypeLabel::ePdfFile)
.Add(1);
} elseif (printerName.EqualsLiteral("Microsoft XPS Document Writer")) {
glean::printing::target_type
.EnumGet(glean::printing::TargetTypeLabel::eXpsFile)
.Add(1);
} else {
nsAString::const_iterator start, end;
printerName.BeginReading(start);
printerName.EndReading(end); if (CaseInsensitiveFindInReadable(u"pdf"_ns, start, end)) {
glean::printing::target_type
.EnumGet(glean::printing::TargetTypeLabel::ePdfUnknown)
.Add(1);
} else {
glean::printing::target_type
.EnumGet(glean::printing::TargetTypeLabel::eUnknown)
.Add(1);
}
}
// If we're in the child or we're printing to PDF we only need information // from the print settings. if (XRE_IsContentProcess() ||
mOutputFormat == nsIPrintSettings::kOutputFormatPDF) { return NS_OK;
}
LPDEVMODEW devMode;
psWin->GetDevMode(&devMode); // creates new memory (makes a copy)
if (!deviceName.IsEmpty() && !driverName.IsEmpty() && devMode) { // Scaling is special, it is one of the few // devMode items that we control in layout if (devMode->dmFields & DM_SCALE) { double scale = double(devMode->dmScale) / 100.0f; if (scale != 1.0) {
aPrintSettings->SetScaling(scale);
devMode->dmScale = 100;
}
}
if (mOutputFormat == nsIPrintSettings::kOutputFormatPDF) {
nsString filename; // TODO(dshin): // - Does this handle bug 1659470? // - Should this code path be enabled, we should use temporary files and // then move the file in `EndDocument()`.
mPrintSettings->GetToFileName(filename);
nsAutoCString printFile(NS_ConvertUTF16toUTF8(filename).get()); auto skStream = MakeUnique<SkFILEWStream>(printFile.get()); return PrintTargetSkPDF::CreateOrNull(std::move(skStream), size);
}
if (mDevMode) {
NS_WARNING_ASSERTION(!mDriverName.IsEmpty(), "No driver!");
HDC dc =
::CreateDCW(mDriverName.get(), mDeviceName.get(), nullptr, mDevMode); if (!dc) {
gfxCriticalError(gfxCriticalError::DefaultOptions(false))
<< "Failed to create device context in GetSurfaceForPrinter"; return nullptr;
} return PrintTargetEMF::CreateOrNull(dc, size);
}
} #endif
auto stream = [&]() -> nsCOMPtr<nsIOutputStream> { if (mPrintSettings->GetOutputDestination() ==
nsIPrintSettings::kOutputDestinationStream) {
nsCOMPtr<nsIOutputStream> out;
mPrintSettings->GetOutputStream(getter_AddRefs(out)); return out;
}
// Even if the destination may be a named path, write to a temp file - // this is consistent with behaviour of `PrintTarget` on other platforms.
nsCOMPtr<nsIFile> file;
nsresult rv = NS_OpenAnonymousTemporaryNsIFile(getter_AddRefs(mTempFile));
file = mTempFile;
if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr;
}
if (mDevMode) {
NS_WARNING_ASSERTION(!mDriverName.IsEmpty(), "No driver!");
HDC dc =
::CreateDCW(mDriverName.get(), mDeviceName.get(), nullptr, mDevMode); if (!dc) {
gfxCriticalError(gfxCriticalError::DefaultOptions(false))
<< "Failed to create device context in GetSurfaceForPrinter"; return nullptr;
}
// The PrintTargetWindows takes over ownership of this DC return PrintTargetWindows::CreateOrNull(dc);
}
if (targetPath.IsEmpty()) { return PrintEndDocumentPromise::CreateAndResolve(true, __func__);
}
// We still need to move the file to its actual destination.
nsCOMPtr<nsIFile> destFile; auto rv = NS_NewLocalFile(targetPath, getter_AddRefs(destFile)); if (NS_FAILED(rv)) { return PrintEndDocumentPromise::CreateAndReject(rv, __func__);
}
// This should be fine - Windows API calls usually prevent moving // between different volumes (See Win32 API's `MOVEFILE_COPY_ALLOWED` // flag), but we handle that down this call.
MOZ_TRY(tempFile->MoveTo(destDir, destLeafName)); return NS_OK;
});
}
//---------------------------------------------------------------------------------- // Setup the object's data member with the selected printer's data
nsresult nsDeviceContextSpecWin::GetDataFromPrinter(const nsAString& aName,
nsIPrintSettings* aPS) {
nsresult rv = NS_ERROR_FAILURE;
nsHPRINTER hPrinter = nullptr; const nsString& flat = PromiseFlatString(aName); wchar_t* name =
(wchar_t*)flat.get(); // Windows APIs use non-const name argument
BOOL status = ::OpenPrinterW(name, &hPrinter, nullptr); if (status) {
nsAutoPrinter autoPrinter(hPrinter);
LPDEVMODEW pDevMode;
// Allocate a buffer of the correct size. LONG needed =
::DocumentPropertiesW(nullptr, hPrinter, name, nullptr, nullptr, 0); if (needed < 0) {
PR_PL(
("**** nsDeviceContextSpecWin::GetDataFromPrinter - Couldn't get " "size of DEVMODE using DocumentPropertiesW(pDeviceName = \"%s\"). " "GetLastEror() = %08lx\n",
NS_ConvertUTF16toUTF8(aName).get(), GetLastError())); return NS_ERROR_FAILURE;
}
// Some drivers do not return the correct size for their DEVMODE, so we // over-allocate to try and compensate. // (See https://bugzilla.mozilla.org/show_bug.cgi?id=1664530#c5)
needed *= 2;
pDevMode =
(LPDEVMODEW)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, needed); if (!pDevMode) return NS_ERROR_FAILURE;
// Get the default DevMode for the printer and modify it for our needs. LONG ret = ::DocumentPropertiesW(nullptr, hPrinter, name, pDevMode, nullptr,
DM_OUT_BUFFER);
if (ret == IDOK && aPS) {
nsCOMPtr<nsIPrintSettingsWin> psWin = do_QueryInterface(aPS);
MOZ_ASSERT(psWin);
psWin->CopyToNative(pDevMode); // Sets back the changes we made to the DevMode into the Printer Driver
ret = ::DocumentPropertiesW(nullptr, hPrinter, name, pDevMode, pDevMode,
DM_IN_BUFFER | DM_OUT_BUFFER);
// We need to copy the final DEVMODE settings back to our print settings, // because they may have been set from invalid prefs. if (ret == IDOK) { // We need to get information from the device as well.
nsAutoHDC printerDC(::CreateICW(kDriverName, name, nullptr, pDevMode)); if (NS_WARN_IF(!printerDC)) {
::HeapFree(::GetProcessHeap(), 0, pDevMode); return NS_ERROR_FAILURE;
}
//*********************************************************** // Printer List //***********************************************************
nsPrinterListWin::~nsPrinterListWin() = default;
// Helper to get the array of PRINTER_INFO_4 records from the OS into a // caller-supplied byte array; returns the number of records present. staticunsigned GetPrinterInfo4(nsTArray<BYTE>& aBuffer) { const DWORD kLevel = 4;
DWORD needed = 0;
DWORD count = 0; const DWORD kFlags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_CONNECTIONS; BOOL ok = ::EnumPrintersW(kFlags,
nullptr, // Name
kLevel, // Level
nullptr, // pPrinterEnum
0, // cbBuf (buffer size)
&needed, // Bytes needed in buffer
&count); if (needed > 0) { if (!aBuffer.SetLength(needed, fallible)) { return 0;
}
ok = ::EnumPrintersW(kFlags, nullptr, kLevel, aBuffer.Elements(),
aBuffer.Length(), &needed, &count);
} if (!ok) { return 0;
} return count;
}
if (!count) {
PR_PL(("[No printers found]\n")); return {};
}
constauto* printers = reinterpret_cast<const _PRINTER_INFO_4W*>(buffer.Elements());
nsTArray<PrinterInfo> list; for (unsigned i = 0; i < count; i++) { // For LOCAL printers, we check whether OpenPrinter succeeds, and omit // them from the list if not. This avoids presenting printers that the // user cannot actually use (e.g. due to Windows permissions). // For NETWORK printers, this check may block for a long time (waiting for // network timeout), so we skip it; if the user tries to access a printer // that isn't available, we'll have to show an error later. // (We always need to be able to handle an error, anyhow, as the printer // could get disconnected after we've created the list, for example.) bool isAvailable = false; if (printers[i].Attributes & PRINTER_ATTRIBUTE_NETWORK) {
isAvailable = true;
} elseif (printers[i].Attributes & PRINTER_ATTRIBUTE_LOCAL) {
HANDLE handle; if (::OpenPrinterW(printers[i].pPrinterName, &handle, nullptr)) {
::ClosePrinter(handle);
isAvailable = true;
}
} if (isAvailable) {
list.AppendElement(PrinterInfo{nsString(printers[i].pPrinterName)});
PR_PL(("Printer Name: %s\n",
NS_ConvertUTF16toUTF8(printers[i].pPrinterName).get()));
}
}
if (list.IsEmpty()) {
PR_PL(("[No usable printers found]\n")); return {};
}
list.Sort([](const PrinterInfo& a, const PrinterInfo& b) {
size_t len = std::min(a.mName.Length(), b.mName.Length()); int result = CaseInsensitiveCompare(a.mName.BeginReading(),
b.mName.BeginReading(), len); return result ? result : int(a.mName.Length()) - int(b.mName.Length());
});
// When printing to PDF on Windows there is no associated printer driver.
int16_t outputFormat = aPrintSettings->GetOutputFormat(); if (outputFormat == nsIPrintSettings::kOutputFormatPDF) { return NS_OK;
}
RefPtr<nsDeviceContextSpecWin> devSpecWin = new nsDeviceContextSpecWin(); if (!devSpecWin) return NS_ERROR_OUT_OF_MEMORY;
// If the settings have already been initialized from prefs then pass these to // GetDataFromPrinter, so that they are saved to the printer. bool initializedFromPrefs;
nsresult rv =
aPrintSettings->GetIsInitializedFromPrefs(&initializedFromPrefs); if (NS_WARN_IF(NS_FAILED(rv))) { return rv;
} if (initializedFromPrefs) { // If we pass in print settings to GetDataFromPrinter it already copies // things back to the settings, so we can return here. return devSpecWin->GetDataFromPrinter(aPrinterName, aPrintSettings);
}
devSpecWin->GetDataFromPrinter(aPrinterName);
LPDEVMODEW devmode;
devSpecWin->GetDevMode(devmode); if (NS_WARN_IF(!devmode)) { return NS_ERROR_FAILURE;
}
aPrintSettings->SetPrinterName(aPrinterName);
// We need to get information from the device as well. const nsString& flat = PromiseFlatString(aPrinterName);
char16ptr_t printerName = flat.get();
HDC dc = ::CreateICW(kDriverName, printerName, nullptr, devmode); if (NS_WARN_IF(!dc)) { return NS_ERROR_FAILURE;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.