Print enhancement: add webContents.printerList And a print option to select printer

This commit is contained in:
renaesop 2017-02-06 17:18:04 +08:00
parent 093b844859
commit 023a3fd547
15 changed files with 290 additions and 27 deletions

View file

@ -41,6 +41,7 @@
#include "atom/common/options_switches.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "brightray/browser/inspectable_web_contents.h"
#include "brightray/browser/inspectable_web_contents_view.h"
#include "chrome/browser/printing/print_preview_message_handler.h"
@ -65,6 +66,7 @@
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/context_menu_params.h"
#include "native_mate/converter.h"
#include "native_mate/dictionary.h"
#include "native_mate/object_template_builder.h"
#include "net/url_request/url_request_context.h"
@ -83,6 +85,7 @@ namespace {
struct PrintSettings {
bool silent;
bool print_background;
base::string16 device_name;
};
} // namespace
@ -120,10 +123,25 @@ struct Converter<PrintSettings> {
return false;
dict.Get("silent", &(out->silent));
dict.Get("printBackground", &(out->print_background));
dict.Get("deviceName", &(out->device_name));
return true;
}
};
template<>
struct Converter<printing::PrinterBasicInfo> {
static v8::Local<v8::Value>
ToV8(v8::Isolate* isolate, const printing::PrinterBasicInfo& val) {
mate::Dictionary dict(isolate, v8::Object::New(isolate));
dict.Set("printerName", val.printer_name);
dict.Set("printerDescription", val.printer_description);
dict.Set("printerStatus", val.printer_status);
dict.Set("isDefault", val.is_default);
dict.Set("options", val.options);
return dict.GetHandle();
}
};
template<>
struct Converter<WindowOpenDisposition> {
static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
@ -1114,14 +1132,24 @@ bool WebContents::IsAudioMuted() {
}
void WebContents::Print(mate::Arguments* args) {
PrintSettings settings = { false, false };
PrintSettings settings = { false, false, base::string16() };
if (args->Length() == 1 && !args->GetNext(&settings)) {
args->ThrowError();
return;
}
printing::PrintViewManagerBasic::FromWebContents(web_contents())->
PrintNow(settings.silent, settings.print_background);
PrintNow(settings.silent,
settings.print_background, settings.device_name);
}
std::vector<printing::PrinterBasicInfo> WebContents::GetPrinterList(
mate::Arguments* args) {
std::vector<printing::PrinterBasicInfo> printerList;
auto printBackend = printing::PrintBackend::CreateInstance(nullptr);
printBackend->EnumeratePrinters(&printerList);
return printerList;
}
void WebContents::PrintToPDF(const base::DictionaryValue& setting,
@ -1616,6 +1644,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
&WebContents::UnregisterServiceWorker)
.SetMethod("inspectServiceWorker", &WebContents::InspectServiceWorker)
.SetMethod("print", &WebContents::Print)
.SetMethod("printerList", &WebContents::GetPrinterList)
.SetMethod("_printToPDF", &WebContents::PrintToPDF)
.SetMethod("addWorkSpace", &WebContents::AddWorkSpace)
.SetMethod("removeWorkSpace", &WebContents::RemoveWorkSpace)

View file

@ -16,6 +16,7 @@
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/favicon_url.h"
#include "native_mate/handle.h"
#include "printing/backend/print_backend.h"
#include "ui/gfx/image/image.h"
namespace blink {
@ -109,6 +110,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
void SetAudioMuted(bool muted);
bool IsAudioMuted();
void Print(mate::Arguments* args);
std::vector<printing::PrinterBasicInfo> GetPrinterList(mate::Arguments* args);
void SetEmbedder(const WebContents* embedder);
// Print current page as PDF.

View file

@ -24,6 +24,21 @@
#include "printing/printing_utils.h"
#include "ui/base/l10n/l10n_util.h"
#include <stddef.h>
#include <algorithm>
#include <cmath>
#include <memory>
#include <string>
#include <utility>
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "printing/page_size_margins.h"
#include "printing/print_job_constants.h"
#include "printing/print_settings.h"
#include "printing/units.h"
using content::BrowserThread;
namespace printing {
@ -36,6 +51,81 @@ void HoldRefCallback(const scoped_refptr<printing::PrintJobWorkerOwner>& owner,
callback.Run();
}
void SetCustomMarginsToJobSettings(const PageSizeMargins& page_size_margins,
base::DictionaryValue* settings) {
std::unique_ptr<base::DictionaryValue> custom_margins(new base::DictionaryValue());
custom_margins->SetDouble(kSettingMarginTop, page_size_margins.margin_top);
custom_margins->SetDouble(kSettingMarginBottom, page_size_margins.margin_bottom);
custom_margins->SetDouble(kSettingMarginLeft, page_size_margins.margin_left);
custom_margins->SetDouble(kSettingMarginRight, page_size_margins.margin_right);
settings->Set(kSettingMarginsCustom, std::move(custom_margins));
}
void PrintSettingsToJobSettings(const PrintSettings& settings,
base::DictionaryValue* job_settings) {
// header footer
job_settings->SetBoolean(kSettingHeaderFooterEnabled,
settings.display_header_footer());
job_settings->SetString(kSettingHeaderFooterTitle, settings.title());
job_settings->SetString(kSettingHeaderFooterURL, settings.url());
// bg
job_settings->SetBoolean(kSettingShouldPrintBackgrounds,
settings.should_print_backgrounds());
job_settings->SetBoolean(kSettingShouldPrintSelectionOnly,
settings.selection_only());
// margin
auto margin_type = settings.margin_type();
job_settings->SetInteger(kSettingMarginsType, settings.margin_type());
if (margin_type == CUSTOM_MARGINS) {
const auto& margins_in_points = settings.requested_custom_margins_in_points();
PageSizeMargins page_size_margins;
page_size_margins.margin_top = margins_in_points.top;
page_size_margins.margin_bottom = margins_in_points.bottom;
page_size_margins.margin_left = margins_in_points.left;
page_size_margins.margin_right = margins_in_points.right;
SetCustomMarginsToJobSettings(page_size_margins, job_settings);
}
job_settings->SetInteger(kSettingPreviewPageCount, 1);
// range
if (!settings.ranges().empty()) {
base::ListValue* page_range_array = new base::ListValue;
job_settings->Set(kSettingPageRange, page_range_array);
for (size_t i = 0; i < settings.ranges().size(); ++i) {
std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue);
dict->SetInteger(kSettingPageRangeFrom, settings.ranges()[i].from + 1);
dict->SetInteger(kSettingPageRangeTo, settings.ranges()[i].to + 1);
page_range_array->Append(std::move(dict));
}
}
job_settings->SetBoolean(kSettingCollate, settings.collate());
job_settings->SetInteger(kSettingCopies, 1);
job_settings->SetInteger(kSettingColor, settings.color());
job_settings->SetInteger(kSettingDuplexMode, settings.duplex_mode());
job_settings->SetBoolean(kSettingLandscape, settings.landscape());
job_settings->SetString(kSettingDeviceName, settings.device_name());
job_settings->SetInteger("scaleFactor", 100);
job_settings->SetBoolean("rasterizePDF", false);
job_settings->SetInteger("desiredDpi", settings.desired_dpi());
job_settings->SetInteger("dpi", settings.dpi());
job_settings->SetBoolean(kSettingPrintToPDF, false);
job_settings->SetBoolean(kSettingCloudPrintDialog, false);
job_settings->SetBoolean(kSettingPrintWithPrivet, false);
job_settings->SetBoolean(kSettingPrintWithExtension, false);
job_settings->SetBoolean("showSystemDialog", false);
job_settings->SetInteger(kSettingPreviewPageCount, 1);
}
class PrintingContextDelegate : public PrintingContext::Delegate {
public:
PrintingContextDelegate(int render_process_id, int render_view_id);
@ -115,7 +205,8 @@ void PrintJobWorker::GetSettings(
bool ask_user_for_settings,
int document_page_count,
bool has_selection,
MarginType margin_type) {
MarginType margin_type,
const base::string16& device_name) {
DCHECK(task_runner_->RunsTasksOnCurrentThread());
DCHECK_EQ(page_number_, PageNumber::npos());
@ -137,6 +228,13 @@ void PrintJobWorker::GetSettings(
base::Unretained(this),
document_page_count,
has_selection)));
} else if (!device_name.empty()) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&HoldRefCallback, make_scoped_refptr(owner_),
base::Bind(&PrintJobWorker::InitWithDeviceName,
base::Unretained(this),
device_name)));
} else {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
@ -212,6 +310,14 @@ void PrintJobWorker::UseDefaultSettings() {
GetSettingsDone(result);
}
void PrintJobWorker::InitWithDeviceName(const base::string16& device_name) {
const auto& settings = printing_context_->settings();
std::unique_ptr<base::DictionaryValue> dic(new base::DictionaryValue);
PrintSettingsToJobSettings(settings, dic.get());
dic->SetString(kSettingDeviceName, device_name);
UpdatePrintSettings(std::move(dic));
}
void PrintJobWorker::StartPrinting(PrintedDocument* new_document) {
DCHECK(task_runner_->RunsTasksOnCurrentThread());
DCHECK_EQ(page_number_, PageNumber::npos());

View file

@ -46,7 +46,8 @@ class PrintJobWorker {
bool ask_user_for_settings,
int document_page_count,
bool has_selection,
MarginType margin_type);
MarginType margin_type,
const base::string16& device_name);
// Set the new print settings.
void SetSettings(std::unique_ptr<base::DictionaryValue> new_settings);
@ -127,6 +128,9 @@ class PrintJobWorker {
// systems.
void UseDefaultSettings();
// set the printer name
void InitWithDeviceName(const base::string16& device_name);
// Printing context delegate.
std::unique_ptr<PrintingContext::Delegate> printing_context_delegate_;

View file

@ -64,9 +64,10 @@ PrintViewManagerBase::~PrintViewManagerBase() {
}
#if !defined(DISABLE_BASIC_PRINTING)
bool PrintViewManagerBase::PrintNow(bool silent, bool print_background) {
bool PrintViewManagerBase::PrintNow(bool silent, bool print_background,
const base::string16& device_name) {
return PrintNowInternal(new PrintMsg_PrintPages(
routing_id(), silent, print_background));
routing_id(), silent, print_background, device_name));
}
#endif // !DISABLE_BASIC_PRINTING

View file

@ -39,7 +39,7 @@ class PrintViewManagerBase : public content::NotificationObserver,
// Prints the current document immediately. Since the rendering is
// asynchronous, the actual printing will not be completed on the return of
// this function. Returns false if printing is impossible at the moment.
virtual bool PrintNow(bool silent, bool print_background);
virtual bool PrintNow(bool silent, bool print_background, const base::string16&);
#endif // !DISABLE_BASIC_PRINTING
// PrintedPagesSource implementation.

View file

@ -82,7 +82,31 @@ void PrinterQuery::GetSettings(
is_print_dialog_box_shown_,
expected_page_count,
has_selection,
margin_type));
margin_type,
base::string16()));
}
void PrinterQuery::GetSettings(
GetSettingsAskParam ask_user_for_settings,
int expected_page_count,
bool has_selection,
MarginType margin_type,
const base::string16& device_name,
const base::Closure& callback) {
DCHECK(RunsTasksOnCurrentThread());
DCHECK(!is_print_dialog_box_shown_);
StartWorker(callback);
// Real work is done in PrintJobWorker::GetSettings().
is_print_dialog_box_shown_ = ask_user_for_settings == ASK_USER;
worker_->PostTask(FROM_HERE,
base::Bind(&PrintJobWorker::GetSettings,
base::Unretained(worker_.get()),
is_print_dialog_box_shown_,
expected_page_count,
has_selection,
margin_type,
device_name));
}
void PrinterQuery::SetSettings(std::unique_ptr<base::DictionaryValue> new_settings,

View file

@ -50,7 +50,15 @@ class PrinterQuery : public PrintJobWorkerOwner {
MarginType margin_type,
const base::Closure& callback);
// Updates the current settings with |new_settings| dictionary values.
void GetSettings(
GetSettingsAskParam ask_user_for_settings,
int expected_page_count,
bool has_selection,
MarginType margin_type,
const base::string16& device_name,
const base::Closure& callback);
// Updates the current settings with |new_settings| dictionary values.
void SetSettings(std::unique_ptr<base::DictionaryValue> new_settings,
const base::Closure& callback);

View file

@ -123,6 +123,9 @@ bool PrintingMessageFilter::OnMessageReceived(const IPC::Message& message) {
#endif
IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_GetDefaultPrintSettings,
OnGetDefaultPrintSettings)
IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_InitSettingWithDeviceName,
OnInitSettingWithDeviceName)
IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_ScriptedPrint, OnScriptedPrint)
IPC_MESSAGE_HANDLER_DELAY_REPLY(PrintHostMsg_UpdatePrintSettings,
OnUpdatePrintSettings)
@ -278,6 +281,30 @@ void PrintingMessageFilter::OnGetDefaultPrintSettings(IPC::Message* reply_msg) {
reply_msg));
}
void PrintingMessageFilter::OnInitSettingWithDeviceName(const base::string16& device_name,
IPC::Message* reply_msg) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
scoped_refptr<PrinterQuery> printer_query;
printer_query = queue_->PopPrinterQuery(0);
if (!printer_query.get()) {
printer_query =
queue_->CreatePrinterQuery(render_process_id_, reply_msg->routing_id());
}
// Loads default settings. This is asynchronous, only the IPC message sender
// will hang until the settings are retrieved.
printer_query->GetSettings(
PrinterQuery::DEFAULTS,
0,
false,
DEFAULT_MARGINS,
device_name,
base::Bind(&PrintingMessageFilter::OnGetDefaultPrintSettingsReply,
this,
printer_query,
reply_msg));
}
void PrintingMessageFilter::OnGetDefaultPrintSettingsReply(
scoped_refptr<PrinterQuery> printer_query,
IPC::Message* reply_msg) {

View file

@ -85,6 +85,11 @@ class PrintingMessageFilter : public content::BrowserMessageFilter {
// Get the default print setting.
void OnGetDefaultPrintSettings(IPC::Message* reply_msg);
// Set deviceName
void OnInitSettingWithDeviceName(const base::string16& device_name,
IPC::Message* reply_msg);
void OnGetDefaultPrintSettingsReply(scoped_refptr<PrinterQuery> printer_query,
IPC::Message* reply_msg);

View file

@ -23,7 +23,8 @@ PrintMsg_Print_Params::PrintMsg_Print_Params()
print_scaling_option(blink::WebPrintScalingOptionSourceSize),
title(),
url(),
should_print_backgrounds(false) {
should_print_backgrounds(false),
device_name() {
}
PrintMsg_Print_Params::~PrintMsg_Print_Params() {}
@ -45,6 +46,7 @@ void PrintMsg_Print_Params::Reset() {
title.clear();
url.clear();
should_print_backgrounds = false;
device_name.clear();
}
PrintMsg_PrintPages_Params::PrintMsg_PrintPages_Params()

View file

@ -52,6 +52,7 @@ struct PrintMsg_Print_Params {
base::string16 title;
base::string16 url;
bool should_print_backgrounds;
base::string16 device_name;
};
struct PrintMsg_PrintPages_Params {
@ -213,9 +214,10 @@ IPC_STRUCT_END()
// Tells the render view to switch the CSS to print media type, renders every
// requested pages and switch back the CSS to display media type.
IPC_MESSAGE_ROUTED2(PrintMsg_PrintPages,
IPC_MESSAGE_ROUTED3(PrintMsg_PrintPages,
bool /* silent print */,
bool /* print page's background */)
bool /* print page's background */,
base::string16 /* device name*/)
// Tells the render view that printing is done so it can clean up.
IPC_MESSAGE_ROUTED1(PrintMsg_PrintingDone,
@ -260,6 +262,11 @@ IPC_MESSAGE_ROUTED1(PrintHostMsg_DidPrintPage,
IPC_SYNC_MESSAGE_ROUTED0_1(PrintHostMsg_GetDefaultPrintSettings,
PrintMsg_Print_Params /* default_settings */)
// you can set the printer
IPC_SYNC_MESSAGE_ROUTED1_1(PrintHostMsg_InitSettingWithDeviceName,
base::string16, /* device name */
PrintMsg_Print_Params /* default_settings */)
// The renderer wants to update the current print settings with new
// |job_settings|.
IPC_SYNC_MESSAGE_ROUTED2_2(PrintHostMsg_UpdatePrintSettings,

View file

@ -661,13 +661,14 @@ void PrintWebViewHelper::OnDestruct() {
}
#if !defined(DISABLE_BASIC_PRINTING)
void PrintWebViewHelper::OnPrintPages(bool silent, bool print_background) {
void PrintWebViewHelper::OnPrintPages(bool silent, bool print_background,
const base::string16& device_name) {
if (ipc_nesting_level_> 1)
return;
blink::WebLocalFrame* frame =
render_view()->GetMainRenderFrame()->GetWebFrame();
Print(frame, blink::WebNode(), silent, print_background);
Print(frame, blink::WebNode(), silent, print_background, device_name);
}
#endif // !DISABLE_BASIC_PRINTING
@ -851,7 +852,8 @@ void PrintWebViewHelper::PrintNode(const blink::WebNode& node) {
void PrintWebViewHelper::Print(blink::WebLocalFrame* frame,
const blink::WebNode& node,
bool silent,
bool print_background) {
bool print_background,
const base::string16& device_name) {
// If still not finished with earlier print request simply ignore.
if (prep_frame_view_)
return;
@ -859,7 +861,7 @@ void PrintWebViewHelper::Print(blink::WebLocalFrame* frame,
FrameReference frame_ref(frame);
int expected_page_count = 0;
if (!CalculateNumberOfPages(frame, node, &expected_page_count)) {
if (!CalculateNumberOfPages(frame, node, &expected_page_count, device_name)) {
DidFinishPrinting(FAIL_PRINT_INIT);
return; // Failed to init print page settings.
}
@ -995,10 +997,16 @@ void PrintWebViewHelper::ComputePageLayoutInPointsForCss(
CalculatePageLayoutFromPrintParams(params, page_layout_in_points);
}
bool PrintWebViewHelper::InitPrintSettings(bool fit_to_paper_size) {
bool PrintWebViewHelper::InitPrintSettings(bool fit_to_paper_size,
const base::string16& device_name) {
PrintMsg_PrintPages_Params settings;
Send(new PrintHostMsg_GetDefaultPrintSettings(routing_id(),
&settings.params));
if (device_name.empty()) {
Send(new PrintHostMsg_GetDefaultPrintSettings(routing_id(),
&settings.params));
} else {
Send(new PrintHostMsg_InitSettingWithDeviceName(routing_id(), device_name,
&settings.params));
}
// Check if the printer returned any settings, if the settings is empty, we
// can safely assume there are no printer drivers configured. So we safely
// terminate.
@ -1023,10 +1031,11 @@ bool PrintWebViewHelper::InitPrintSettings(bool fit_to_paper_size) {
bool PrintWebViewHelper::CalculateNumberOfPages(blink::WebLocalFrame* frame,
const blink::WebNode& node,
int* number_of_pages) {
int* number_of_pages,
const base::string16& device_name) {
DCHECK(frame);
bool fit_to_paper_size = !(PrintingNodeOrPdfFrame(frame, node));
if (!InitPrintSettings(fit_to_paper_size)) {
if (!InitPrintSettings(fit_to_paper_size, device_name)) {
notify_browser_of_print_failure_ = false;
Send(new PrintHostMsg_ShowInvalidPrinterSettingsError(routing_id()));
return false;

View file

@ -98,7 +98,7 @@ class PrintWebViewHelper
// Message handlers ---------------------------------------------------------
#if !defined(DISABLE_BASIC_PRINTING)
void OnPrintPages(bool silent, bool print_background);
void OnPrintPages(bool silent, bool print_background, const base::string16&);
void OnPrintingDone(bool success);
#endif // !DISABLE_BASIC_PRINTING
void OnPrintPreview(const base::DictionaryValue& settings);
@ -137,7 +137,8 @@ class PrintWebViewHelper
void Print(blink::WebLocalFrame* frame,
const blink::WebNode& node,
bool silent = false,
bool print_background = false);
bool print_background = false,
const base::string16& device_name = base::string16());
// Notification when printing is done - signal tear-down/free resources.
void DidFinishPrinting(PrintingResult result);
@ -146,12 +147,14 @@ class PrintWebViewHelper
// Initialize print page settings with default settings.
// Used only for native printing workflow.
bool InitPrintSettings(bool fit_to_paper_size);
bool InitPrintSettings(bool fit_to_paper_size,
const base::string16& device_name = base::string16());
// Calculate number of pages in source document.
bool CalculateNumberOfPages(blink::WebLocalFrame* frame,
const blink::WebNode& node,
int* number_of_pages);
int* number_of_pages,
const base::string16& device_name = base::string16());
// Update the current print settings with new |passed_job_settings|.
// |passed_job_settings| dictionary contains print job details such as printer

View file

@ -859,18 +859,54 @@ Unregisters any ServiceWorker if present and returns a boolean as
response to `callback` when the JS promise is fulfilled or false
when the JS promise is rejected.
#### `contents.printerList()`
Get the system printer list, the result is an array of printer description.
eg:
````js
[{ printerName: 'Zebra_LP2844',
printerDescription: 'Zebra LP2844',
printerStatus: 3,
isDefault: 0,
options:
{ copies: '1',
'device-uri': 'usb://Zebra/LP2844?location=14200000',
finishings: '3',
'job-cancel-after': '10800',
'job-hold-until': 'no-hold',
'job-priority': '50',
'job-sheets': 'none,none',
'marker-change-time': '0',
'number-up': '1',
'printer-commands': 'none',
'printer-info': 'Zebra LP2844',
'printer-is-accepting-jobs': 'true',
'printer-is-shared': 'true',
'printer-location': '',
'printer-make-and-model': 'Zebra EPL2 Label Printer',
'printer-state': '3',
'printer-state-change-time': '1484872644',
'printer-state-reasons': 'offline-report',
'printer-type': '36932',
'printer-uri-supported': 'ipp://localhost/printers/Zebra_LP2844',
system_driverinfo: 'Z' } }]
````
#### `contents.print([options])`
* `options` Object (optional)
* `silent` Boolean - Don't ask user for print settings. Default is `false`.
* `printBackground` Boolean - Also prints the background color and image of
the web page. Default is `false`.
* `deviceName` String - Set the printer
Prints window's web page. When `silent` is set to `true`, Electron will pick
up system's default printer and default settings for printing.
up printer depending on `deviceName`(when left will pick system's default printer)
and default settings for printing.
Calling `window.print()` in web page is equivalent to calling
`webContents.print({silent: false, printBackground: false})`.
`webContents.print({silent: false, printBackground: false, deviceName: ''})`.
Use `page-break-before: always; ` CSS style to force to print to a new page.