diff options
-rw-r--r-- | chrome/browser/printing/print_system_task_proxy.cc | 445 | ||||
-rw-r--r-- | chrome/browser/printing/print_system_task_proxy_unittest.cc | 321 | ||||
-rw-r--r-- | chrome/browser/ui/webui/print_preview/print_preview_handler.cc | 11 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 8 | ||||
-rw-r--r-- | printing/backend/cups_helper.cc | 344 | ||||
-rw-r--r-- | printing/backend/cups_helper.h | 11 | ||||
-rw-r--r-- | printing/backend/cups_helper_unittest.cc | 159 | ||||
-rw-r--r-- | printing/backend/print_backend.cc | 8 | ||||
-rw-r--r-- | printing/backend/print_backend.h | 22 | ||||
-rw-r--r-- | printing/backend/print_backend_chromeos.cc | 10 | ||||
-rw-r--r-- | printing/backend/print_backend_cups.cc | 14 | ||||
-rw-r--r-- | printing/backend/print_backend_win.cc | 96 | ||||
-rw-r--r-- | printing/printing.gyp | 8 |
13 files changed, 697 insertions, 760 deletions
diff --git a/chrome/browser/printing/print_system_task_proxy.cc b/chrome/browser/printing/print_system_task_proxy.cc index 2bd9744..16f6e54 100644 --- a/chrome/browser/printing/print_system_task_proxy.cc +++ b/chrome/browser/printing/print_system_task_proxy.cc @@ -7,12 +7,9 @@ #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 "chrome/browser/ui/webui/print_preview/print_preview_handler.h" #include "chrome/common/child_process_logging.h" @@ -23,93 +20,6 @@ #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 using content::BrowserThread; @@ -123,185 +33,6 @@ const char kPrinterColorModelForBlack[] = "printerColorModelForBlack"; const char kPrinterColorModelForColor[] = "printerColorModelForColor"; const char kPrinterDefaultDuplexValue[] = "printerDefaultDuplexValue"; -#if defined(OS_WIN) -const char kPskColor[] = "psk:Color"; -const char kPskGray[] = "psk:Grayscale"; -const char kPskMonochrome[] = "psk:Monochrome"; -const char kPskDuplexFeature[] = "psk:JobDuplexAllDocumentsContiguously"; -const char kPskTwoSided[] = "psk:TwoSided"; -#elif 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"; - -bool 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 (!color_model) - return false; - - 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; - else if (ppdFindChoice(color_model, printing::kGrayscale)) - *color_model_for_black = printing::GRAYSCALE; - - 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); - } - return true; -} - -bool 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); - if (!printout_mode) - return false; - - *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; - } - } - return true; -} - -bool 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 (!color_mode_option) - return false; - - 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); - } - return true; -} - -bool 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 (!color_mode_option) - return false; - - 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); - } - return true; -} - -bool 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 (!color_mode_option) - return false; - - 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); - } - return true; -} -#endif - } // namespace PrintSystemTaskProxy::PrintSystemTaskProxy( @@ -378,121 +109,6 @@ void PrintSystemTaskProxy::SetupPrinterList(ListValue* printers) { delete printers; } -bool PrintSystemTaskProxy::ParsePrinterCapabilities( - const printing::PrinterCapsAndDefaults& printer_info, - const std::string& printer_name, - bool* set_color_as_default, - int* printer_color_space_for_color, - int* printer_color_space_for_black, - bool* set_duplex_as_default, - int* default_duplex_setting_value) { -#if defined(USE_CUPS) - FilePath ppd_file_path; - if (!file_util::CreateTemporaryFile(&ppd_file_path)) - return false; - - 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 false; - } - - 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 - 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; - } - } - - bool is_color_device = false; - ppd_attr_t* attr = ppdFindAttr(ppd, kColorDevice, NULL); - if (attr && attr->value) - is_color_device = ppd->color_device; - *set_color_as_default = is_color_device; - - if (!((is_color_device && getBasicColorModelSettings( - ppd, printer_color_space_for_black, - printer_color_space_for_color, set_color_as_default)) || - getPrintOutModeColorSettings( - ppd, printer_color_space_for_black, - printer_color_space_for_color, set_color_as_default) || - getColorModeSettings( - ppd, printer_color_space_for_black, - printer_color_space_for_color, set_color_as_default) || - getHPColorSettings( - ppd, printer_color_space_for_black, - printer_color_space_for_color, set_color_as_default) || - getProcessColorModelSettings( - ppd, printer_color_space_for_black, - printer_color_space_for_color, set_color_as_default))) { - VLOG(1) << "Unknown printer color model"; - } - ppdClose(ppd); - } - file_util::Delete(ppd_file_path, false); - return true; - -#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_color_as_default = - (printer_info.printer_defaults.find(kPskColor) != std::string::npos); - - *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; - } - } - return true; - -#else - - NOTIMPLEMENTED(); - return false; - -#endif // defined(OS_WIN) -} - - void PrintSystemTaskProxy::GetPrinterCapabilities( const std::string& printer_name) { VLOG(1) << "Get printer capabilities start for " << printer_name; @@ -500,6 +116,8 @@ void PrintSystemTaskProxy::GetPrinterCapabilities( print_backend_->GetPrinterDriverInfo(printer_name)); if (!print_backend_->IsValidPrinter(printer_name)) { + // TODO(gene): Notify explicitly if printer is not valid, instead of + // failed to get capabilities. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&PrintSystemTaskProxy::SendFailedToGetPrinterCapabilities, @@ -507,48 +125,35 @@ void PrintSystemTaskProxy::GetPrinterCapabilities( return; } - bool set_color_as_default = false; - 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; - bool disable_color_options = false; - - printing::PrinterCapsAndDefaults info; - if (print_backend_->GetPrinterCapsAndDefaults(printer_name, &info) && - ParsePrinterCapabilities(info, - printer_name, - &set_color_as_default, - &printer_color_space_for_color, - &printer_color_space_for_black, - &set_duplex_as_default, - &default_duplex_setting_value)) { - disable_color_options = (!printer_color_space_for_color || - !printer_color_space_for_black || - (printer_color_space_for_color == - printer_color_space_for_black)); - } else { + printing::PrinterSemanticCapsAndDefaults info; + if (!print_backend_->GetPrinterSemanticCapsAndDefaults(printer_name, &info)) { VLOG(1) << "Failed to get capabilities for " << printer_name; + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&PrintSystemTaskProxy::SendFailedToGetPrinterCapabilities, + this, printer_name)); + return; } DictionaryValue settings_info; settings_info.SetString(kPrinterId, printer_name); - 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; - + settings_info.SetBoolean(kDisableColorOption, !info.color_capable); settings_info.SetBoolean(printing::kSettingSetColorAsDefault, - set_color_as_default); - 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); + info.color_default); + // TODO(gene): Make new capabilities format for Print Preview + // that will suit semantic capabiltities better. + // Refactor pld API code below + if (info.duplex_capable) { + settings_info.SetBoolean(kSetDuplexAsDefault, + info.duplex_default != printing::SIMPLEX); + settings_info.SetInteger(kPrinterDefaultDuplexValue, + printing::LONG_EDGE); + } else { + settings_info.SetBoolean(kSetDuplexAsDefault, false); + settings_info.SetInteger(kPrinterDefaultDuplexValue, + printing::UNKNOWN_DUPLEX_MODE); + } + BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&PrintSystemTaskProxy::SendPrinterCapabilities, this, diff --git a/chrome/browser/printing/print_system_task_proxy_unittest.cc b/chrome/browser/printing/print_system_task_proxy_unittest.cc deleted file mode 100644 index cf82631..0000000 --- a/chrome/browser/printing/print_system_task_proxy_unittest.cc +++ /dev/null @@ -1,321 +0,0 @@ -// Copyright (c) 2012 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 <cups/cups.h> -#include <cups/ppd.h> - -#include <cstring> -#include <string> -#include <vector> - -#include "base/file_path.h" -#include "base/file_util.h" -#include "base/message_loop.h" -#include "base/scoped_temp_dir.h" -#include "chrome/browser/printing/print_system_task_proxy.h" -#include "content/public/test/test_browser_thread.h" -#include "printing/backend/print_backend.h" -#include "printing/print_job_constants.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace { - -#if !defined(OS_MACOSX) - -// TestEntry stores the printer name and the expected number of options. -struct TestEntry { - TestEntry(std::string name, int count) - : printer_name(name), - expected_option_count(count) {} - - std::string printer_name; - int expected_option_count; -}; - -// Verify the option marked in |ppd|. -void verifyOptionValue(ppd_file_t* ppd, - const std::string& option_name, - const std::string& expected_choice_value) { - ppd_choice_t* option_choice = ppdFindMarkedChoice(ppd, option_name.c_str()); - if (option_choice == NULL) { - ppd_option_t* option = ppdFindOption(ppd, option_name.c_str()); - if (option != NULL) - option_choice = ppdFindChoice(option, option->defchoice); - } - ASSERT_TRUE(option_choice); - EXPECT_EQ(strcmp(option_choice->choice, expected_choice_value.c_str()), 0); -} - -#endif // !defined(OS_MACOSX) - -class PrintSystemTaskProxyTest : public testing::Test { - public: - PrintSystemTaskProxyTest() - : loop_(MessageLoop::TYPE_UI), - ui_thread_(content::BrowserThread::UI, &loop_) { - } - - protected: - virtual void TearDown() OVERRIDE { - MessageLoop::current()->RunAllPending(); - } - - private: - MessageLoop loop_; - content::TestBrowserThread ui_thread_; -}; - -} // namespace - -#if !defined(OS_MACOSX) - -using printing_internal::parse_lpoptions; - - - -// Test to verify that lpoption custom settings are marked on the ppd file. -TEST_F(PrintSystemTaskProxyTest, MarkLpoptionsInPPD) { - const std::string kColorModel = "ColorModel"; - const std::string kBlack = "Black"; - const std::string kGray = "Gray"; - - const std::string kDuplex = "Duplex"; - const std::string kDuplexNone = "None"; - const std::string kDuplexNoTumble = "DuplexNoTumble"; - const std::string kDuplexTumble = "DuplexTumble"; - const std::string kTestPrinterName = "printerE"; - - ScopedTempDir temp_directory; - ASSERT_TRUE(temp_directory.CreateUniqueTempDir()); - - std::string system_lpoptions; // Specifies the system lpoption data. - system_lpoptions.append("Dest printerE ColorModel=Black Duplex="); - system_lpoptions.append(kDuplexNone+" "); - - // Create and write the system lpoptions to a temp file. - FilePath system_lp_options_file; - ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_directory.path(), - &system_lp_options_file)); - ASSERT_TRUE(file_util::WriteFile(system_lp_options_file, - system_lpoptions.c_str(), - system_lpoptions.size())); - - // Specifies the user lpoption data. - std::string user_lpoptions; - user_lpoptions.append("Dest printerE Duplex="+kDuplexNoTumble+"\n"); - - // Create and write the user lpoptions to a temp file. - FilePath user_lp_options_file; - ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_directory.path(), - &user_lp_options_file)); - ASSERT_TRUE(file_util::WriteFile(user_lp_options_file, user_lpoptions.c_str(), - user_lpoptions.size())); - // Specifies the test ppd data. - std::string test_ppd_data; - test_ppd_data.append("*PPD-Adobe: \"4.3\"\n\n" - "*OpenGroup: General/General\n\n" - "*OpenUI *ColorModel/Color Model: PickOne\n" - "*DefaultColorModel: Gray\n" - "*ColorModel Gray/Grayscale: \"" - "<</cupsColorSpace 0/cupsColorOrder 0>>" - "setpagedevice\"\n" - "*ColorModel Black/Inverted Grayscale: \"" - "<</cupsColorSpace 3/cupsColorOrder 0>>" - "setpagedevice\"\n" - "*CloseUI: *ColorModel\n" - "*OpenUI *Duplex/2-Sided Printing: PickOne\n" - "*DefaultDuplex: DuplexTumble\n" - "*Duplex None/Off: \"<</Duplex false>>" - "setpagedevice\"\n" - "*Duplex DuplexNoTumble/LongEdge: \"" - "<</Duplex true/Tumble false>>setpagedevice\"\n" - "*Duplex DuplexTumble/ShortEdge: \"" - "<</Duplex true/Tumble true>>setpagedevice\"\n" - "*CloseUI: *Duplex\n\n" - "*CloseGroup: General\n"); - - // Create a test ppd file. - FilePath ppd_file_path; - ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_directory.path(), - &ppd_file_path)); - ASSERT_TRUE(file_util::WriteFile(ppd_file_path, test_ppd_data.c_str(), - test_ppd_data.size())); - - ppd_file_t* ppd = ppdOpenFile(ppd_file_path.value().c_str()); - ASSERT_TRUE(ppd); - ppdMarkDefaults(ppd); - - // Verify the default settings. - verifyOptionValue(ppd, kDuplex, kDuplexTumble); - verifyOptionValue(ppd, kColorModel, kGray); - - // Parse the system lpoptions data. - int num_options = 0; - cups_option_t* options = NULL; - parse_lpoptions(system_lp_options_file, kTestPrinterName, &num_options, - &options); - ASSERT_EQ(num_options, 2); - EXPECT_EQ(num_options != 0, options != NULL); - cupsMarkOptions(ppd, num_options, options); - cupsFreeOptions(num_options, options); - - // Verify that the settings are updated as per system lpoptions. - verifyOptionValue(ppd, kDuplex, kDuplexNone); - verifyOptionValue(ppd, kColorModel, kBlack); - - // Parse the user lpoptions data. - num_options = 0; - options = NULL; - parse_lpoptions(user_lp_options_file, kTestPrinterName, &num_options, - &options); - ASSERT_EQ(num_options, 1); - EXPECT_EQ(num_options != 0, options != NULL); - cupsMarkOptions(ppd, num_options, options); - cupsFreeOptions(num_options, options); - - // Verify that the settings are updated as per user lpoptions. Make sure - // duplex setting is updated but the color setting remains the same. - verifyOptionValue(ppd, kDuplex, kDuplexNoTumble); - verifyOptionValue(ppd, kColorModel, kBlack); - ppdClose(ppd); -} - -// Test the lpoption parsing code. -TEST_F(PrintSystemTaskProxyTest, ParseLpoptionData) { - // Specifies the user lpoption data. - std::string user_lpoptions; - - // Printer A default printer settings. - user_lpoptions.append("Default printerA Duplex=None landscape=true "); - user_lpoptions.append("media=A4 Collate=True sides=two-sided-long-edge "); - user_lpoptions.append("ColorModel=Color nUp=7\n"); - - // PrinterB custom settings. - user_lpoptions.append("Dest printerB Duplex=None scaling=98 "); - user_lpoptions.append("landscape=true media=A4 collate=True\n"); - - // PrinterC has a invalid key and value but the format is valid. - user_lpoptions.append("Dest printerC invalidKey1=invalidValue1 "); - user_lpoptions.append("invalidKey2=invalidValue2 "); - user_lpoptions.append("invalidKey3=invalidValue3 \n"); - - // PrinterA instance custom settings. These settings will not override - // PrinterA settings. - user_lpoptions.append("Dest printerA/instanceA "); - user_lpoptions.append("scaling=33 Duplex=DuplexTumble landscape=true\n"); - - // PrinterD custom settings but the format is invalid because of the tab key - // delimiter. - user_lpoptions.append("Dest printerD\tDuplex=DuplexNoTumble\n"); - - // PrinterE custom settings. - user_lpoptions.append("Dest printerE Duplex=DuplexNoTumble\n"); - - ScopedTempDir temp_directory; - ASSERT_TRUE(temp_directory.CreateUniqueTempDir()); - - // Create and write the user lpoptions to a temp file. - FilePath userLpOptionsFile; - ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_directory.path(), - &userLpOptionsFile)); - ASSERT_TRUE(file_util::WriteFile(userLpOptionsFile, user_lpoptions.c_str(), - user_lpoptions.size())); - std::vector<TestEntry> test_cases; - test_cases.push_back( - TestEntry("printerA", 7)); // Parse generic printer. - test_cases.push_back( - TestEntry("printerB", 5)); // Valid printer info. - test_cases.push_back( - TestEntry("printerC", 3)); // Invalid settings found. - test_cases.push_back( - TestEntry("printerD", 0)); // Tab key delimiter used. - test_cases.push_back( - TestEntry("printerE", 1)); // user specified custom settings. - test_cases.push_back( - TestEntry("printerF", 0)); // Custom settings not found. - - // Parse the lpoptions for each printer. Parse the system file followed by the - // user file. Ordering is important. - int num_options; - cups_option_t* options; - for (std::vector<TestEntry>::iterator it = test_cases.begin(); - it != test_cases.end(); ++it) { - num_options = 0; - options = NULL; - printing_internal::parse_lpoptions(userLpOptionsFile, it->printer_name, - &num_options, &options); - ASSERT_EQ(num_options, it->expected_option_count); - EXPECT_EQ(num_options != 0, options != NULL); - cupsFreeOptions(num_options, options); - } -} -#endif // !defined(OS_MACOSX) - -// Test duplex detection code, which regressed in http://crbug.com/103999. -TEST_F(PrintSystemTaskProxyTest, DetectDuplexModeCUPS) { - // Specifies the test ppd data. - printing::PrinterCapsAndDefaults printer_info; - printer_info.printer_capabilities.append( - "*PPD-Adobe: \"4.3\"\n\n" - "*OpenGroup: General/General\n\n" - "*OpenUI *Duplex/Double-Sided Printing: PickOne\n" - "*DefaultDuplex: None\n" - "*Duplex None/Off: " - "\"<</Duplex false>>setpagedevice\"\n" - "*Duplex DuplexNoTumble/Long Edge (Standard): " - "\"<</Duplex true/Tumble false>>setpagedevice\"\n" - "*Duplex DuplexTumble/Short Edge (Flip): " - "\"<</Duplex true/Tumble true>>setpagedevice\"\n" - "*CloseUI: *Duplex\n\n" - "*CloseGroup: General\n"); - - bool set_color_as_default = false; - 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; - - scoped_refptr<PrintSystemTaskProxy> proxy( - new PrintSystemTaskProxy(base::WeakPtr<PrintPreviewHandler>(), NULL, - false)); - ASSERT_TRUE(proxy->ParsePrinterCapabilities( - printer_info, - "InvalidPrinter", - &set_color_as_default, - &printer_color_space_for_color, - &printer_color_space_for_black, - &set_duplex_as_default, - &default_duplex_setting_value)); - EXPECT_FALSE(set_duplex_as_default); - EXPECT_EQ(printing::SIMPLEX, default_duplex_setting_value); -} - -TEST_F(PrintSystemTaskProxyTest, DetectNoDuplexModeCUPS) { - // Specifies the test ppd data. - printing::PrinterCapsAndDefaults printer_info; - printer_info.printer_capabilities.append( - "*PPD-Adobe: \"4.3\"\n\n" - "*OpenGroup: General/General\n\n" - "*CloseGroup: General\n"); - - bool set_color_as_default = false; - 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; - - scoped_refptr<PrintSystemTaskProxy> proxy( - new PrintSystemTaskProxy(base::WeakPtr<PrintPreviewHandler>(), NULL, - false)); - ASSERT_TRUE(proxy->ParsePrinterCapabilities( - printer_info, - "InvalidPrinter", - &set_color_as_default, - &printer_color_space_for_color, - &printer_color_space_for_black, - &set_duplex_as_default, - &default_duplex_setting_value)); - EXPECT_FALSE(set_duplex_as_default); - EXPECT_EQ(printing::UNKNOWN_DUPLEX_MODE, default_duplex_setting_value); -} diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc index 0c1d60a..26f8239 100644 --- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc +++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc @@ -7,7 +7,6 @@ #include <ctype.h> #include <string> -#include <vector> #include "base/base64.h" #include "base/bind.h" @@ -763,16 +762,8 @@ void PrintPreviewHandler::ActivateInitiatorTabAndClosePreviewTab() { void PrintPreviewHandler::SendPrinterCapabilities( const DictionaryValue& settings_info) { VLOG(1) << "Get printer capabilities finished"; - // Copy so we can override with sticky values. - scoped_ptr<DictionaryValue> settings(settings_info.DeepCopy()); - if (GetStickySettings()->color_model() != printing::UNKNOWN_COLOR_MODEL) { - settings->SetBoolean( - printing::kSettingSetColorAsDefault, - printing::isColorModelSelected( - GetStickySettings()->color_model())); - } web_ui()->CallJavascriptFunction("updateWithPrinterCapabilities", - *settings); + settings_info); } void PrintPreviewHandler::SendFailedToGetPrinterCapabilities( diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index b297f33..64f443f 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -2605,14 +2605,6 @@ ['exclude', '^browser/importer/'], ], }], - ['use_cups==1', { - 'defines': [ - 'USE_CUPS', - ], - 'sources': [ - 'browser/printing/print_system_task_proxy_unittest.cc', - ], - }], ['component=="shared_library" and incremental_chrome_dll!=1', { # This is needed for tests that subclass # RendererWebKitPlatformSupportImpl, which subclasses stuff in diff --git a/printing/backend/cups_helper.cc b/printing/backend/cups_helper.cc index 7fc6983..c2251c6 100644 --- a/printing/backend/cups_helper.cc +++ b/printing/backend/cups_helper.cc @@ -4,8 +4,296 @@ #include "printing/backend/cups_helper.h" +#include "base/file_util.h" #include "base/logging.h" +#include "base/string_number_conversions.h" +#include "base/string_split.h" +#include "base/string_util.h" +#include "base/values.h" #include "googleurl/src/gurl.h" +#include "printing/backend/print_backend.h" +#include "printing/backend/print_backend_consts.h" + +// This section contains helper code for PPD parsing for semantic capabilities. +namespace { + +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"; + +#if !defined(OS_MACOSX) +void ParseLpOptions(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"; + const size_t kDestLen = sizeof(kDest) - 1; + const 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 MarkLpOptions(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; + ParseLpOptions(*it, printer_name, &num_options, &options); + if (num_options > 0 && options) { + cupsMarkOptions(*ppd, num_options, options); + cupsFreeOptions(num_options, options); + } + } +} +#endif // !defined(OS_MACOSX) + +bool 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 (!color_model) + return false; + + 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; + else if (ppdFindChoice(color_model, printing::kGrayscale)) + *color_model_for_black = printing::GRAYSCALE; + + 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) && + (base::strcasecmp(marked_choice->choice, printing::kGrayscale) != 0); + } + return true; +} + +bool 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); + if (!printout_mode) + return false; + + *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; + } + } + return true; +} + +bool 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 (!color_mode_option) + return false; + + 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); + } + return true; +} + +bool 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 (!color_mode_option) + return false; + + 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); + } + return true; +} + +bool 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 (!color_mode_option) + return false; + + 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); + } + return true; +} + +bool GetColorModelSettings(ppd_file_t* ppd, + int* cm_black, + int* cm_color, + bool* is_color) { + bool is_color_device = false; + ppd_attr_t* attr = ppdFindAttr(ppd, kColorDevice, NULL); + if (attr && attr->value) + is_color_device = ppd->color_device; + + *is_color = is_color_device; + return (is_color_device && + GetBasicColorModelSettings(ppd, cm_black, cm_color, is_color)) || + GetPrintOutModeColorSettings(ppd, cm_black, cm_color, is_color) || + GetColorModeSettings(ppd, cm_black, cm_color, is_color) || + GetHPColorSettings(ppd, cm_black, cm_color, is_color) || + GetProcessColorModelSettings(ppd, cm_black, cm_color, is_color); +} + +} // namespace namespace printing { @@ -46,4 +334,60 @@ http_t* HttpConnectionCUPS::http() { return http_; } +bool parsePpdCapabilities( + const std::string& printer_name, + const std::string& printer_capabilities, + PrinterSemanticCapsAndDefaults* printer_info) { + FilePath ppd_file_path; + if (!file_util::CreateTemporaryFile(&ppd_file_path)) + return false; + + int data_size = printer_capabilities.length(); + if (data_size != file_util::WriteFile( + ppd_file_path, + printer_capabilities.data(), + data_size)) { + file_util::Delete(ppd_file_path, false); + return false; + } + + ppd_file_t* ppd = ppdOpenFile(ppd_file_path.value().c_str()); + if (!ppd) + return false; + + printing::PrinterSemanticCapsAndDefaults caps; +#if !defined(OS_MACOSX) + MarkLpOptions(printer_name, &ppd); +#endif + 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) { + caps.duplex_capable = true; + if (base::strcasecmp(duplex_choice->choice, kDuplexNone) != 0) + caps.duplex_default = printing::LONG_EDGE; + else + caps.duplex_default = printing::SIMPLEX; + } + + bool is_color = false; + int cm_color = 0, cm_black = 0; + if (!GetColorModelSettings(ppd, &cm_black, &cm_color, &is_color)) { + VLOG(1) << "Unknown printer color model"; + } + + caps.color_capable = (cm_color && cm_black && (cm_color != cm_black)); + caps.color_default = is_color; + + ppdClose(ppd); + file_util::Delete(ppd_file_path, false); + + *printer_info = caps; + return true; +} + } // namespace printing diff --git a/printing/backend/cups_helper.h b/printing/backend/cups_helper.h index 43907bd..04ff08a 100644 --- a/printing/backend/cups_helper.h +++ b/printing/backend/cups_helper.h @@ -7,6 +7,8 @@ #include <cups/cups.h> +#include <string> + #include "printing/printing_export.h" class GURL; @@ -14,6 +16,8 @@ class GURL; // These are helper functions for dealing with CUPS. namespace printing { +struct PrinterSemanticCapsAndDefaults; + // Helper wrapper around http_t structure, with connection and cleanup // functionality. class PRINTING_EXPORT HttpConnectionCUPS { @@ -30,6 +34,13 @@ class PRINTING_EXPORT HttpConnectionCUPS { http_t* http_; }; +// Helper function to parse and convert PPD capabilitites to +// semantic options. +PRINTING_EXPORT bool parsePpdCapabilities( + const std::string& printer_name, + const std::string& printer_capabilities, + PrinterSemanticCapsAndDefaults* printer_info); + } // namespace printing #endif // PRINTING_BACKEND_CUPS_HELPER_H_ diff --git a/printing/backend/cups_helper_unittest.cc b/printing/backend/cups_helper_unittest.cc new file mode 100644 index 0000000..d83b3b7 --- /dev/null +++ b/printing/backend/cups_helper_unittest.cc @@ -0,0 +1,159 @@ +// Copyright (c) 2012 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 "printing/backend/cups_helper.h" +#include "printing/backend/print_backend.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(PrintBackendCupsHelperTest, TestPpdParsingNoColorDuplexLongEdge) { + std::string test_ppd_data; + test_ppd_data.append( + "*PPD-Adobe: \"4.3\"\n\n" + "*OpenGroup: General/General\n\n" + "*OpenUI *ColorModel/Color Model: PickOne\n" + "*DefaultColorModel: Gray\n" + "*ColorModel Gray/Grayscale: \"" + "<</cupsColorSpace 0/cupsColorOrder 0>>" + "setpagedevice\"\n" + "*ColorModel Black/Inverted Grayscale: \"" + "<</cupsColorSpace 3/cupsColorOrder 0>>" + "setpagedevice\"\n" + "*CloseUI: *ColorModel\n" + "*OpenUI *Duplex/2-Sided Printing: PickOne\n" + "*DefaultDuplex: DuplexTumble\n" + "*Duplex None/Off: \"<</Duplex false>>" + "setpagedevice\"\n" + "*Duplex DuplexNoTumble/LongEdge: \"" + "<</Duplex true/Tumble false>>setpagedevice\"\n" + "*Duplex DuplexTumble/ShortEdge: \"" + "<</Duplex true/Tumble true>>setpagedevice\"\n" + "*CloseUI: *Duplex\n\n" + "*CloseGroup: General\n"); + + printing::PrinterSemanticCapsAndDefaults caps; + EXPECT_TRUE(printing::parsePpdCapabilities("test", test_ppd_data, &caps)); + EXPECT_FALSE(caps.color_capable); + EXPECT_FALSE(caps.color_default); + EXPECT_TRUE(caps.duplex_capable); + EXPECT_EQ(caps.duplex_default, printing::LONG_EDGE); +} + +// Test duplex detection code, which regressed in http://crbug.com/103999. +TEST(PrintBackendCupsHelperTest, TestPpdParsingNoColorDuplexSimples) { + std::string test_ppd_data; + test_ppd_data.append( + "*PPD-Adobe: \"4.3\"\n\n" + "*OpenGroup: General/General\n\n" + "*OpenUI *Duplex/Double-Sided Printing: PickOne\n" + "*DefaultDuplex: None\n" + "*Duplex None/Off: " + "\"<</Duplex false>>setpagedevice\"\n" + "*Duplex DuplexNoTumble/Long Edge (Standard): " + "\"<</Duplex true/Tumble false>>setpagedevice\"\n" + "*Duplex DuplexTumble/Short Edge (Flip): " + "\"<</Duplex true/Tumble true>>setpagedevice\"\n" + "*CloseUI: *Duplex\n\n" + "*CloseGroup: General\n"); + + printing::PrinterSemanticCapsAndDefaults caps; + EXPECT_TRUE(printing::parsePpdCapabilities("test", test_ppd_data, &caps)); + EXPECT_FALSE(caps.color_capable); + EXPECT_FALSE(caps.color_default); + EXPECT_TRUE(caps.duplex_capable); + EXPECT_EQ(caps.duplex_default, printing::SIMPLEX); +} + +TEST(PrintBackendCupsHelperTest, TestPpdParsingNoColorNoDuplex) { + std::string test_ppd_data; + test_ppd_data.append( + "*PPD-Adobe: \"4.3\"\n\n" + "*OpenGroup: General/General\n\n" + "*OpenUI *ColorModel/Color Model: PickOne\n" + "*DefaultColorModel: Gray\n" + "*ColorModel Gray/Grayscale: \"" + "<</cupsColorSpace 0/cupsColorOrder 0>>" + "setpagedevice\"\n" + "*ColorModel Black/Inverted Grayscale: \"" + "<</cupsColorSpace 3/cupsColorOrder 0>>" + "setpagedevice\"\n" + "*CloseUI: *ColorModel\n" + "*CloseGroup: General\n"); + + printing::PrinterSemanticCapsAndDefaults caps; + EXPECT_TRUE(printing::parsePpdCapabilities("test", test_ppd_data, &caps)); + EXPECT_FALSE(caps.color_capable); + EXPECT_FALSE(caps.color_default); + EXPECT_FALSE(caps.duplex_capable); + EXPECT_EQ(caps.duplex_default, printing::UNKNOWN_DUPLEX_MODE); +} + +TEST(PrintBackendCupsHelperTest, TestPpdParsingColorTrueDuplexLongEdge) { + std::string test_ppd_data; + test_ppd_data.append( + "*PPD-Adobe: \"4.3\"\n\n" + "*ColorDevice: True\n" + "*DefaultColorSpace: CMYK\n\n" + "*OpenGroup: General/General\n\n" + "*OpenUI *ColorModel/Color Model: PickOne\n" + "*DefaultColorModel: CMYK\n" + "*ColorModel CMYK/Color: " + "\"(cmyk) RCsetdevicecolor\"\n" + "*ColorModel Gray/Black and White: " + "\"(gray) RCsetdevicecolor\"\n" + "*CloseUI: *ColorModel\n" + "*OpenUI *Duplex/2-Sided Printing: PickOne\n" + "*DefaultDuplex: DuplexTumble\n" + "*Duplex None/Off: \"<</Duplex false>>" + "setpagedevice\"\n" + "*Duplex DuplexNoTumble/LongEdge: \"" + "<</Duplex true/Tumble false>>setpagedevice\"\n" + "*Duplex DuplexTumble/ShortEdge: \"" + "<</Duplex true/Tumble true>>setpagedevice\"\n" + "*CloseUI: *Duplex\n\n" + "*CloseGroup: General\n"); + + printing::PrinterSemanticCapsAndDefaults caps; + EXPECT_TRUE(printing::parsePpdCapabilities("test", test_ppd_data, &caps)); + EXPECT_TRUE(caps.color_capable); + EXPECT_TRUE(caps.color_default); + EXPECT_TRUE(caps.duplex_capable); + EXPECT_EQ(caps.duplex_default, printing::LONG_EDGE); +} + +TEST(PrintBackendCupsHelperTest, TestPpdParsingColorFalseDuplexLongEdge) { + std::string test_ppd_data; + test_ppd_data.append( + "*PPD-Adobe: \"4.3\"\n\n" + "*ColorDevice: True\n" + "*DefaultColorSpace: CMYK\n\n" + "*OpenGroup: General/General\n\n" + "*OpenUI *ColorModel/Color Model: PickOne\n" + "*DefaultColorModel: Grayscale\n" + "*ColorModel Color/Color: " + "\"%% FoomaticRIPOptionSetting: ColorModel=Color\"\n" + "*FoomaticRIPOptionSetting ColorModel=Color: " + "\"JCLDatamode=Color GSCmdLine=Color\"\n" + "*ColorModel Grayscale/Grayscale: " + "\"%% FoomaticRIPOptionSetting: ColorModel=Grayscale\"\n" + "*FoomaticRIPOptionSetting ColorModel=Grayscale: " + "\"JCLDatamode=Grayscale GSCmdLine=Grayscale\"\n" + "*CloseUI: *ColorModel\n" + "*OpenUI *Duplex/2-Sided Printing: PickOne\n" + "*DefaultDuplex: DuplexTumble\n" + "*Duplex None/Off: \"<</Duplex false>>" + "setpagedevice\"\n" + "*Duplex DuplexNoTumble/LongEdge: \"" + "<</Duplex true/Tumble false>>setpagedevice\"\n" + "*Duplex DuplexTumble/ShortEdge: \"" + "<</Duplex true/Tumble true>>setpagedevice\"\n" + "*CloseUI: *Duplex\n\n" + "*CloseGroup: General\n"); + + printing::PrinterSemanticCapsAndDefaults caps; + EXPECT_TRUE(printing::parsePpdCapabilities("test", test_ppd_data, &caps)); + EXPECT_TRUE(caps.color_capable); + EXPECT_FALSE(caps.color_default); + EXPECT_TRUE(caps.duplex_capable); + EXPECT_EQ(caps.duplex_default, printing::LONG_EDGE); +} diff --git a/printing/backend/print_backend.cc b/printing/backend/print_backend.cc index f3de089..b993bfc 100644 --- a/printing/backend/print_backend.cc +++ b/printing/backend/print_backend.cc @@ -12,6 +12,14 @@ PrinterBasicInfo::PrinterBasicInfo() PrinterBasicInfo::~PrinterBasicInfo() {} +PrinterSemanticCapsAndDefaults::PrinterSemanticCapsAndDefaults() + : color_capable(false), + duplex_capable(false), + color_default(false), + duplex_default(UNKNOWN_DUPLEX_MODE) {} + +PrinterSemanticCapsAndDefaults::~PrinterSemanticCapsAndDefaults() {} + PrinterCapsAndDefaults::PrinterCapsAndDefaults() {} PrinterCapsAndDefaults::~PrinterCapsAndDefaults() {} diff --git a/printing/backend/print_backend.h b/printing/backend/print_backend.h index dfa4762..3cbf162 100644 --- a/printing/backend/print_backend.h +++ b/printing/backend/print_backend.h @@ -10,6 +10,7 @@ #include <vector> #include "base/memory/ref_counted.h" +#include "printing/print_job_constants.h" #include "printing/printing_export.h" namespace base { @@ -32,6 +33,19 @@ struct PRINTING_EXPORT PrinterBasicInfo { typedef std::vector<PrinterBasicInfo> PrinterList; +struct PRINTING_EXPORT PrinterSemanticCapsAndDefaults { + PrinterSemanticCapsAndDefaults(); + ~PrinterSemanticCapsAndDefaults(); + + // Capabilities. + bool color_capable; + bool duplex_capable; + + // Current defaults. + bool color_default; + DuplexMode duplex_default; +}; + struct PRINTING_EXPORT PrinterCapsAndDefaults { PrinterCapsAndDefaults(); ~PrinterCapsAndDefaults(); @@ -58,6 +72,14 @@ class PRINTING_EXPORT PrintBackend // Get the default printer name. Empty string if no default printer. virtual std::string GetDefaultPrinterName() = 0; + // Gets the semantic capabilities and defaults for a specific printer. + // This is usually a lighter implementation than GetPrinterCapsAndDefaults(). + // NOTE: on some old platforms (WinXP without XPS pack) + // GetPrinterCapsAndDefaults() will fail, while this function will succeed. + virtual bool GetPrinterSemanticCapsAndDefaults( + const std::string& printer_name, + PrinterSemanticCapsAndDefaults* printer_info) = 0; + // Gets the capabilities and defaults for a specific printer. virtual bool GetPrinterCapsAndDefaults( const std::string& printer_name, diff --git a/printing/backend/print_backend_chromeos.cc b/printing/backend/print_backend_chromeos.cc index 003b0fd..c206d51 100644 --- a/printing/backend/print_backend_chromeos.cc +++ b/printing/backend/print_backend_chromeos.cc @@ -16,6 +16,9 @@ class PrintBackendChromeOS : public PrintBackend { // PrintBackend implementation. virtual bool EnumeratePrinters(PrinterList* printer_list) OVERRIDE; virtual std::string GetDefaultPrinterName() OVERRIDE; + virtual bool GetPrinterSemanticCapsAndDefaults( + const std::string& printer_name, + PrinterSemanticCapsAndDefaults* printer_info) OVERRIDE; virtual bool GetPrinterCapsAndDefaults( const std::string& printer_name, PrinterCapsAndDefaults* printer_info) OVERRIDE; @@ -33,7 +36,12 @@ bool PrintBackendChromeOS::EnumeratePrinters(PrinterList* printer_list) { return true; } - +bool PrintBackendChromeOS::GetPrinterSemanticCapsAndDefaults( + const std::string& printer_name, + PrinterSemanticCapsAndDefaults* printer_info) { + NOTREACHED(); + return false; +} bool PrintBackendChromeOS::GetPrinterCapsAndDefaults( const std::string& printer_name, diff --git a/printing/backend/print_backend_cups.cc b/printing/backend/print_backend_cups.cc index d0d8abb..5c515f16 100644 --- a/printing/backend/print_backend_cups.cc +++ b/printing/backend/print_backend_cups.cc @@ -108,6 +108,9 @@ class PrintBackendCUPS : public PrintBackend { // PrintBackend implementation. virtual bool EnumeratePrinters(PrinterList* printer_list) OVERRIDE; virtual std::string GetDefaultPrinterName() OVERRIDE; + virtual bool GetPrinterSemanticCapsAndDefaults( + const std::string& printer_name, + PrinterSemanticCapsAndDefaults* printer_info) OVERRIDE; virtual bool GetPrinterCapsAndDefaults( const std::string& printer_name, PrinterCapsAndDefaults* printer_info) OVERRIDE; @@ -210,6 +213,17 @@ std::string PrintBackendCUPS::GetDefaultPrinterName() { return dest ? std::string(dest->name) : std::string(); } +bool PrintBackendCUPS::GetPrinterSemanticCapsAndDefaults( + const std::string& printer_name, + PrinterSemanticCapsAndDefaults* printer_info) { + PrinterCapsAndDefaults info; + if (!GetPrinterCapsAndDefaults(printer_name, &info) ) + return false; + + return parsePpdCapabilities( + printer_name, info.printer_capabilities, printer_info); +} + bool PrintBackendCUPS::GetPrinterCapsAndDefaults( const std::string& printer_name, PrinterCapsAndDefaults* printer_info) { diff --git a/printing/backend/print_backend_win.cc b/printing/backend/print_backend_win.cc index 46ea15a..f7f84bf 100644 --- a/printing/backend/print_backend_win.cc +++ b/printing/backend/print_backend_win.cc @@ -18,6 +18,29 @@ namespace { +// This class is designed to work with PRINTER_INFO_X structures +// and calls GetPrinter internally with correctly allocated buffer. +template <typename T> +class PrinterInfo { + public: + bool GetPrinterInfo(HANDLE printer, int level) { + DWORD buf_size = 0; + GetPrinter(printer, level, NULL, 0, &buf_size); + if (buf_size == 0) + return false; + buffer_.reset(new uint8[buf_size]); + memset(buffer_.get(), 0, buf_size); + return !!GetPrinter(printer, level, buffer_.get(), buf_size, &buf_size); + } + + const T* get() const { + return reinterpret_cast<T*>(buffer_.get()); + } + + private: + scoped_array<uint8> buffer_; +}; + HRESULT StreamOnHGlobalToString(IStream* stream, std::string* out) { DCHECK(stream); DCHECK(out); @@ -42,6 +65,9 @@ class PrintBackendWin : public PrintBackend { // PrintBackend implementation. virtual bool EnumeratePrinters(PrinterList* printer_list) OVERRIDE; virtual std::string GetDefaultPrinterName() OVERRIDE; + virtual bool GetPrinterSemanticCapsAndDefaults( + const std::string& printer_name, + PrinterSemanticCapsAndDefaults* printer_info) OVERRIDE; virtual bool GetPrinterCapsAndDefaults( const std::string& printer_name, PrinterCapsAndDefaults* printer_info) OVERRIDE; @@ -93,6 +119,76 @@ std::string PrintBackendWin::GetDefaultPrinterName() { return WideToUTF8(default_printer_name); } +bool PrintBackendWin::GetPrinterSemanticCapsAndDefaults( + const std::string& printer_name, + PrinterSemanticCapsAndDefaults* printer_info) { + ScopedPrinterHandle printer_handle; + OpenPrinter(const_cast<LPTSTR>(UTF8ToWide(printer_name).c_str()), + printer_handle.Receive(), NULL); + DCHECK(printer_handle); + if (!printer_handle.IsValid()) + return false; + + PrinterInfo<PRINTER_INFO_5> info_5; + if (!info_5.GetPrinterInfo(printer_handle, 5)) + return false; + + // Get printer capabilities. For more info see here: + // http://msdn.microsoft.com/en-us/library/windows/desktop/dd183552(v=vs.85).aspx + bool color_supported = (DeviceCapabilities(info_5.get()->pPrinterName, + info_5.get()->pPortName, + DC_COLORDEVICE, + NULL, + NULL) == 1); + + bool duplex_supported = (DeviceCapabilities(info_5.get()->pPrinterName, + info_5.get()->pPortName, + DC_DUPLEX, + NULL, + NULL) == 1); + + // PRINTER_INFO_9 retrieves current user settings. + PrinterInfo<PRINTER_INFO_9> info_9; + if (!info_9.GetPrinterInfo(printer_handle, 9)) + return false; + DEVMODE* devmode = info_9.get()->pDevMode; + + // Sometimes user settings are not available (have not been setted up yet). + // Use printer default settings (PRINTER_INFO_8) in this case. + PrinterInfo<PRINTER_INFO_8> info_8; + if (!devmode) { + if (info_8.GetPrinterInfo(printer_handle, 8)) + devmode = info_8.get()->pDevMode; + } + if (!devmode) + return false; + + PrinterSemanticCapsAndDefaults caps; + caps.color_capable = color_supported; + if ((devmode->dmFields & DM_COLOR) == DM_COLOR) + caps.color_default = (devmode->dmColor == DMCOLOR_COLOR); + + caps.duplex_capable = duplex_supported; + if ((devmode->dmFields & DM_DUPLEX) == DM_DUPLEX) { + switch (devmode->dmDuplex) { + case DMDUP_SIMPLEX: + caps.duplex_default = SIMPLEX; + break; + case DMDUP_VERTICAL: + caps.duplex_default = LONG_EDGE; + break; + case DMDUP_HORIZONTAL: + caps.duplex_default = SHORT_EDGE; + break; + default: + NOTREACHED(); + } + } + + *printer_info = caps; + return true; +} + bool PrintBackendWin::GetPrinterCapsAndDefaults( const std::string& printer_name, PrinterCapsAndDefaults* printer_info) { diff --git a/printing/printing.gyp b/printing/printing.gyp index 6af7b88..89d0d1d 100644 --- a/printing/printing.gyp +++ b/printing/printing.gyp @@ -234,6 +234,14 @@ 'printing_context_win_unittest.cc', ] }], + ['use_cups==1', { + 'defines': [ + 'USE_CUPS', + ], + 'sources': [ + 'backend/cups_helper_unittest.cc', + ], + }], ['toolkit_uses_gtk == 1', { 'dependencies': [ '../build/linux/system.gyp:gtk', |