1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "printing/backend/cups_helper.h"
9 #include "base/file_util.h"
10 #include "base/logging.h"
11 #include "base/string_number_conversions.h"
12 #include "base/string_util.h"
13 #include "base/strings/string_split.h"
14 #include "base/values.h"
15 #include "googleurl/src/gurl.h"
16 #include "printing/backend/print_backend.h"
17 #include "printing/backend/print_backend_consts.h"
19 // This section contains helper code for PPD parsing for semantic capabilities.
22 const char kColorDevice
[] = "ColorDevice";
23 const char kColorModel
[] = "ColorModel";
24 const char kColorMode
[] = "ColorMode";
25 const char kProcessColorModel
[] = "ProcessColorModel";
26 const char kPrintoutMode
[] = "PrintoutMode";
27 const char kDraftGray
[] = "Draft.Gray";
28 const char kHighGray
[] = "High.Gray";
30 const char kDuplex
[] = "Duplex";
31 const char kDuplexNone
[] = "None";
33 #if !defined(OS_MACOSX)
34 void ParseLpOptions(const base::FilePath
& filepath
,
35 const std::string
& printer_name
,
36 int* num_options
, cups_option_t
** options
) {
38 if (!file_util::ReadFileToString(filepath
, &content
))
41 const char kDest
[] = "dest";
42 const char kDefault
[] = "default";
43 const size_t kDestLen
= sizeof(kDest
) - 1;
44 const size_t kDefaultLen
= sizeof(kDefault
) - 1;
45 std::vector
<std::string
> lines
;
46 base::SplitString(content
, '\n', &lines
);
48 for (size_t i
= 0; i
< lines
.size(); ++i
) {
49 std::string line
= lines
[i
];
53 if (base::strncasecmp (line
.c_str(), kDefault
, kDefaultLen
) == 0 &&
54 isspace(line
[kDefaultLen
])) {
55 line
= line
.substr(kDefaultLen
);
56 } else if (base::strncasecmp (line
.c_str(), kDest
, kDestLen
) == 0 &&
57 isspace(line
[kDestLen
])) {
58 line
= line
.substr(kDestLen
);
63 TrimWhitespaceASCII(line
, TRIM_ALL
, &line
);
67 size_t space_found
= line
.find(' ');
68 if (space_found
== std::string::npos
)
71 std::string name
= line
.substr(0, space_found
);
75 if (base::strncasecmp(printer_name
.c_str(), name
.c_str(),
76 name
.length()) != 0) {
77 continue; // This is not the required printer.
80 line
= line
.substr(space_found
+ 1);
81 TrimWhitespaceASCII(line
, TRIM_ALL
, &line
); // Remove extra spaces.
84 // Parse the selected printer custom options.
85 *num_options
= cupsParseOptions(line
.c_str(), 0, options
);
89 void MarkLpOptions(const std::string
& printer_name
, ppd_file_t
** ppd
) {
90 cups_option_t
* options
= NULL
;
92 ppdMarkDefaults(*ppd
);
94 const char kSystemLpOptionPath
[] = "/etc/cups/lpoptions";
95 const char kUserLpOptionPath
[] = ".cups/lpoptions";
97 std::vector
<base::FilePath
> file_locations
;
98 file_locations
.push_back(base::FilePath(kSystemLpOptionPath
));
99 file_locations
.push_back(base::FilePath(
100 file_util::GetHomeDir().Append(kUserLpOptionPath
)));
102 for (std::vector
<base::FilePath
>::const_iterator it
= file_locations
.begin();
103 it
!= file_locations
.end(); ++it
) {
106 ParseLpOptions(*it
, printer_name
, &num_options
, &options
);
107 if (num_options
> 0 && options
) {
108 cupsMarkOptions(*ppd
, num_options
, options
);
109 cupsFreeOptions(num_options
, options
);
113 #endif // !defined(OS_MACOSX)
115 bool GetBasicColorModelSettings(ppd_file_t
* ppd
,
116 int* color_model_for_black
,
117 int* color_model_for_color
,
118 bool* color_is_default
) {
119 ppd_option_t
* color_model
= ppdFindOption(ppd
, kColorModel
);
123 if (ppdFindChoice(color_model
, printing::kBlack
))
124 *color_model_for_black
= printing::BLACK
;
125 else if (ppdFindChoice(color_model
, printing::kGray
))
126 *color_model_for_black
= printing::GRAY
;
127 else if (ppdFindChoice(color_model
, printing::kGrayscale
))
128 *color_model_for_black
= printing::GRAYSCALE
;
130 if (ppdFindChoice(color_model
, printing::kColor
))
131 *color_model_for_color
= printing::COLOR
;
132 else if (ppdFindChoice(color_model
, printing::kCMYK
))
133 *color_model_for_color
= printing::CMYK
;
134 else if (ppdFindChoice(color_model
, printing::kRGB
))
135 *color_model_for_color
= printing::RGB
;
136 else if (ppdFindChoice(color_model
, printing::kRGBA
))
137 *color_model_for_color
= printing::RGBA
;
138 else if (ppdFindChoice(color_model
, printing::kRGB16
))
139 *color_model_for_color
= printing::RGB16
;
140 else if (ppdFindChoice(color_model
, printing::kCMY
))
141 *color_model_for_color
= printing::CMY
;
142 else if (ppdFindChoice(color_model
, printing::kKCMY
))
143 *color_model_for_color
= printing::KCMY
;
144 else if (ppdFindChoice(color_model
, printing::kCMY_K
))
145 *color_model_for_color
= printing::CMY_K
;
147 ppd_choice_t
* marked_choice
= ppdFindMarkedChoice(ppd
, kColorModel
);
149 marked_choice
= ppdFindChoice(color_model
, color_model
->defchoice
);
153 (base::strcasecmp(marked_choice
->choice
, printing::kBlack
) != 0) &&
154 (base::strcasecmp(marked_choice
->choice
, printing::kGray
) != 0) &&
155 (base::strcasecmp(marked_choice
->choice
, printing::kGrayscale
) != 0);
160 bool GetPrintOutModeColorSettings(ppd_file_t
* ppd
,
161 int* color_model_for_black
,
162 int* color_model_for_color
,
163 bool* color_is_default
) {
164 ppd_option_t
* printout_mode
= ppdFindOption(ppd
, kPrintoutMode
);
168 *color_model_for_color
= printing::PRINTOUTMODE_NORMAL
;
169 *color_model_for_black
= printing::PRINTOUTMODE_NORMAL
;
171 // Check to see if NORMAL_GRAY value is supported by PrintoutMode.
172 // If NORMAL_GRAY is not supported, NORMAL value is used to
173 // represent grayscale. If NORMAL_GRAY is supported, NORMAL is used to
175 if (ppdFindChoice(printout_mode
, printing::kNormalGray
))
176 *color_model_for_black
= printing::PRINTOUTMODE_NORMAL_GRAY
;
178 // Get the default marked choice to identify the default color setting
180 ppd_choice_t
* printout_mode_choice
= ppdFindMarkedChoice(ppd
, kPrintoutMode
);
181 if (!printout_mode_choice
) {
182 printout_mode_choice
= ppdFindChoice(printout_mode
,
183 printout_mode
->defchoice
);
185 if (printout_mode_choice
) {
186 if ((base::strcasecmp(printout_mode_choice
->choice
,
187 printing::kNormalGray
) == 0) ||
188 (base::strcasecmp(printout_mode_choice
->choice
, kHighGray
) == 0) ||
189 (base::strcasecmp(printout_mode_choice
->choice
, kDraftGray
) == 0)) {
190 *color_model_for_black
= printing::PRINTOUTMODE_NORMAL_GRAY
;
191 *color_is_default
= false;
197 bool GetColorModeSettings(ppd_file_t
* ppd
,
198 int* color_model_for_black
,
199 int* color_model_for_color
,
200 bool* color_is_default
) {
201 // Samsung printers use "ColorMode" attribute in their ppds.
202 ppd_option_t
* color_mode_option
= ppdFindOption(ppd
, kColorMode
);
203 if (!color_mode_option
)
206 if (ppdFindChoice(color_mode_option
, printing::kColor
))
207 *color_model_for_color
= printing::COLORMODE_COLOR
;
209 if (ppdFindChoice(color_mode_option
, printing::kMonochrome
))
210 *color_model_for_black
= printing::COLORMODE_MONOCHROME
;
212 ppd_choice_t
* mode_choice
= ppdFindMarkedChoice(ppd
, kColorMode
);
214 mode_choice
= ppdFindChoice(color_mode_option
,
215 color_mode_option
->defchoice
);
220 (base::strcasecmp(mode_choice
->choice
, printing::kColor
) == 0);
225 bool GetHPColorSettings(ppd_file_t
* ppd
,
226 int* color_model_for_black
,
227 int* color_model_for_color
,
228 bool* color_is_default
) {
229 // HP printers use "Color/Color Model" attribute in their ppds.
230 ppd_option_t
* color_mode_option
= ppdFindOption(ppd
, printing::kColor
);
231 if (!color_mode_option
)
234 if (ppdFindChoice(color_mode_option
, printing::kColor
))
235 *color_model_for_color
= printing::HP_COLOR_COLOR
;
236 if (ppdFindChoice(color_mode_option
, printing::kBlack
))
237 *color_model_for_black
= printing::HP_COLOR_BLACK
;
239 ppd_choice_t
* mode_choice
= ppdFindMarkedChoice(ppd
, kColorMode
);
241 mode_choice
= ppdFindChoice(color_mode_option
,
242 color_mode_option
->defchoice
);
246 (base::strcasecmp(mode_choice
->choice
, printing::kColor
) == 0);
251 bool GetProcessColorModelSettings(ppd_file_t
* ppd
,
252 int* color_model_for_black
,
253 int* color_model_for_color
,
254 bool* color_is_default
) {
255 // Canon printers use "ProcessColorModel" attribute in their ppds.
256 ppd_option_t
* color_mode_option
= ppdFindOption(ppd
, kProcessColorModel
);
257 if (!color_mode_option
)
260 if (ppdFindChoice(color_mode_option
, printing::kRGB
))
261 *color_model_for_color
= printing::PROCESSCOLORMODEL_RGB
;
262 else if (ppdFindChoice(color_mode_option
, printing::kCMYK
))
263 *color_model_for_color
= printing::PROCESSCOLORMODEL_CMYK
;
265 if (ppdFindChoice(color_mode_option
, printing::kGreyscale
))
266 *color_model_for_black
= printing::PROCESSCOLORMODEL_GREYSCALE
;
268 ppd_choice_t
* mode_choice
= ppdFindMarkedChoice(ppd
, kProcessColorModel
);
270 mode_choice
= ppdFindChoice(color_mode_option
,
271 color_mode_option
->defchoice
);
276 (base::strcasecmp(mode_choice
->choice
, printing::kGreyscale
) != 0);
281 bool GetColorModelSettings(ppd_file_t
* ppd
,
285 bool is_color_device
= false;
286 ppd_attr_t
* attr
= ppdFindAttr(ppd
, kColorDevice
, NULL
);
287 if (attr
&& attr
->value
)
288 is_color_device
= ppd
->color_device
;
290 *is_color
= is_color_device
;
291 return (is_color_device
&&
292 GetBasicColorModelSettings(ppd
, cm_black
, cm_color
, is_color
)) ||
293 GetPrintOutModeColorSettings(ppd
, cm_black
, cm_color
, is_color
) ||
294 GetColorModeSettings(ppd
, cm_black
, cm_color
, is_color
) ||
295 GetHPColorSettings(ppd
, cm_black
, cm_color
, is_color
) ||
296 GetProcessColorModelSettings(ppd
, cm_black
, cm_color
, is_color
);
303 // Default port for IPP print servers.
304 static const int kDefaultIPPServerPort
= 631;
306 // Helper wrapper around http_t structure, with connection and cleanup
308 HttpConnectionCUPS::HttpConnectionCUPS(const GURL
& print_server_url
,
309 http_encryption_t encryption
)
311 // If we have an empty url, use default print server.
312 if (print_server_url
.is_empty())
315 int port
= print_server_url
.IntPort();
316 if (port
== url_parse::PORT_UNSPECIFIED
)
317 port
= kDefaultIPPServerPort
;
319 http_
= httpConnectEncrypt(print_server_url
.host().c_str(), port
,
322 LOG(ERROR
) << "CP_CUPS: Failed connecting to print server: " <<
327 HttpConnectionCUPS::~HttpConnectionCUPS() {
332 void HttpConnectionCUPS::SetBlocking(bool blocking
) {
333 httpBlocking(http_
, blocking
? 1 : 0);
336 http_t
* HttpConnectionCUPS::http() {
340 bool parsePpdCapabilities(
341 const std::string
& printer_name
,
342 const std::string
& printer_capabilities
,
343 PrinterSemanticCapsAndDefaults
* printer_info
) {
344 base::FilePath ppd_file_path
;
345 if (!file_util::CreateTemporaryFile(&ppd_file_path
))
348 int data_size
= printer_capabilities
.length();
349 if (data_size
!= file_util::WriteFile(
351 printer_capabilities
.data(),
353 file_util::Delete(ppd_file_path
, false);
357 ppd_file_t
* ppd
= ppdOpenFile(ppd_file_path
.value().c_str());
361 printing::PrinterSemanticCapsAndDefaults caps
;
362 #if !defined(OS_MACOSX)
363 MarkLpOptions(printer_name
, &ppd
);
365 ppd_choice_t
* duplex_choice
= ppdFindMarkedChoice(ppd
, kDuplex
);
366 if (!duplex_choice
) {
367 ppd_option_t
* option
= ppdFindOption(ppd
, kDuplex
);
369 duplex_choice
= ppdFindChoice(option
, option
->defchoice
);
373 caps
.duplex_capable
= true;
374 if (base::strcasecmp(duplex_choice
->choice
, kDuplexNone
) != 0)
375 caps
.duplex_default
= printing::LONG_EDGE
;
377 caps
.duplex_default
= printing::SIMPLEX
;
380 bool is_color
= false;
381 int cm_color
= 0, cm_black
= 0;
382 if (!GetColorModelSettings(ppd
, &cm_black
, &cm_color
, &is_color
)) {
383 VLOG(1) << "Unknown printer color model";
386 caps
.color_changeable
= (cm_color
&& cm_black
&& (cm_color
!= cm_black
));
387 caps
.color_default
= is_color
;
390 file_util::Delete(ppd_file_path
, false);
392 *printer_info
= caps
;
396 } // namespace printing