Chromium Code Reviews| Index: chrome/browser/printing/print_system_task_proxy.cc |
| diff --git a/chrome/browser/printing/print_system_task_proxy.cc b/chrome/browser/printing/print_system_task_proxy.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..0f70253450bb376055c45af5246f32412b232d0b |
| --- /dev/null |
| +++ b/chrome/browser/printing/print_system_task_proxy.cc |
| @@ -0,0 +1,527 @@ |
| +// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/printing/print_system_task_proxy.h" |
| + |
| +#include <ctype.h> |
| + |
| +#include <string> |
| +#include <vector> |
| + |
| +#include "base/bind.h" |
| +#include "base/metrics/histogram.h" |
| +#include "base/string_split.h" |
| +#include "base/string_util.h" |
| +#include "base/values.h" |
| +#include "printing/backend/print_backend.h" |
| +#include "printing/print_job_constants.h" |
| + |
| +#if defined(USE_CUPS) |
| +#include <cups/cups.h> |
| +#include <cups/ppd.h> |
| + |
| +#include "base/file_util.h" |
| +#endif |
| + |
| +#if defined(USE_CUPS) && !defined(OS_MACOSX) |
| +namespace printing_internal { |
| + |
| +void parse_lpoptions(const FilePath& filepath, const std::string& printer_name, |
| + int* num_options, cups_option_t** options) { |
| + std::string content; |
| + if (!file_util::ReadFileToString(filepath, &content)) |
| + return; |
| + |
| + const char kDest[] = "dest"; |
| + const char kDefault[] = "default"; |
| + size_t kDestLen = sizeof(kDest) - 1; |
| + size_t kDefaultLen = sizeof(kDefault) - 1; |
| + std::vector <std::string> lines; |
| + base::SplitString(content, '\n', &lines); |
| + |
| + for (size_t i = 0; i < lines.size(); ++i) { |
| + std::string line = lines[i]; |
| + if (line.empty()) |
| + continue; |
| + |
| + if (base::strncasecmp (line.c_str(), kDefault, kDefaultLen) == 0 && |
| + isspace(line[kDefaultLen])) { |
| + line = line.substr(kDefaultLen); |
| + } else if (base::strncasecmp (line.c_str(), kDest, kDestLen) == 0 && |
| + isspace(line[kDestLen])) { |
| + line = line.substr(kDestLen); |
| + } else { |
| + continue; |
| + } |
| + |
| + TrimWhitespaceASCII(line, TRIM_ALL, &line); |
| + if (line.empty()) |
| + continue; |
| + |
| + size_t space_found = line.find(' '); |
| + if (space_found == std::string::npos) |
| + continue; |
| + |
| + std::string name = line.substr(0, space_found); |
| + if (name.empty()) |
| + continue; |
| + |
| + if (base::strncasecmp(printer_name.c_str(), name.c_str(), |
| + name.length()) != 0) { |
| + continue; // This is not the required printer. |
| + } |
| + |
| + line = line.substr(space_found + 1); |
| + TrimWhitespaceASCII(line, TRIM_ALL, &line); // Remove extra spaces. |
| + if (line.empty()) |
| + continue; |
| + // Parse the selected printer custom options. |
| + *num_options = cupsParseOptions(line.c_str(), 0, options); |
| + } |
| +} |
| + |
| +void mark_lpoptions(const std::string& printer_name, ppd_file_t** ppd) { |
| + cups_option_t* options = NULL; |
| + int num_options = 0; |
| + ppdMarkDefaults(*ppd); |
| + |
| + const char kSystemLpOptionPath[] = "/etc/cups/lpoptions"; |
| + const char kUserLpOptionPath[] = ".cups/lpoptions"; |
| + |
| + std::vector<FilePath> file_locations; |
| + file_locations.push_back(FilePath(kSystemLpOptionPath)); |
| + file_locations.push_back(FilePath( |
| + file_util::GetHomeDir().Append(kUserLpOptionPath))); |
| + |
| + for (std::vector<FilePath>::const_iterator it = file_locations.begin(); |
| + it != file_locations.end(); ++it) { |
| + num_options = 0; |
| + options = NULL; |
| + parse_lpoptions(*it, printer_name, &num_options, &options); |
| + if (num_options > 0 && options) { |
| + cupsMarkOptions(*ppd, num_options, options); |
| + cupsFreeOptions(num_options, options); |
| + } |
| + } |
| +} |
| + |
| +} // printing_internal namespace |
| +#endif |
| + |
| +namespace { |
| + |
| +const char kDisableColorOption[] = "disableColorOption"; |
| +const char kSetColorAsDefault[] = "setColorAsDefault"; |
| +const char kSetDuplexAsDefault[] = "setDuplexAsDefault"; |
| +const char kPrinterColorModelForBlack[] = "printerColorModelForBlack"; |
| +const char kPrinterColorModelForColor[] = "printerColorModelForColor"; |
| +const char kPrinterDefaultDuplexValue[] = "printerDefaultDuplexValue"; |
| + |
| +#if defined(USE_CUPS) |
| +const char kColorDevice[] = "ColorDevice"; |
| +const char kColorModel[] = "ColorModel"; |
| +const char kColorMode[] = "ColorMode"; |
| +const char kProcessColorModel[] = "ProcessColorModel"; |
| +const char kPrintoutMode[] = "PrintoutMode"; |
| +const char kDraftGray[] = "Draft.Gray"; |
| +const char kHighGray[] = "High.Gray"; |
| + |
| +const char kDuplex[] = "Duplex"; |
| +const char kDuplexNone[] = "None"; |
| + |
| +void getBasicColorModelSettings( |
| + ppd_file_t* ppd, int* color_model_for_black, int* color_model_for_color, |
| + bool* color_is_default) { |
| + ppd_option_t* color_model = ppdFindOption(ppd, kColorModel); |
| + if (ppdFindChoice(color_model, printing::kBlack)) |
| + *color_model_for_black = printing::BLACK; |
| + else if (ppdFindChoice(color_model, printing::kGray)) |
| + *color_model_for_black = printing::GRAY; |
| + |
| + if (ppdFindChoice(color_model, printing::kColor)) |
| + *color_model_for_color = printing::COLOR; |
| + else if (ppdFindChoice(color_model, printing::kCMYK)) |
| + *color_model_for_color = printing::CMYK; |
| + else if (ppdFindChoice(color_model, printing::kRGB)) |
| + *color_model_for_color = printing::RGB; |
| + else if (ppdFindChoice(color_model, printing::kRGBA)) |
| + *color_model_for_color = printing::RGBA; |
| + else if (ppdFindChoice(color_model, printing::kRGB16)) |
| + *color_model_for_color = printing::RGB16; |
| + else if (ppdFindChoice(color_model, printing::kCMY)) |
| + *color_model_for_color = printing::CMY; |
| + else if (ppdFindChoice(color_model, printing::kKCMY)) |
| + *color_model_for_color = printing::KCMY; |
| + else if (ppdFindChoice(color_model, printing::kCMY_K)) |
| + *color_model_for_color = printing::CMY_K; |
| + |
| + ppd_choice_t* marked_choice = ppdFindMarkedChoice(ppd, kColorModel); |
| + if (!marked_choice) |
| + marked_choice = ppdFindChoice(color_model, color_model->defchoice); |
| + |
| + if (marked_choice) { |
| + *color_is_default = |
| + (base::strcasecmp(marked_choice->choice, printing::kBlack) != 0) && |
| + (base::strcasecmp(marked_choice->choice, printing::kGray) != 0); |
| + } |
| +} |
| + |
| +void getPrintOutModeColorSettings( |
| + ppd_file_t* ppd, int* color_model_for_black, int* color_model_for_color, |
| + bool* color_is_default) { |
| + ppd_option_t* printout_mode = ppdFindOption(ppd, kPrintoutMode); |
| + *color_model_for_color = printing::PRINTOUTMODE_NORMAL; |
| + *color_model_for_black = printing::PRINTOUTMODE_NORMAL; |
| + |
| + // Check to see if NORMAL_GRAY value is supported by PrintoutMode. |
| + // If NORMAL_GRAY is not supported, NORMAL value is used to |
| + // represent grayscale. If NORMAL_GRAY is supported, NORMAL is used to |
| + // represent color. |
| + if (ppdFindChoice(printout_mode, printing::kNormalGray)) |
| + *color_model_for_black = printing::PRINTOUTMODE_NORMAL_GRAY; |
| + |
| + // Get the default marked choice to identify the default color setting |
| + // value. |
| + ppd_choice_t* printout_mode_choice = ppdFindMarkedChoice(ppd, kPrintoutMode); |
| + if (!printout_mode_choice) { |
| + printout_mode_choice = ppdFindChoice(printout_mode, |
| + printout_mode->defchoice); |
| + } |
| + if (printout_mode_choice) { |
| + if ((base::strcasecmp(printout_mode_choice->choice, |
| + printing::kNormalGray) == 0) || |
| + (base::strcasecmp(printout_mode_choice->choice, kHighGray) == 0) || |
| + (base::strcasecmp(printout_mode_choice->choice, kDraftGray) == 0)) { |
| + *color_model_for_black = printing::PRINTOUTMODE_NORMAL_GRAY; |
| + *color_is_default = false; |
| + } |
| + } |
| +} |
| + |
| +void getColorModeSettings( |
| + ppd_file_t* ppd, int* color_model_for_black, int* color_model_for_color, |
| + bool* color_is_default) { |
| + // Samsung printers use "ColorMode" attribute in their ppds. |
| + ppd_option_t* color_mode_option = ppdFindOption(ppd, kColorMode); |
| + if (ppdFindChoice(color_mode_option, printing::kColor)) |
| + *color_model_for_color = printing::COLORMODE_COLOR; |
| + |
| + if (ppdFindChoice(color_mode_option, printing::kMonochrome)) |
| + *color_model_for_black = printing::COLORMODE_MONOCHROME; |
| + |
| + ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kColorMode); |
| + if (!mode_choice) { |
| + mode_choice = ppdFindChoice(color_mode_option, |
| + color_mode_option->defchoice); |
| + } |
| + |
| + if (mode_choice) { |
| + *color_is_default = |
| + (base::strcasecmp(mode_choice->choice, printing::kColor) == 0); |
| + } |
| +} |
| + |
| +void getHPColorSettings( |
| + ppd_file_t* ppd, int* color_model_for_black, int* color_model_for_color, |
| + bool* color_is_default) { |
| + // HP printers use "Color/Color Model" attribute in their ppds. |
| + ppd_option_t* color_mode_option = ppdFindOption(ppd, printing::kColor); |
| + if (ppdFindChoice(color_mode_option, printing::kColor)) |
| + *color_model_for_color = printing::HP_COLOR_COLOR; |
| + if (ppdFindChoice(color_mode_option, printing::kBlack)) |
| + *color_model_for_black = printing::HP_COLOR_BLACK; |
| + |
| + ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kColorMode); |
| + if (!mode_choice) { |
| + mode_choice = ppdFindChoice(color_mode_option, |
| + color_mode_option->defchoice); |
| + } |
| + if (mode_choice) { |
| + *color_is_default = |
| + (base::strcasecmp(mode_choice->choice, printing::kColor) == 0); |
| + } |
| +} |
| + |
| +void getProcessColorModelSettings( |
| + ppd_file_t* ppd, int* color_model_for_black, int* color_model_for_color, |
| + bool* color_is_default) { |
| + // Canon printers use "ProcessColorModel" attribute in their ppds. |
| + ppd_option_t* color_mode_option = ppdFindOption(ppd, kProcessColorModel); |
| + if (ppdFindChoice(color_mode_option, printing::kRGB)) |
| + *color_model_for_color = printing::PROCESSCOLORMODEL_RGB; |
| + else if (ppdFindChoice(color_mode_option, printing::kCMYK)) |
| + *color_model_for_color = printing::PROCESSCOLORMODEL_CMYK; |
| + |
| + if (ppdFindChoice(color_mode_option, printing::kGreyscale)) |
| + *color_model_for_black = printing::PROCESSCOLORMODEL_GREYSCALE; |
| + |
| + ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kProcessColorModel); |
| + if (!mode_choice) { |
| + mode_choice = ppdFindChoice(color_mode_option, |
| + color_mode_option->defchoice); |
| + } |
| + |
| + if (mode_choice) { |
| + *color_is_default = |
| + (base::strcasecmp(mode_choice->choice, printing::kGreyscale) != 0); |
| + } |
| +} |
| +#elif defined(OS_WIN) |
| +const char kPskColor[] = "psk:Color"; |
|
vandebo (ex-Chrome)
2011/10/11 19:11:42
Consider putting this block first since it is much
kmadhusu
2011/10/11 21:36:03
Done.
|
| +const char kPskGray[] = "psk:Grayscale"; |
| +const char kPskMonochrome[] = "psk:Monochrome"; |
| +const char kPskDuplexFeature[] = "psk:JobDuplexAllDocumentsContiguously"; |
| +const char kPskTwoSided[] = "psk:TwoSided"; |
| +#endif |
| + |
| +} // namespace |
| + |
| +PrintSystemTaskProxy::PrintSystemTaskProxy( |
| + const base::WeakPtr<PrintPreviewHandler>& handler, |
| + printing::PrintBackend* print_backend, |
| + bool has_logged_printers_count) |
| + : handler_(handler), |
| + print_backend_(print_backend), |
| + has_logged_printers_count_(has_logged_printers_count) { |
| +} |
| + |
| +PrintSystemTaskProxy::~PrintSystemTaskProxy() { |
| +} |
| + |
| +void PrintSystemTaskProxy::GetDefaultPrinter() { |
| + VLOG(1) << "Get default printer start"; |
| + StringValue* default_printer = NULL; |
| + if (PrintPreviewHandler::last_used_printer_name_ == NULL) { |
| + default_printer = new StringValue( |
| + print_backend_->GetDefaultPrinterName()); |
| + } else { |
| + default_printer = new StringValue( |
| + *PrintPreviewHandler::last_used_printer_name_); |
| + } |
| + std::string default_printer_string; |
| + default_printer->GetAsString(&default_printer_string); |
| + VLOG(1) << "Get default printer finished, found: " |
| + << default_printer_string; |
| + |
| + StringValue* cloud_print_data = NULL; |
| + if (PrintPreviewHandler::last_used_printer_cloud_print_data_ != NULL) { |
| + cloud_print_data = new StringValue( |
| + *PrintPreviewHandler::last_used_printer_cloud_print_data_); |
| + } else { |
| + cloud_print_data = new StringValue(""); |
| + } |
| + |
| + BrowserThread::PostTask( |
| + BrowserThread::UI, FROM_HERE, |
| + base::Bind(&PrintSystemTaskProxy::SendDefaultPrinter, this, |
| + default_printer, cloud_print_data)); |
| +} |
| + |
| +void PrintSystemTaskProxy::SendDefaultPrinter( |
| + const StringValue* default_printer, const StringValue* cloud_print_data) { |
| + if (handler_) |
| + handler_->SendDefaultPrinter(*default_printer, *cloud_print_data); |
| + delete default_printer; |
| +} |
| + |
| +void PrintSystemTaskProxy::EnumeratePrinters() { |
| + VLOG(1) << "Enumerate printers start"; |
| + ListValue* printers = new ListValue; |
| + printing::PrinterList printer_list; |
| + print_backend_->EnumeratePrinters(&printer_list); |
| + |
| + if (!has_logged_printers_count_) { |
| + // Record the total number of printers. |
| + UMA_HISTOGRAM_COUNTS("PrintPreview.NumberOfPrinters", |
| + printer_list.size()); |
| + } |
| + int i = 0; |
| + for (printing::PrinterList::iterator iter = printer_list.begin(); |
| + iter != printer_list.end(); ++iter, ++i) { |
| + DictionaryValue* printer_info = new DictionaryValue; |
| + std::string printerName; |
| +#if defined(OS_MACOSX) |
| + // On Mac, |iter->printer_description| specifies the printer name and |
| + // |iter->printer_name| specifies the device name / printer queue name. |
| + printerName = iter->printer_description; |
| +#else |
| + printerName = iter->printer_name; |
| +#endif |
| + printer_info->SetString(printing::kSettingPrinterName, printerName); |
| + printer_info->SetString(printing::kSettingDeviceName, iter->printer_name); |
| + printers->Append(printer_info); |
| + } |
| + VLOG(1) << "Enumerate printers finished, found " << i << " printers"; |
| + |
| + BrowserThread::PostTask( |
| + BrowserThread::UI, FROM_HERE, |
| + base::Bind(&PrintSystemTaskProxy::SetupPrinterList, this, printers)); |
| +} |
| + |
| +void PrintSystemTaskProxy::SetupPrinterList(ListValue* printers) { |
| + if (handler_) |
| + handler_->SetupPrinterList(*printers); |
| + delete printers; |
| +} |
| + |
| +void PrintSystemTaskProxy::GetPrinterCapabilities( |
| + const std::string& printer_name) { |
| + VLOG(1) << "Get printer capabilities start for " << printer_name; |
| + printing::PrinterCapsAndDefaults printer_info; |
| + |
| + bool set_color_as_default = false; |
| + bool disable_color_options = true; |
| + bool set_duplex_as_default = false; |
| + int printer_color_space_for_color = printing::UNKNOWN_COLOR_MODEL; |
| + int printer_color_space_for_black = printing::UNKNOWN_COLOR_MODEL; |
| + int default_duplex_setting_value = printing::UNKNOWN_DUPLEX_MODE; |
| + if (!print_backend_->GetPrinterCapsAndDefaults(printer_name, |
| + &printer_info)) { |
| + return; |
| + } |
| + |
| +#if defined(USE_CUPS) |
| + FilePath ppd_file_path; |
| + if (!file_util::CreateTemporaryFile(&ppd_file_path)) |
| + return; |
| + |
| + int data_size = printer_info.printer_capabilities.length(); |
| + if (data_size != file_util::WriteFile( |
| + ppd_file_path, |
| + printer_info.printer_capabilities.data(), |
| + data_size)) { |
| + file_util::Delete(ppd_file_path, false); |
| + return; |
| + } |
| + |
| + ppd_file_t* ppd = ppdOpenFile(ppd_file_path.value().c_str()); |
| + if (ppd) { |
| +#if !defined(OS_MACOSX) |
| + printing_internal::mark_lpoptions(printer_name, &ppd); |
| +#endif |
| + bool is_color_device = false; |
|
vandebo (ex-Chrome)
2011/10/11 19:11:42
consider moving this block down to 425, so all the
kmadhusu
2011/10/11 21:36:03
Done.
|
| + ppd_attr_t* attr = ppdFindAttr(ppd, kColorDevice, NULL); |
| + if (attr && attr->value) |
| + is_color_device = ppd->color_device; |
| + disable_color_options = !is_color_device; |
| + |
| + ppd_choice_t* duplex_choice = ppdFindMarkedChoice(ppd, kDuplex); |
| + if (duplex_choice) { |
| + ppd_option_t* option = ppdFindOption(ppd, kDuplex); |
| + if (option) |
| + duplex_choice = ppdFindChoice(option, option->defchoice); |
| + } |
| + |
| + if (duplex_choice) { |
| + if (base::strcasecmp(duplex_choice->choice, kDuplexNone) != 0) { |
| + set_duplex_as_default = true; |
| + default_duplex_setting_value = printing::LONG_EDGE; |
| + } else { |
| + default_duplex_setting_value = printing::SIMPLEX; |
| + } |
| + } |
| + |
| + set_color_as_default = is_color_device; |
| + |
| + if (is_color_device && ppdFindOption(ppd, kColorModel)) { |
|
kmadhusu
2011/10/10 23:34:26
All the data members and member functions of this
vandebo (ex-Chrome)
2011/10/11 19:11:42
Hmm, this structure ends up calling ppdFindOption
kmadhusu
2011/10/11 21:36:03
Done.
|
| + getBasicColorModelSettings( |
| + ppd, &printer_color_space_for_black, &printer_color_space_for_color, |
| + &set_color_as_default); |
| + } else if (ppdFindOption(ppd, kPrintoutMode)) { |
| + getPrintOutModeColorSettings( |
| + ppd, &printer_color_space_for_black, &printer_color_space_for_color, |
| + &set_color_as_default); |
| + } else if (ppdFindOption(ppd, kColorMode)) { |
| + getColorModeSettings( |
| + ppd, &printer_color_space_for_black, &printer_color_space_for_color, |
| + &set_color_as_default); |
| + } else if (ppdFindOption(ppd, printing::kColor)) { |
| + getHPColorSettings( |
| + ppd, &printer_color_space_for_black, &printer_color_space_for_color, |
| + &set_color_as_default); |
| + } else if (ppdFindOption(ppd, kProcessColorModel)) { |
| + getProcessColorModelSettings( |
| + ppd, &printer_color_space_for_black, &printer_color_space_for_color, |
| + &set_color_as_default); |
| + } |
| + ppdClose(ppd); |
| + } |
| + file_util::Delete(ppd_file_path, false); |
| +#elif defined(OS_WIN) |
| + // According to XPS 1.0 spec, only color printers have psk:Color. |
| + // Therefore we don't need to parse the whole XML file, we just need to |
| + // search the string. The spec can be found at: |
| + // http://msdn.microsoft.com/en-us/windows/hardware/gg463431. |
| + if (printer_info.printer_capabilities.find(kPskColor) != std::string::npos) |
| + printer_color_space_for_color = printing::COLOR; |
| + |
| + if ((printer_info.printer_capabilities.find(kPskGray) != |
| + std::string::npos) || |
| + (printer_info.printer_capabilities.find(kPskMonochrome) != |
| + std::string::npos)) { |
| + printer_color_space_for_black = printing::GRAY; |
| + } |
| + |
| + set_duplex_as_default = |
| + (printer_info.printer_defaults.find(kPskDuplexFeature) != |
| + std::string::npos) && |
| + (printer_info.printer_defaults.find(kPskTwoSided) != |
| + std::string::npos); |
| + |
| + if (printer_info.printer_defaults.find(kPskDuplexFeature) != |
| + std::string::npos) { |
| + if (printer_info.printer_defaults.find(kPskTwoSided) != |
| + std::string::npos) { |
| + default_duplex_setting_value = printing::LONG_EDGE; |
| + } else { |
| + default_duplex_setting_value = printing::SIMPLEX; |
| + } |
| + } |
| + set_color_as_default = |
|
vandebo (ex-Chrome)
2011/10/11 19:11:42
move with the other color code.
kmadhusu
2011/10/11 21:36:03
Done.
|
| + (printer_info.printer_defaults.find(kPskColor) != std::string::npos); |
| +#else |
| + NOTIMPLEMENTED(); |
| +#endif |
| + disable_color_options = !printer_color_space_for_color || |
| + !printer_color_space_for_black || |
| + (printer_color_space_for_color == |
| + printer_color_space_for_black); |
| + |
| + DictionaryValue settings_info; |
| + settings_info.SetBoolean(kDisableColorOption, disable_color_options); |
| + if (printer_color_space_for_color == printing::UNKNOWN_COLOR_MODEL) |
| + printer_color_space_for_color = printing::COLOR; |
| + |
| + if (printer_color_space_for_black == printing::UNKNOWN_COLOR_MODEL) |
| + printer_color_space_for_black = printing::GRAY; |
| + |
| + if (disable_color_options || |
| + PrintPreviewHandler::last_used_color_model_ == |
| + printing::UNKNOWN_COLOR_MODEL) { |
| + settings_info.SetBoolean(kSetColorAsDefault, set_color_as_default); |
| + } else { |
| + settings_info.SetBoolean(kSetColorAsDefault, |
| + printing::isColorModelSelected( |
| + PrintPreviewHandler::last_used_color_model_)); |
| + } |
| + |
| + settings_info.SetBoolean(kSetDuplexAsDefault, set_duplex_as_default); |
| + settings_info.SetInteger(kPrinterColorModelForColor, |
| + printer_color_space_for_color); |
| + settings_info.SetInteger(kPrinterColorModelForBlack, |
| + printer_color_space_for_black); |
| + settings_info.SetInteger(kPrinterDefaultDuplexValue, |
| + default_duplex_setting_value); |
| + BrowserThread::PostTask( |
| + BrowserThread::UI, FROM_HERE, |
| + base::Bind(&PrintSystemTaskProxy::SendPrinterCapabilities, this, |
| + settings_info.DeepCopy())); |
| +} |
| + |
| +void PrintSystemTaskProxy::SendPrinterCapabilities( |
| + DictionaryValue* settings_info) { |
| + if (handler_) |
| + handler_->SendPrinterCapabilities(*settings_info); |
| + delete settings_info; |
| +} |