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/base_paths.h"
10 #include "base/files/file_util.h"
11 #include "base/logging.h"
12 #include "base/path_service.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "base/values.h"
17 #include "printing/backend/print_backend.h"
18 #include "printing/backend/print_backend_consts.h"
19 #include "printing/units.h"
24 // This section contains helper code for PPD parsing for semantic capabilities.
27 const char kColorDevice
[] = "ColorDevice";
28 const char kColorModel
[] = "ColorModel";
29 const char kColorMode
[] = "ColorMode";
30 const char kProcessColorModel
[] = "ProcessColorModel";
31 const char kPrintoutMode
[] = "PrintoutMode";
32 const char kDraftGray
[] = "Draft.Gray";
33 const char kHighGray
[] = "High.Gray";
35 const char kDuplex
[] = "Duplex";
36 const char kDuplexNone
[] = "None";
37 const char kPageSize
[] = "PageSize";
39 const double kMicronsPerPoint
= 10.0f
* kHundrethsMMPerInch
/ kPointsPerInch
;
41 void ParseLpOptions(const base::FilePath
& filepath
,
42 const std::string
& printer_name
,
43 int* num_options
, cups_option_t
** options
) {
45 if (!base::ReadFileToString(filepath
, &content
))
48 const char kDest
[] = "dest";
49 const char kDefault
[] = "default";
50 const size_t kDestLen
= sizeof(kDest
) - 1;
51 const size_t kDefaultLen
= sizeof(kDefault
) - 1;
52 std::vector
<std::string
> lines
;
53 base::SplitString(content
, '\n', &lines
);
55 for (size_t i
= 0; i
< lines
.size(); ++i
) {
56 std::string line
= lines
[i
];
60 if (base::strncasecmp (line
.c_str(), kDefault
, kDefaultLen
) == 0 &&
61 isspace(line
[kDefaultLen
])) {
62 line
= line
.substr(kDefaultLen
);
63 } else if (base::strncasecmp (line
.c_str(), kDest
, kDestLen
) == 0 &&
64 isspace(line
[kDestLen
])) {
65 line
= line
.substr(kDestLen
);
70 base::TrimWhitespaceASCII(line
, base::TRIM_ALL
, &line
);
74 size_t space_found
= line
.find(' ');
75 if (space_found
== std::string::npos
)
78 std::string name
= line
.substr(0, space_found
);
82 if (base::strncasecmp(printer_name
.c_str(), name
.c_str(),
83 name
.length()) != 0) {
84 continue; // This is not the required printer.
87 line
= line
.substr(space_found
+ 1);
88 // Remove extra spaces.
89 base::TrimWhitespaceASCII(line
, base::TRIM_ALL
, &line
);
92 // Parse the selected printer custom options.
93 *num_options
= cupsParseOptions(line
.c_str(), 0, options
);
97 void MarkLpOptions(const std::string
& printer_name
, ppd_file_t
** ppd
) {
98 cups_option_t
* options
= NULL
;
101 const char kSystemLpOptionPath
[] = "/etc/cups/lpoptions";
102 const char kUserLpOptionPath
[] = ".cups/lpoptions";
104 std::vector
<base::FilePath
> file_locations
;
105 file_locations
.push_back(base::FilePath(kSystemLpOptionPath
));
106 base::FilePath homedir
;
107 PathService::Get(base::DIR_HOME
, &homedir
);
108 file_locations
.push_back(base::FilePath(homedir
.Append(kUserLpOptionPath
)));
110 for (std::vector
<base::FilePath
>::const_iterator it
= file_locations
.begin();
111 it
!= file_locations
.end(); ++it
) {
114 ParseLpOptions(*it
, printer_name
, &num_options
, &options
);
115 if (num_options
> 0 && options
) {
116 cupsMarkOptions(*ppd
, num_options
, options
);
117 cupsFreeOptions(num_options
, options
);
122 bool GetBasicColorModelSettings(ppd_file_t
* ppd
,
123 ColorModel
* color_model_for_black
,
124 ColorModel
* color_model_for_color
,
125 bool* color_is_default
) {
126 ppd_option_t
* color_model
= ppdFindOption(ppd
, kColorModel
);
130 if (ppdFindChoice(color_model
, printing::kBlack
))
131 *color_model_for_black
= printing::BLACK
;
132 else if (ppdFindChoice(color_model
, printing::kGray
))
133 *color_model_for_black
= printing::GRAY
;
134 else if (ppdFindChoice(color_model
, printing::kGrayscale
))
135 *color_model_for_black
= printing::GRAYSCALE
;
137 if (ppdFindChoice(color_model
, printing::kColor
))
138 *color_model_for_color
= printing::COLOR
;
139 else if (ppdFindChoice(color_model
, printing::kCMYK
))
140 *color_model_for_color
= printing::CMYK
;
141 else if (ppdFindChoice(color_model
, printing::kRGB
))
142 *color_model_for_color
= printing::RGB
;
143 else if (ppdFindChoice(color_model
, printing::kRGBA
))
144 *color_model_for_color
= printing::RGBA
;
145 else if (ppdFindChoice(color_model
, printing::kRGB16
))
146 *color_model_for_color
= printing::RGB16
;
147 else if (ppdFindChoice(color_model
, printing::kCMY
))
148 *color_model_for_color
= printing::CMY
;
149 else if (ppdFindChoice(color_model
, printing::kKCMY
))
150 *color_model_for_color
= printing::KCMY
;
151 else if (ppdFindChoice(color_model
, printing::kCMY_K
))
152 *color_model_for_color
= printing::CMY_K
;
154 ppd_choice_t
* marked_choice
= ppdFindMarkedChoice(ppd
, kColorModel
);
156 marked_choice
= ppdFindChoice(color_model
, color_model
->defchoice
);
160 (base::strcasecmp(marked_choice
->choice
, printing::kBlack
) != 0) &&
161 (base::strcasecmp(marked_choice
->choice
, printing::kGray
) != 0) &&
162 (base::strcasecmp(marked_choice
->choice
, printing::kGrayscale
) != 0);
167 bool GetPrintOutModeColorSettings(ppd_file_t
* ppd
,
168 ColorModel
* color_model_for_black
,
169 ColorModel
* color_model_for_color
,
170 bool* color_is_default
) {
171 ppd_option_t
* printout_mode
= ppdFindOption(ppd
, kPrintoutMode
);
175 *color_model_for_color
= printing::PRINTOUTMODE_NORMAL
;
176 *color_model_for_black
= printing::PRINTOUTMODE_NORMAL
;
178 // Check to see if NORMAL_GRAY value is supported by PrintoutMode.
179 // If NORMAL_GRAY is not supported, NORMAL value is used to
180 // represent grayscale. If NORMAL_GRAY is supported, NORMAL is used to
182 if (ppdFindChoice(printout_mode
, printing::kNormalGray
))
183 *color_model_for_black
= printing::PRINTOUTMODE_NORMAL_GRAY
;
185 // Get the default marked choice to identify the default color setting
187 ppd_choice_t
* printout_mode_choice
= ppdFindMarkedChoice(ppd
, kPrintoutMode
);
188 if (!printout_mode_choice
) {
189 printout_mode_choice
= ppdFindChoice(printout_mode
,
190 printout_mode
->defchoice
);
192 if (printout_mode_choice
) {
193 if ((base::strcasecmp(printout_mode_choice
->choice
,
194 printing::kNormalGray
) == 0) ||
195 (base::strcasecmp(printout_mode_choice
->choice
, kHighGray
) == 0) ||
196 (base::strcasecmp(printout_mode_choice
->choice
, kDraftGray
) == 0)) {
197 *color_model_for_black
= printing::PRINTOUTMODE_NORMAL_GRAY
;
198 *color_is_default
= false;
204 bool GetColorModeSettings(ppd_file_t
* ppd
,
205 ColorModel
* color_model_for_black
,
206 ColorModel
* color_model_for_color
,
207 bool* color_is_default
) {
208 // Samsung printers use "ColorMode" attribute in their ppds.
209 ppd_option_t
* color_mode_option
= ppdFindOption(ppd
, kColorMode
);
210 if (!color_mode_option
)
213 if (ppdFindChoice(color_mode_option
, printing::kColor
))
214 *color_model_for_color
= printing::COLORMODE_COLOR
;
216 if (ppdFindChoice(color_mode_option
, printing::kMonochrome
))
217 *color_model_for_black
= printing::COLORMODE_MONOCHROME
;
219 ppd_choice_t
* mode_choice
= ppdFindMarkedChoice(ppd
, kColorMode
);
221 mode_choice
= ppdFindChoice(color_mode_option
,
222 color_mode_option
->defchoice
);
227 (base::strcasecmp(mode_choice
->choice
, printing::kColor
) == 0);
232 bool GetHPColorSettings(ppd_file_t
* ppd
,
233 ColorModel
* color_model_for_black
,
234 ColorModel
* color_model_for_color
,
235 bool* color_is_default
) {
236 // HP printers use "Color/Color Model" attribute in their ppds.
237 ppd_option_t
* color_mode_option
= ppdFindOption(ppd
, printing::kColor
);
238 if (!color_mode_option
)
241 if (ppdFindChoice(color_mode_option
, printing::kColor
))
242 *color_model_for_color
= printing::HP_COLOR_COLOR
;
243 if (ppdFindChoice(color_mode_option
, printing::kBlack
))
244 *color_model_for_black
= printing::HP_COLOR_BLACK
;
246 ppd_choice_t
* mode_choice
= ppdFindMarkedChoice(ppd
, kColorMode
);
248 mode_choice
= ppdFindChoice(color_mode_option
,
249 color_mode_option
->defchoice
);
253 (base::strcasecmp(mode_choice
->choice
, printing::kColor
) == 0);
258 bool GetProcessColorModelSettings(ppd_file_t
* ppd
,
259 ColorModel
* color_model_for_black
,
260 ColorModel
* color_model_for_color
,
261 bool* color_is_default
) {
262 // Canon printers use "ProcessColorModel" attribute in their ppds.
263 ppd_option_t
* color_mode_option
= ppdFindOption(ppd
, kProcessColorModel
);
264 if (!color_mode_option
)
267 if (ppdFindChoice(color_mode_option
, printing::kRGB
))
268 *color_model_for_color
= printing::PROCESSCOLORMODEL_RGB
;
269 else if (ppdFindChoice(color_mode_option
, printing::kCMYK
))
270 *color_model_for_color
= printing::PROCESSCOLORMODEL_CMYK
;
272 if (ppdFindChoice(color_mode_option
, printing::kGreyscale
))
273 *color_model_for_black
= printing::PROCESSCOLORMODEL_GREYSCALE
;
275 ppd_choice_t
* mode_choice
= ppdFindMarkedChoice(ppd
, kProcessColorModel
);
277 mode_choice
= ppdFindChoice(color_mode_option
,
278 color_mode_option
->defchoice
);
283 (base::strcasecmp(mode_choice
->choice
, printing::kGreyscale
) != 0);
288 bool GetColorModelSettings(ppd_file_t
* ppd
,
289 ColorModel
* cm_black
,
290 ColorModel
* cm_color
,
292 bool is_color_device
= false;
293 ppd_attr_t
* attr
= ppdFindAttr(ppd
, kColorDevice
, NULL
);
294 if (attr
&& attr
->value
)
295 is_color_device
= ppd
->color_device
;
297 *is_color
= is_color_device
;
298 return (is_color_device
&&
299 GetBasicColorModelSettings(ppd
, cm_black
, cm_color
, is_color
)) ||
300 GetPrintOutModeColorSettings(ppd
, cm_black
, cm_color
, is_color
) ||
301 GetColorModeSettings(ppd
, cm_black
, cm_color
, is_color
) ||
302 GetHPColorSettings(ppd
, cm_black
, cm_color
, is_color
) ||
303 GetProcessColorModelSettings(ppd
, cm_black
, cm_color
, is_color
);
306 // Default port for IPP print servers.
307 const int kDefaultIPPServerPort
= 631;
311 // Helper wrapper around http_t structure, with connection and cleanup
313 HttpConnectionCUPS::HttpConnectionCUPS(const GURL
& print_server_url
,
314 http_encryption_t encryption
)
316 // If we have an empty url, use default print server.
317 if (print_server_url
.is_empty())
320 int port
= print_server_url
.IntPort();
321 if (port
== url::PORT_UNSPECIFIED
)
322 port
= kDefaultIPPServerPort
;
324 http_
= httpConnectEncrypt(print_server_url
.host().c_str(), port
, encryption
);
326 LOG(ERROR
) << "CP_CUPS: Failed connecting to print server: "
331 HttpConnectionCUPS::~HttpConnectionCUPS() {
336 void HttpConnectionCUPS::SetBlocking(bool blocking
) {
337 httpBlocking(http_
, blocking
? 1 : 0);
340 http_t
* HttpConnectionCUPS::http() {
344 bool ParsePpdCapabilities(
345 const std::string
& printer_name
,
346 const std::string
& printer_capabilities
,
347 PrinterSemanticCapsAndDefaults
* printer_info
) {
348 base::FilePath ppd_file_path
;
349 if (!base::CreateTemporaryFile(&ppd_file_path
))
352 int data_size
= printer_capabilities
.length();
353 if (data_size
!= base::WriteFile(
355 printer_capabilities
.data(),
357 base::DeleteFile(ppd_file_path
, false);
361 ppd_file_t
* ppd
= ppdOpenFile(ppd_file_path
.value().c_str());
364 ppd_status_t ppd_status
= ppdLastError(&line
);
365 LOG(ERROR
) << "Failed to open PDD file: error " << ppd_status
<< " at line "
366 << line
<< ", " << ppdErrorString(ppd_status
);
369 ppdMarkDefaults(ppd
);
370 MarkLpOptions(printer_name
, &ppd
);
372 printing::PrinterSemanticCapsAndDefaults caps
;
373 caps
.collate_capable
= true;
374 caps
.collate_default
= true;
375 caps
.copies_capable
= true;
377 ppd_choice_t
* duplex_choice
= ppdFindMarkedChoice(ppd
, kDuplex
);
378 if (!duplex_choice
) {
379 ppd_option_t
* option
= ppdFindOption(ppd
, kDuplex
);
381 duplex_choice
= ppdFindChoice(option
, option
->defchoice
);
385 caps
.duplex_capable
= true;
386 if (base::strcasecmp(duplex_choice
->choice
, kDuplexNone
) != 0)
387 caps
.duplex_default
= printing::LONG_EDGE
;
389 caps
.duplex_default
= printing::SIMPLEX
;
392 bool is_color
= false;
393 ColorModel cm_color
= UNKNOWN_COLOR_MODEL
, cm_black
= UNKNOWN_COLOR_MODEL
;
394 if (!GetColorModelSettings(ppd
, &cm_black
, &cm_color
, &is_color
)) {
395 VLOG(1) << "Unknown printer color model";
398 caps
.color_changeable
= ((cm_color
!= UNKNOWN_COLOR_MODEL
) &&
399 (cm_black
!= UNKNOWN_COLOR_MODEL
) &&
400 (cm_color
!= cm_black
));
401 caps
.color_default
= is_color
;
402 caps
.color_model
= cm_color
;
403 caps
.bw_model
= cm_black
;
405 if (ppd
->num_sizes
> 0 && ppd
->sizes
) {
406 VLOG(1) << "Paper list size - " << ppd
->num_sizes
;
407 ppd_option_t
* paper_option
= ppdFindOption(ppd
, kPageSize
);
408 for (int i
= 0; i
< ppd
->num_sizes
; ++i
) {
409 gfx::Size
paper_size_microns(
410 static_cast<int>(ppd
->sizes
[i
].width
* kMicronsPerPoint
+ 0.5),
411 static_cast<int>(ppd
->sizes
[i
].length
* kMicronsPerPoint
+ 0.5));
412 if (paper_size_microns
.width() > 0 && paper_size_microns
.height() > 0) {
413 PrinterSemanticCapsAndDefaults::Paper paper
;
414 paper
.size_um
= paper_size_microns
;
415 paper
.vendor_id
= ppd
->sizes
[i
].name
;
417 ppd_choice_t
* paper_choice
=
418 ppdFindChoice(paper_option
, ppd
->sizes
[i
].name
);
419 // Human readable paper name should be UTF-8 encoded, but some PPDs
420 // do not follow this standard.
421 if (paper_choice
&& base::IsStringUTF8(paper_choice
->text
)) {
422 paper
.display_name
= paper_choice
->text
;
425 caps
.papers
.push_back(paper
);
426 if (i
== 0 || ppd
->sizes
[i
].marked
) {
427 caps
.default_paper
= paper
;
434 base::DeleteFile(ppd_file_path
, false);
436 *printer_info
= caps
;
440 } // namespace printing