fix: silent printing default dpi on Windows (#41838)
Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
This commit is contained in:
parent
3ea26fb9d0
commit
8b0bc691c4
4 changed files with 185 additions and 97 deletions
2
BUILD.gn
2
BUILD.gn
|
@ -701,6 +701,8 @@ source_set("electron_lib") {
|
||||||
sources += [
|
sources += [
|
||||||
"shell/browser/printing/print_view_manager_electron.cc",
|
"shell/browser/printing/print_view_manager_electron.cc",
|
||||||
"shell/browser/printing/print_view_manager_electron.h",
|
"shell/browser/printing/print_view_manager_electron.h",
|
||||||
|
"shell/browser/printing/printing_utils.cc",
|
||||||
|
"shell/browser/printing/printing_utils.h",
|
||||||
"shell/renderer/printing/print_render_frame_helper_delegate.cc",
|
"shell/renderer/printing/print_render_frame_helper_delegate.cc",
|
||||||
"shell/renderer/printing/print_render_frame_helper_delegate.h",
|
"shell/renderer/printing/print_render_frame_helper_delegate.h",
|
||||||
]
|
]
|
||||||
|
|
|
@ -76,7 +76,6 @@
|
||||||
#include "mojo/public/cpp/system/platform_handle.h"
|
#include "mojo/public/cpp/system/platform_handle.h"
|
||||||
#include "ppapi/buildflags/buildflags.h"
|
#include "ppapi/buildflags/buildflags.h"
|
||||||
#include "printing/buildflags/buildflags.h"
|
#include "printing/buildflags/buildflags.h"
|
||||||
#include "printing/print_job_constants.h"
|
|
||||||
#include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
|
#include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
|
||||||
#include "services/service_manager/public/cpp/interface_provider.h"
|
#include "services/service_manager/public/cpp/interface_provider.h"
|
||||||
#include "shell/browser/api/electron_api_browser_window.h"
|
#include "shell/browser/api/electron_api_browser_window.h"
|
||||||
|
@ -173,10 +172,10 @@
|
||||||
#include "components/printing/browser/print_manager_utils.h"
|
#include "components/printing/browser/print_manager_utils.h"
|
||||||
#include "components/printing/browser/print_to_pdf/pdf_print_result.h"
|
#include "components/printing/browser/print_to_pdf/pdf_print_result.h"
|
||||||
#include "components/printing/browser/print_to_pdf/pdf_print_utils.h"
|
#include "components/printing/browser/print_to_pdf/pdf_print_utils.h"
|
||||||
#include "printing/backend/print_backend.h" // nogncheck
|
#include "printing/mojom/print.mojom.h" // nogncheck
|
||||||
#include "printing/mojom/print.mojom.h" // nogncheck
|
|
||||||
#include "printing/page_range.h"
|
#include "printing/page_range.h"
|
||||||
#include "shell/browser/printing/print_view_manager_electron.h"
|
#include "shell/browser/printing/print_view_manager_electron.h"
|
||||||
|
#include "shell/browser/printing/printing_utils.h"
|
||||||
|
|
||||||
#if BUILDFLAG(IS_WIN)
|
#if BUILDFLAG(IS_WIN)
|
||||||
#include "printing/backend/win_helper.h"
|
#include "printing/backend/win_helper.h"
|
||||||
|
@ -530,96 +529,6 @@ std::optional<base::TimeDelta> GetCursorBlinkInterval() {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if BUILDFLAG(ENABLE_PRINTING)
|
|
||||||
// This will return false if no printer with the provided device_name can be
|
|
||||||
// found on the network. We need to check this because Chromium does not do
|
|
||||||
// sanity checking of device_name validity and so will crash on invalid names.
|
|
||||||
bool IsDeviceNameValid(const std::u16string& device_name) {
|
|
||||||
#if BUILDFLAG(IS_MAC)
|
|
||||||
base::apple::ScopedCFTypeRef<CFStringRef> new_printer_id(
|
|
||||||
base::SysUTF16ToCFStringRef(device_name));
|
|
||||||
PMPrinter new_printer = PMPrinterCreateFromPrinterID(new_printer_id.get());
|
|
||||||
bool printer_exists = new_printer != nullptr;
|
|
||||||
PMRelease(new_printer);
|
|
||||||
return printer_exists;
|
|
||||||
#else
|
|
||||||
scoped_refptr<printing::PrintBackend> print_backend =
|
|
||||||
printing::PrintBackend::CreateInstance(
|
|
||||||
g_browser_process->GetApplicationLocale());
|
|
||||||
return print_backend->IsValidPrinter(base::UTF16ToUTF8(device_name));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function returns a validated device name.
|
|
||||||
// If the user passed one to webContents.print(), we check that it's valid and
|
|
||||||
// return it or fail if the network doesn't recognize it. If the user didn't
|
|
||||||
// pass a device name, we first try to return the system default printer. If one
|
|
||||||
// isn't set, then pull all the printers and use the first one or fail if none
|
|
||||||
// exist.
|
|
||||||
std::pair<std::string, std::u16string> GetDeviceNameToUse(
|
|
||||||
const std::u16string& device_name) {
|
|
||||||
#if BUILDFLAG(IS_WIN)
|
|
||||||
// Blocking is needed here because Windows printer drivers are oftentimes
|
|
||||||
// not thread-safe and have to be accessed on the UI thread.
|
|
||||||
ScopedAllowBlockingForElectron allow_blocking;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!device_name.empty()) {
|
|
||||||
if (!IsDeviceNameValid(device_name))
|
|
||||||
return std::make_pair("Invalid deviceName provided", std::u16string());
|
|
||||||
return std::make_pair(std::string(), device_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
scoped_refptr<printing::PrintBackend> print_backend =
|
|
||||||
printing::PrintBackend::CreateInstance(
|
|
||||||
g_browser_process->GetApplicationLocale());
|
|
||||||
std::string printer_name;
|
|
||||||
printing::mojom::ResultCode code =
|
|
||||||
print_backend->GetDefaultPrinterName(printer_name);
|
|
||||||
|
|
||||||
// We don't want to return if this fails since some devices won't have a
|
|
||||||
// default printer.
|
|
||||||
if (code != printing::mojom::ResultCode::kSuccess)
|
|
||||||
LOG(ERROR) << "Failed to get default printer name";
|
|
||||||
|
|
||||||
if (printer_name.empty()) {
|
|
||||||
printing::PrinterList printers;
|
|
||||||
if (print_backend->EnumeratePrinters(printers) !=
|
|
||||||
printing::mojom::ResultCode::kSuccess)
|
|
||||||
return std::make_pair("Failed to enumerate printers", std::u16string());
|
|
||||||
if (printers.empty())
|
|
||||||
return std::make_pair("No printers available on the network",
|
|
||||||
std::u16string());
|
|
||||||
|
|
||||||
printer_name = printers.front().printer_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_pair(std::string(), base::UTF8ToUTF16(printer_name));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied from
|
|
||||||
// chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc:L36-L54
|
|
||||||
scoped_refptr<base::TaskRunner> CreatePrinterHandlerTaskRunner() {
|
|
||||||
// USER_VISIBLE because the result is displayed in the print preview dialog.
|
|
||||||
#if !BUILDFLAG(IS_WIN)
|
|
||||||
static constexpr base::TaskTraits kTraits = {
|
|
||||||
base::MayBlock(), base::TaskPriority::USER_VISIBLE};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(USE_CUPS)
|
|
||||||
// CUPS is thread safe.
|
|
||||||
return base::ThreadPool::CreateTaskRunner(kTraits);
|
|
||||||
#elif BUILDFLAG(IS_WIN)
|
|
||||||
// Windows drivers are likely not thread-safe and need to be accessed on the
|
|
||||||
// UI thread.
|
|
||||||
return content::GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE});
|
|
||||||
#else
|
|
||||||
// Be conservative on unsupported platforms.
|
|
||||||
return base::ThreadPool::CreateSingleThreadTaskRunner(kTraits);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct UserDataLink : public base::SupportsUserData::Data {
|
struct UserDataLink : public base::SupportsUserData::Data {
|
||||||
explicit UserDataLink(base::WeakPtr<WebContents> contents)
|
explicit UserDataLink(base::WeakPtr<WebContents> contents)
|
||||||
: web_contents(contents) {}
|
: web_contents(contents) {}
|
||||||
|
@ -2972,6 +2881,12 @@ void WebContents::OnGetDeviceNameToUse(
|
||||||
// If the user has passed a deviceName use it, otherwise use default printer.
|
// If the user has passed a deviceName use it, otherwise use default printer.
|
||||||
print_settings.Set(printing::kSettingDeviceName, info.second);
|
print_settings.Set(printing::kSettingDeviceName, info.second);
|
||||||
|
|
||||||
|
if (!print_settings.FindInt(printing::kSettingDpiHorizontal)) {
|
||||||
|
gfx::Size dpi = GetDefaultPrinterDPI(info.second);
|
||||||
|
print_settings.Set(printing::kSettingDpiHorizontal, dpi.width());
|
||||||
|
print_settings.Set(printing::kSettingDpiVertical, dpi.height());
|
||||||
|
}
|
||||||
|
|
||||||
auto* print_view_manager =
|
auto* print_view_manager =
|
||||||
PrintViewManagerElectron::FromWebContents(web_contents());
|
PrintViewManagerElectron::FromWebContents(web_contents());
|
||||||
if (!print_view_manager)
|
if (!print_view_manager)
|
||||||
|
@ -3133,7 +3048,6 @@ void WebContents::Print(gin::Arguments* args) {
|
||||||
|
|
||||||
// Set custom dots per inch (dpi)
|
// Set custom dots per inch (dpi)
|
||||||
gin_helper::Dictionary dpi_settings;
|
gin_helper::Dictionary dpi_settings;
|
||||||
int dpi = 72;
|
|
||||||
if (options.Get("dpi", &dpi_settings)) {
|
if (options.Get("dpi", &dpi_settings)) {
|
||||||
int horizontal = 72;
|
int horizontal = 72;
|
||||||
dpi_settings.Get("horizontal", &horizontal);
|
dpi_settings.Get("horizontal", &horizontal);
|
||||||
|
@ -3141,9 +3055,6 @@ void WebContents::Print(gin::Arguments* args) {
|
||||||
int vertical = 72;
|
int vertical = 72;
|
||||||
dpi_settings.Get("vertical", &vertical);
|
dpi_settings.Get("vertical", &vertical);
|
||||||
settings.Set(printing::kSettingDpiVertical, vertical);
|
settings.Set(printing::kSettingDpiVertical, vertical);
|
||||||
} else {
|
|
||||||
settings.Set(printing::kSettingDpiHorizontal, dpi);
|
|
||||||
settings.Set(printing::kSettingDpiVertical, dpi);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
print_task_runner_->PostTaskAndReplyWithResult(
|
print_task_runner_->PostTaskAndReplyWithResult(
|
||||||
|
|
134
shell/browser/printing/printing_utils.cc
Normal file
134
shell/browser/printing/printing_utils.cc
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
// Copyright (c) 2024 Microsoft, GmbH.
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "shell/browser/printing/printing_utils.h"
|
||||||
|
|
||||||
|
#include "base/apple/scoped_typeref.h"
|
||||||
|
#include "base/strings/sys_string_conversions.h"
|
||||||
|
#include "base/strings/utf_string_conversions.h"
|
||||||
|
#include "base/task/task_traits.h"
|
||||||
|
#include "base/task/thread_pool.h"
|
||||||
|
#include "chrome/browser/browser_process.h"
|
||||||
|
#include "content/public/browser/browser_thread.h"
|
||||||
|
#include "electron/buildflags/buildflags.h"
|
||||||
|
#include "printing/backend/print_backend.h" // nogncheck
|
||||||
|
#include "printing/units.h"
|
||||||
|
#include "shell/common/thread_restrictions.h"
|
||||||
|
|
||||||
|
#if BUILDFLAG(IS_MAC)
|
||||||
|
#include <ApplicationServices/ApplicationServices.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BUILDFLAG(IS_LINUX)
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if BUILDFLAG(IS_WIN)
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace electron {
|
||||||
|
|
||||||
|
gfx::Size GetDefaultPrinterDPI(const std::u16string& device_name) {
|
||||||
|
#if BUILDFLAG(IS_MAC)
|
||||||
|
return gfx::Size(printing::kDefaultMacDpi, printing::kDefaultMacDpi);
|
||||||
|
#elif BUILDFLAG(IS_WIN)
|
||||||
|
HDC hdc =
|
||||||
|
CreateDC(L"WINSPOOL", base::as_wcstr(device_name), nullptr, nullptr);
|
||||||
|
if (hdc == nullptr)
|
||||||
|
return gfx::Size(printing::kDefaultPdfDpi, printing::kDefaultPdfDpi);
|
||||||
|
|
||||||
|
int dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
|
||||||
|
int dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
|
||||||
|
DeleteDC(hdc);
|
||||||
|
|
||||||
|
return gfx::Size(dpi_x, dpi_y);
|
||||||
|
#else
|
||||||
|
GtkPrintSettings* print_settings = gtk_print_settings_new();
|
||||||
|
int dpi = gtk_print_settings_get_resolution(print_settings);
|
||||||
|
g_object_unref(print_settings);
|
||||||
|
return gfx::Size(dpi, dpi);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDeviceNameValid(const std::u16string& device_name) {
|
||||||
|
#if BUILDFLAG(IS_MAC)
|
||||||
|
base::apple::ScopedCFTypeRef<CFStringRef> new_printer_id(
|
||||||
|
base::SysUTF16ToCFStringRef(device_name));
|
||||||
|
PMPrinter new_printer = PMPrinterCreateFromPrinterID(new_printer_id.get());
|
||||||
|
bool printer_exists = new_printer != nullptr;
|
||||||
|
PMRelease(new_printer);
|
||||||
|
return printer_exists;
|
||||||
|
#else
|
||||||
|
scoped_refptr<printing::PrintBackend> print_backend =
|
||||||
|
printing::PrintBackend::CreateInstance(
|
||||||
|
g_browser_process->GetApplicationLocale());
|
||||||
|
return print_backend->IsValidPrinter(base::UTF16ToUTF8(device_name));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::string, std::u16string> GetDeviceNameToUse(
|
||||||
|
const std::u16string& device_name) {
|
||||||
|
#if BUILDFLAG(IS_WIN)
|
||||||
|
// Blocking is needed here because Windows printer drivers are oftentimes
|
||||||
|
// not thread-safe and have to be accessed on the UI thread.
|
||||||
|
ScopedAllowBlockingForElectron allow_blocking;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!device_name.empty()) {
|
||||||
|
if (!IsDeviceNameValid(device_name))
|
||||||
|
return std::make_pair("Invalid deviceName provided", std::u16string());
|
||||||
|
return std::make_pair(std::string(), device_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
scoped_refptr<printing::PrintBackend> print_backend =
|
||||||
|
printing::PrintBackend::CreateInstance(
|
||||||
|
g_browser_process->GetApplicationLocale());
|
||||||
|
std::string printer_name;
|
||||||
|
printing::mojom::ResultCode code =
|
||||||
|
print_backend->GetDefaultPrinterName(printer_name);
|
||||||
|
|
||||||
|
// We don't want to return if this fails since some devices won't have a
|
||||||
|
// default printer.
|
||||||
|
if (code != printing::mojom::ResultCode::kSuccess)
|
||||||
|
LOG(ERROR) << "Failed to get default printer name";
|
||||||
|
|
||||||
|
if (printer_name.empty()) {
|
||||||
|
printing::PrinterList printers;
|
||||||
|
if (print_backend->EnumeratePrinters(printers) !=
|
||||||
|
printing::mojom::ResultCode::kSuccess)
|
||||||
|
return std::make_pair("Failed to enumerate printers", std::u16string());
|
||||||
|
if (printers.empty())
|
||||||
|
return std::make_pair("No printers available on the network",
|
||||||
|
std::u16string());
|
||||||
|
|
||||||
|
printer_name = printers.front().printer_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(std::string(), base::UTF8ToUTF16(printer_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copied from
|
||||||
|
// chrome/browser/ui/webui/print_preview/local_printer_handler_default.cc:L36-L54
|
||||||
|
scoped_refptr<base::TaskRunner> CreatePrinterHandlerTaskRunner() {
|
||||||
|
// USER_VISIBLE because the result is displayed in the print preview dialog.
|
||||||
|
#if !BUILDFLAG(IS_WIN)
|
||||||
|
static constexpr base::TaskTraits kTraits = {
|
||||||
|
base::MayBlock(), base::TaskPriority::USER_VISIBLE};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(USE_CUPS)
|
||||||
|
// CUPS is thread safe.
|
||||||
|
return base::ThreadPool::CreateTaskRunner(kTraits);
|
||||||
|
#elif BUILDFLAG(IS_WIN)
|
||||||
|
// Windows drivers are likely not thread-safe and need to be accessed on the
|
||||||
|
// UI thread.
|
||||||
|
return content::GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE});
|
||||||
|
#else
|
||||||
|
// Be conservative on unsupported platforms.
|
||||||
|
return base::ThreadPool::CreateSingleThreadTaskRunner(kTraits);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace electron
|
41
shell/browser/printing/printing_utils.h
Normal file
41
shell/browser/printing/printing_utils.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright (c) 2024 Microsoft, GmbH.
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef ELECTRON_SHELL_BROWSER_PRINTING_PRINTING_UTILS_H_
|
||||||
|
#define ELECTRON_SHELL_BROWSER_PRINTING_PRINTING_UTILS_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "base/memory/scoped_refptr.h"
|
||||||
|
#include "base/task/task_runner.h"
|
||||||
|
|
||||||
|
namespace gfx {
|
||||||
|
class Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace electron {
|
||||||
|
|
||||||
|
// This function returns the per-platform default printer's DPI.
|
||||||
|
gfx::Size GetDefaultPrinterDPI(const std::u16string& device_name);
|
||||||
|
|
||||||
|
// This will return false if no printer with the provided device_name can be
|
||||||
|
// found on the network. We need to check this because Chromium does not do
|
||||||
|
// sanity checking of device_name validity and so will crash on invalid names.
|
||||||
|
bool IsDeviceNameValid(const std::u16string& device_name);
|
||||||
|
|
||||||
|
// This function returns a validated device name.
|
||||||
|
// If the user passed one to webContents.print(), we check that it's valid and
|
||||||
|
// return it or fail if the network doesn't recognize it. If the user didn't
|
||||||
|
// pass a device name, we first try to return the system default printer. If one
|
||||||
|
// isn't set, then pull all the printers and use the first one or fail if none
|
||||||
|
// exist.
|
||||||
|
std::pair<std::string, std::u16string> GetDeviceNameToUse(
|
||||||
|
const std::u16string& device_name);
|
||||||
|
|
||||||
|
// This function creates a task runner for use with printing tasks.
|
||||||
|
scoped_refptr<base::TaskRunner> CreatePrinterHandlerTaskRunner();
|
||||||
|
|
||||||
|
} // namespace electron
|
||||||
|
|
||||||
|
#endif // ELECTRON_SHELL_BROWSER_PRINTING_PRINTING_UTILS_H_
|
Loading…
Reference in a new issue