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/strings/string_number_conversions.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/string_util.h"
14 #include "base/values.h"
15 #include "printing/backend/print_backend.h"
16 #include "printing/backend/print_backend_consts.h"
21 // This section contains helper code for PPD parsing for semantic capabilities.
24 const char kColorDevice
[] = "ColorDevice";
25 const char kColorModel
[] = "ColorModel";
26 const char kColorMode
[] = "ColorMode";
27 const char kProcessColorModel
[] = "ProcessColorModel";
28 const char kPrintoutMode
[] = "PrintoutMode";
29 const char kDraftGray
[] = "Draft.Gray";
30 const char kHighGray
[] = "High.Gray";
32 const char kDuplex
[] = "Duplex";
33 const char kDuplexNone
[] = "None";
35 #if !defined(OS_MACOSX)
36 void ParseLpOptions(const base::FilePath
& filepath
,
37 const std::string
& printer_name
,
38 int* num_options
, cups_option_t
** options
) {
40 if (!base::ReadFileToString(filepath
, &content
))
43 const char kDest
[] = "dest";
44 const char kDefault
[] = "default";
45 const size_t kDestLen
= sizeof(kDest
) - 1;
46 const size_t kDefaultLen
= sizeof(kDefault
) - 1;
47 std::vector
<std::string
> lines
;
48 base::SplitString(content
, '\n', &lines
);
50 for (size_t i
= 0; i
< lines
.size(); ++i
) {
51 std::string line
= lines
[i
];
55 if (base::strncasecmp (line
.c_str(), kDefault
, kDefaultLen
) == 0 &&
56 isspace(line
[kDefaultLen
])) {
57 line
= line
.substr(kDefaultLen
);
58 } else if (base::strncasecmp (line
.c_str(), kDest
, kDestLen
) == 0 &&
59 isspace(line
[kDestLen
])) {
60 line
= line
.substr(kDestLen
);
65 base::TrimWhitespaceASCII(line
, base::TRIM_ALL
, &line
);
69 size_t space_found
= line
.find(' ');
70 if (space_found
== std::string::npos
)
73 std::string name
= line
.substr(0, space_found
);
77 if (base::strncasecmp(printer_name
.c_str(), name
.c_str(),
78 name
.length()) != 0) {
79 continue; // This is not the required printer.
82 line
= line
.substr(space_found
+ 1);
83 // Remove extra spaces.
84 base::TrimWhitespaceASCII(line
, base::TRIM_ALL
, &line
);
87 // Parse the selected printer custom options.
88 *num_options
= cupsParseOptions(line
.c_str(), 0, options
);
92 void MarkLpOptions(const std::string
& printer_name
, ppd_file_t
** ppd
) {
93 cups_option_t
* options
= NULL
;
95 ppdMarkDefaults(*ppd
);
97 const char kSystemLpOptionPath
[] = "/etc/cups/lpoptions";
98 const char kUserLpOptionPath
[] = ".cups/lpoptions";
100 std::vector
<base::FilePath
> file_locations
;
101 file_locations
.push_back(base::FilePath(kSystemLpOptionPath
));
102 file_locations
.push_back(base::FilePath(
103 base::GetHomeDir().Append(kUserLpOptionPath
)));
105 for (std::vector
<base::FilePath
>::const_iterator it
= file_locations
.begin();
106 it
!= file_locations
.end(); ++it
) {
109 ParseLpOptions(*it
, printer_name
, &num_options
, &options
);
110 if (num_options
> 0 && options
) {
111 cupsMarkOptions(*ppd
, num_options
, options
);
112 cupsFreeOptions(num_options
, options
);
116 #endif // !defined(OS_MACOSX)
118 bool GetBasicColorModelSettings(ppd_file_t
* ppd
,
119 ColorModel
* color_model_for_black
,
120 ColorModel
* color_model_for_color
,
121 bool* color_is_default
) {
122 ppd_option_t
* color_model
= ppdFindOption(ppd
, kColorModel
);
126 if (ppdFindChoice(color_model
, printing::kBlack
))
127 *color_model_for_black
= printing::BLACK
;
128 else if (ppdFindChoice(color_model
, printing::kGray
))
129 *color_model_for_black
= printing::GRAY
;
130 else if (ppdFindChoice(color_model
, printing::kGrayscale
))
131 *color_model_for_black
= printing::GRAYSCALE
;
133 if (ppdFindChoice(color_model
, printing::kColor
))
134 *color_model_for_color
= printing::COLOR
;
135 else if (ppdFindChoice(color_model
, printing::kCMYK
))
136 *color_model_for_color
= printing::CMYK
;
137 else if (ppdFindChoice(color_model
, printing::kRGB
))
138 *color_model_for_color
= printing::RGB
;
139 else if (ppdFindChoice(color_model
, printing::kRGBA
))
140 *color_model_for_color
= printing::RGBA
;
141 else if (ppdFindChoice(color_model
, printing::kRGB16
))
142 *color_model_for_color
= printing::RGB16
;
143 else if (ppdFindChoice(color_model
, printing::kCMY
))
144 *color_model_for_color
= printing::CMY
;
145 else if (ppdFindChoice(color_model
, printing::kKCMY
))
146 *color_model_for_color
= printing::KCMY
;
147 else if (ppdFindChoice(color_model
, printing::kCMY_K
))
148 *color_model_for_color
= printing::CMY_K
;
150 ppd_choice_t
* marked_choice
= ppdFindMarkedChoice(ppd
, kColorModel
);
152 marked_choice
= ppdFindChoice(color_model
, color_model
->defchoice
);
156 (base::strcasecmp(marked_choice
->choice
, printing::kBlack
) != 0) &&
157 (base::strcasecmp(marked_choice
->choice
, printing::kGray
) != 0) &&
158 (base::strcasecmp(marked_choice
->choice
, printing::kGrayscale
) != 0);
163 bool GetPrintOutModeColorSettings(ppd_file_t
* ppd
,
164 ColorModel
* color_model_for_black
,
165 ColorModel
* color_model_for_color
,
166 bool* color_is_default
) {
167 ppd_option_t
* printout_mode
= ppdFindOption(ppd
, kPrintoutMode
);
171 *color_model_for_color
= printing::PRINTOUTMODE_NORMAL
;
172 *color_model_for_black
= printing::PRINTOUTMODE_NORMAL
;
174 // Check to see if NORMAL_GRAY value is supported by PrintoutMode.
175 // If NORMAL_GRAY is not supported, NORMAL value is used to
176 // represent grayscale. If NORMAL_GRAY is supported, NORMAL is used to
178 if (ppdFindChoice(printout_mode
, printing::kNormalGray
))
179 *color_model_for_black
= printing::PRINTOUTMODE_NORMAL_GRAY
;
181 // Get the default marked choice to identify the default color setting
183 ppd_choice_t
* printout_mode_choice
= ppdFindMarkedChoice(ppd
, kPrintoutMode
);
184 if (!printout_mode_choice
) {
185 printout_mode_choice
= ppdFindChoice(printout_mode
,
186 printout_mode
->defchoice
);
188 if (printout_mode_choice
) {
189 if ((base::strcasecmp(printout_mode_choice
->choice
,
190 printing::kNormalGray
) == 0) ||
191 (base::strcasecmp(printout_mode_choice
->choice
, kHighGray
) == 0) ||
192 (base::strcasecmp(printout_mode_choice
->choice
, kDraftGray
) == 0)) {
193 *color_model_for_black
= printing::PRINTOUTMODE_NORMAL_GRAY
;
194 *color_is_default
= false;
200 bool GetColorModeSettings(ppd_file_t
* ppd
,
201 ColorModel
* color_model_for_black
,
202 ColorModel
* color_model_for_color
,
203 bool* color_is_default
) {
204 // Samsung printers use "ColorMode" attribute in their ppds.
205 ppd_option_t
* color_mode_option
= ppdFindOption(ppd
, kColorMode
);
206 if (!color_mode_option
)
209 if (ppdFindChoice(color_mode_option
, printing::kColor
))
210 *color_model_for_color
= printing::COLORMODE_COLOR
;
212 if (ppdFindChoice(color_mode_option
, printing::kMonochrome
))
213 *color_model_for_black
= printing::COLORMODE_MONOCHROME
;
215 ppd_choice_t
* mode_choice
= ppdFindMarkedChoice(ppd
, kColorMode
);
217 mode_choice
= ppdFindChoice(color_mode_option
,
218 color_mode_option
->defchoice
);
223 (base::strcasecmp(mode_choice
->choice
, printing::kColor
) == 0);
228 bool GetHPColorSettings(ppd_file_t
* ppd
,
229 ColorModel
* color_model_for_black
,
230 ColorModel
* color_model_for_color
,
231 bool* color_is_default
) {
232 // HP printers use "Color/Color Model" attribute in their ppds.
233 ppd_option_t
* color_mode_option
= ppdFindOption(ppd
, printing::kColor
);
234 if (!color_mode_option
)
237 if (ppdFindChoice(color_mode_option
, printing::kColor
))
238 *color_model_for_color
= printing::HP_COLOR_COLOR
;
239 if (ppdFindChoice(color_mode_option
, printing::kBlack
))
240 *color_model_for_black
= printing::HP_COLOR_BLACK
;
242 ppd_choice_t
* mode_choice
= ppdFindMarkedChoice(ppd
, kColorMode
);
244 mode_choice
= ppdFindChoice(color_mode_option
,
245 color_mode_option
->defchoice
);
249 (base::strcasecmp(mode_choice
->choice
, printing::kColor
) == 0);
254 bool GetProcessColorModelSettings(ppd_file_t
* ppd
,
255 ColorModel
* color_model_for_black
,
256 ColorModel
* color_model_for_color
,
257 bool* color_is_default
) {
258 // Canon printers use "ProcessColorModel" attribute in their ppds.
259 ppd_option_t
* color_mode_option
= ppdFindOption(ppd
, kProcessColorModel
);
260 if (!color_mode_option
)
263 if (ppdFindChoice(color_mode_option
, printing::kRGB
))
264 *color_model_for_color
= printing::PROCESSCOLORMODEL_RGB
;
265 else if (ppdFindChoice(color_mode_option
, printing::kCMYK
))
266 *color_model_for_color
= printing::PROCESSCOLORMODEL_CMYK
;
268 if (ppdFindChoice(color_mode_option
, printing::kGreyscale
))
269 *color_model_for_black
= printing::PROCESSCOLORMODEL_GREYSCALE
;
271 ppd_choice_t
* mode_choice
= ppdFindMarkedChoice(ppd
, kProcessColorModel
);
273 mode_choice
= ppdFindChoice(color_mode_option
,
274 color_mode_option
->defchoice
);
279 (base::strcasecmp(mode_choice
->choice
, printing::kGreyscale
) != 0);
284 bool GetColorModelSettings(ppd_file_t
* ppd
,
285 ColorModel
* cm_black
,
286 ColorModel
* cm_color
,
288 bool is_color_device
= false;
289 ppd_attr_t
* attr
= ppdFindAttr(ppd
, kColorDevice
, NULL
);
290 if (attr
&& attr
->value
)
291 is_color_device
= ppd
->color_device
;
293 *is_color
= is_color_device
;
294 return (is_color_device
&&
295 GetBasicColorModelSettings(ppd
, cm_black
, cm_color
, is_color
)) ||
296 GetPrintOutModeColorSettings(ppd
, cm_black
, cm_color
, is_color
) ||
297 GetColorModeSettings(ppd
, cm_black
, cm_color
, is_color
) ||
298 GetHPColorSettings(ppd
, cm_black
, cm_color
, is_color
) ||
299 GetProcessColorModelSettings(ppd
, cm_black
, cm_color
, is_color
);
302 // Default port for IPP print servers.
303 const int kDefaultIPPServerPort
= 631;
307 // Helper wrapper around http_t structure, with connection and cleanup
309 HttpConnectionCUPS::HttpConnectionCUPS(const GURL
& print_server_url
,
310 http_encryption_t encryption
)
312 // If we have an empty url, use default print server.
313 if (print_server_url
.is_empty())
316 int port
= print_server_url
.IntPort();
317 if (port
== url_parse::PORT_UNSPECIFIED
)
318 port
= kDefaultIPPServerPort
;
320 http_
= httpConnectEncrypt(print_server_url
.host().c_str(), port
, encryption
);
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 (!base::CreateTemporaryFile(&ppd_file_path
))
348 int data_size
= printer_capabilities
.length();
349 if (data_size
!= base::WriteFile(
351 printer_capabilities
.data(),
353 base::DeleteFile(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 ColorModel cm_color
= UNKNOWN_COLOR_MODEL
, cm_black
= UNKNOWN_COLOR_MODEL
;
382 if (!GetColorModelSettings(ppd
, &cm_black
, &cm_color
, &is_color
)) {
383 VLOG(1) << "Unknown printer color model";
386 caps
.color_changeable
= ((cm_color
!= UNKNOWN_COLOR_MODEL
) &&
387 (cm_black
!= UNKNOWN_COLOR_MODEL
) &&
388 (cm_color
!= cm_black
));
389 caps
.color_default
= is_color
;
390 caps
.color_model
= cm_color
;
391 caps
.bw_model
= cm_black
;
394 base::DeleteFile(ppd_file_path
, false);
396 *printer_info
= caps
;
400 } // namespace printing