0

Get semantic capabilities from Print Backend.

Moved OS specific code into OS specific print backends (vs. print_system_task_proxy.cc).
On Windows get capabilities using native API (vs. XPS parsing)
Some clean up on code and interfaces for Print Backend.

BUG=144031
TEST=Verify Print Preview on ALL platforms (Win XP, Win 7, Linux, Mac).

Review URL: https://chromiumcodereview.appspot.com/10905006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@155310 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
gene@chromium.org
2012-09-07 04:21:21 +00:00
parent 58df36ac04
commit 8b6098e412
13 changed files with 697 additions and 760 deletions

@ -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,

@ -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);
}

@ -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(

@ -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

@ -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

@ -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_

@ -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);
}

@ -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() {}

@ -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,

@ -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,

@ -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) {

@ -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) {

@ -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',