OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 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. |
| 4 |
| 5 #include "printing/backend/cups_ipp_util.h" |
| 6 |
| 7 #include <cups/cups.h> |
| 8 |
| 9 #include <algorithm> |
| 10 #include <string> |
| 11 #include <vector> |
| 12 |
| 13 #include "base/logging.h" |
| 14 #include "base/stl_util.h" |
| 15 #include "base/strings/string_number_conversions.h" |
| 16 #include "base/strings/string_piece.h" |
| 17 #include "base/strings/string_split.h" |
| 18 #include "base/strings/string_util.h" |
| 19 #include "printing/backend/cups_printer.h" |
| 20 #include "printing/backend/print_backend_consts.h" |
| 21 #include "printing/units.h" |
| 22 |
| 23 namespace printing { |
| 24 |
| 25 namespace { |
| 26 |
| 27 const char kIppCollate[] = "sheet-collate"; // RFC 3381 |
| 28 const char kIppCopies[] = CUPS_COPIES; |
| 29 const char kIppColor[] = CUPS_PRINT_COLOR_MODE; |
| 30 const char kIppMedia[] = CUPS_MEDIA; |
| 31 const char kIppDuplex[] = CUPS_SIDES; |
| 32 |
| 33 const char kCollated[] = "collated"; |
| 34 |
| 35 const int kMicronsPerMM = 1000; |
| 36 const double kMMPerInch = 25.4; |
| 37 const double kMicronsPerInch = kMMPerInch * kMicronsPerMM; |
| 38 |
| 39 enum Unit { |
| 40 INCHES, |
| 41 MILLIMETERS, |
| 42 }; |
| 43 |
| 44 struct ColorMap { |
| 45 const char* color; |
| 46 ColorModel model; |
| 47 }; |
| 48 |
| 49 const ColorMap kColorList[]{ |
| 50 {CUPS_PRINT_COLOR_MODE_COLOR, COLORMODE_COLOR}, |
| 51 {CUPS_PRINT_COLOR_MODE_MONOCHROME, COLORMODE_MONOCHROME}, |
| 52 }; |
| 53 |
| 54 ColorModel ColorModelFromIppColor(base::StringPiece ippColor) { |
| 55 for (const ColorMap& color : kColorList) { |
| 56 if (ippColor.compare(color.color) == 0) { |
| 57 return color.model; |
| 58 } |
| 59 } |
| 60 |
| 61 return UNKNOWN_COLOR_MODEL; |
| 62 } |
| 63 |
| 64 bool PrinterSupportsValue(const CupsOptionProvider& printer, |
| 65 base::StringPiece name, |
| 66 base::StringPiece value) { |
| 67 std::vector<base::StringPiece> values = |
| 68 printer.GetSupportedOptionValueStrings(name); |
| 69 return ContainsValue(values, value); |
| 70 } |
| 71 |
| 72 DuplexMode PrinterDefaultDuplex(const CupsOptionProvider& printer) { |
| 73 ipp_attribute_t* attr = printer.GetDefaultOptionValue(kIppDuplex); |
| 74 if (!attr) |
| 75 return UNKNOWN_DUPLEX_MODE; |
| 76 |
| 77 const char* value = ippGetString(attr, 0, nullptr); |
| 78 if (base::EqualsCaseInsensitiveASCII(value, CUPS_SIDES_ONE_SIDED)) |
| 79 return SIMPLEX; |
| 80 |
| 81 if (base::EqualsCaseInsensitiveASCII(value, CUPS_SIDES_TWO_SIDED_PORTRAIT)) |
| 82 return LONG_EDGE; |
| 83 |
| 84 if (base::EqualsCaseInsensitiveASCII(value, CUPS_SIDES_TWO_SIDED_LANDSCAPE)) |
| 85 return SHORT_EDGE; |
| 86 |
| 87 return UNKNOWN_DUPLEX_MODE; |
| 88 } |
| 89 |
| 90 gfx::Size DimensionsToMicrons(base::StringPiece value) { |
| 91 Unit unit; |
| 92 base::StringPiece dims; |
| 93 size_t unit_position; |
| 94 if ((unit_position = value.find("mm")) != base::StringPiece::npos) { |
| 95 unit = MILLIMETERS; |
| 96 dims = value.substr(0, unit_position); |
| 97 } else if ((unit_position = value.find("in")) != base::StringPiece::npos) { |
| 98 unit = INCHES; |
| 99 dims = value.substr(0, unit_position); |
| 100 } else { |
| 101 LOG(WARNING) << "Could not parse paper dimensions"; |
| 102 return {0, 0}; |
| 103 } |
| 104 |
| 105 std::vector<std::string> pieces = base::SplitString( |
| 106 dims, "x", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
| 107 double width; |
| 108 double height; |
| 109 |
| 110 if (pieces.size() != 2 || !base::StringToDouble(pieces[0], &width) || |
| 111 !base::StringToDouble(pieces[1], &height)) { |
| 112 return {0, 0}; |
| 113 } |
| 114 |
| 115 int width_microns; |
| 116 int height_microns; |
| 117 |
| 118 switch (unit) { |
| 119 case MILLIMETERS: |
| 120 width_microns = width * kMicronsPerMM; |
| 121 height_microns = height * kMicronsPerMM; |
| 122 break; |
| 123 case INCHES: |
| 124 width_microns = width * kMicronsPerInch; |
| 125 height_microns = height * kMicronsPerInch; |
| 126 break; |
| 127 default: |
| 128 NOTREACHED(); |
| 129 break; |
| 130 } |
| 131 |
| 132 return gfx::Size{width_microns, height_microns}; |
| 133 } |
| 134 |
| 135 PrinterSemanticCapsAndDefaults::Paper ParsePaper(base::StringPiece value) { |
| 136 // <name>_<width>x<height>{in,mm} |
| 137 // e.g. na_letter_8.5x11in, iso_a4_210x297mm |
| 138 |
| 139 std::vector<base::StringPiece> pieces = base::SplitStringPiece( |
| 140 value, "_", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
| 141 // we expect at least a display string and a dimension string |
| 142 if (pieces.size() < 2) |
| 143 return PrinterSemanticCapsAndDefaults::Paper(); |
| 144 |
| 145 base::StringPiece dimensions = pieces.back(); |
| 146 |
| 147 std::string display = pieces[0].as_string(); |
| 148 for (size_t i = 1; i <= pieces.size() - 2; ++i) { |
| 149 display.append(" "); |
| 150 pieces[i].AppendToString(&display); |
| 151 } |
| 152 |
| 153 PrinterSemanticCapsAndDefaults::Paper paper; |
| 154 paper.display_name = display; |
| 155 paper.vendor_id = value.as_string(); |
| 156 paper.size_um = DimensionsToMicrons(dimensions); |
| 157 |
| 158 return paper; |
| 159 } |
| 160 |
| 161 void ExtractColor(const CupsOptionProvider& printer, |
| 162 PrinterSemanticCapsAndDefaults* printer_info) { |
| 163 printer_info->bw_model = UNKNOWN_COLOR_MODEL; |
| 164 printer_info->color_model = UNKNOWN_COLOR_MODEL; |
| 165 |
| 166 // color and b&w |
| 167 std::vector<ColorModel> color_models = SupportedColorModels(printer); |
| 168 for (ColorModel color : color_models) { |
| 169 switch (color) { |
| 170 case COLORMODE_COLOR: |
| 171 printer_info->color_model = COLORMODE_COLOR; |
| 172 break; |
| 173 case COLORMODE_MONOCHROME: |
| 174 printer_info->bw_model = COLORMODE_MONOCHROME; |
| 175 break; |
| 176 default: |
| 177 // value not needed |
| 178 break; |
| 179 } |
| 180 } |
| 181 |
| 182 // changeable |
| 183 printer_info->color_changeable = |
| 184 (printer_info->color_model != UNKNOWN_COLOR_MODEL && |
| 185 printer_info->bw_model != UNKNOWN_COLOR_MODEL); |
| 186 |
| 187 // default color |
| 188 printer_info->color_default = DefaultColorModel(printer) == COLORMODE_COLOR; |
| 189 } |
| 190 |
| 191 void ExtractCopies(const CupsOptionProvider& printer, |
| 192 PrinterSemanticCapsAndDefaults* printer_info) { |
| 193 // copies |
| 194 int upper_bound; |
| 195 int lower_bound; |
| 196 CopiesRange(printer, &lower_bound, &upper_bound); |
| 197 printer_info->copies_capable = (lower_bound != -1) && (upper_bound >= 2); |
| 198 } |
| 199 |
| 200 } // namespace |
| 201 |
| 202 ColorModel DefaultColorModel(const CupsOptionProvider& printer) { |
| 203 // default color |
| 204 ipp_attribute_t* attr = printer.GetDefaultOptionValue(kIppColor); |
| 205 if (!attr) |
| 206 return UNKNOWN_COLOR_MODEL; |
| 207 |
| 208 return ColorModelFromIppColor(ippGetString(attr, 0, nullptr)); |
| 209 } |
| 210 |
| 211 std::vector<ColorModel> SupportedColorModels( |
| 212 const CupsOptionProvider& printer) { |
| 213 std::vector<ColorModel> colors; |
| 214 |
| 215 std::vector<base::StringPiece> color_modes = |
| 216 printer.GetSupportedOptionValueStrings(kIppColor); |
| 217 |
| 218 for (base::StringPiece color : color_modes) { |
| 219 ColorModel color_model = ColorModelFromIppColor(color); |
| 220 if (color_model != UNKNOWN_COLOR_MODEL) { |
| 221 colors.push_back(color_model); |
| 222 } |
| 223 } |
| 224 |
| 225 return colors; |
| 226 } |
| 227 |
| 228 PrinterSemanticCapsAndDefaults::Paper DefaultPaper( |
| 229 const CupsOptionProvider& printer) { |
| 230 ipp_attribute_t* attr = printer.GetDefaultOptionValue(kIppMedia); |
| 231 if (!attr) |
| 232 return PrinterSemanticCapsAndDefaults::Paper(); |
| 233 |
| 234 return ParsePaper(ippGetString(attr, 0, nullptr)); |
| 235 } |
| 236 |
| 237 std::vector<PrinterSemanticCapsAndDefaults::Paper> SupportedPapers( |
| 238 const CupsOptionProvider& printer) { |
| 239 std::vector<base::StringPiece> papers = |
| 240 printer.GetSupportedOptionValueStrings(kIppMedia); |
| 241 std::vector<PrinterSemanticCapsAndDefaults::Paper> parsed_papers; |
| 242 for (base::StringPiece paper : papers) { |
| 243 parsed_papers.push_back(ParsePaper(paper)); |
| 244 } |
| 245 |
| 246 return parsed_papers; |
| 247 } |
| 248 |
| 249 void CopiesRange(const CupsOptionProvider& printer, |
| 250 int* lower_bound, |
| 251 int* upper_bound) { |
| 252 ipp_attribute_t* attr = printer.GetSupportedOptionValues(kIppCopies); |
| 253 if (!attr) { |
| 254 *lower_bound = -1; |
| 255 *upper_bound = -1; |
| 256 } |
| 257 |
| 258 *lower_bound = ippGetRange(attr, 0, upper_bound); |
| 259 } |
| 260 |
| 261 bool CollateCapable(const CupsOptionProvider& printer) { |
| 262 std::vector<base::StringPiece> values = |
| 263 printer.GetSupportedOptionValueStrings(kIppCollate); |
| 264 auto iter = std::find(values.begin(), values.end(), kCollated); |
| 265 return iter != values.end(); |
| 266 } |
| 267 |
| 268 bool CollateDefault(const CupsOptionProvider& printer) { |
| 269 ipp_attribute_t* attr = printer.GetDefaultOptionValue(kIppCollate); |
| 270 if (!attr) |
| 271 return false; |
| 272 |
| 273 base::StringPiece name = ippGetString(attr, 0, nullptr); |
| 274 return name.compare(kCollated) == 0; |
| 275 } |
| 276 |
| 277 void CapsAndDefaultsFromPrinter(const CupsOptionProvider& printer, |
| 278 PrinterSemanticCapsAndDefaults* printer_info) { |
| 279 // duplex |
| 280 printer_info->duplex_default = PrinterDefaultDuplex(printer); |
| 281 printer_info->duplex_capable = |
| 282 PrinterSupportsValue(printer, kIppDuplex, CUPS_SIDES_TWO_SIDED_PORTRAIT); |
| 283 |
| 284 // collate |
| 285 printer_info->collate_default = CollateDefault(printer); |
| 286 printer_info->collate_capable = CollateCapable(printer); |
| 287 |
| 288 // paper |
| 289 printer_info->default_paper = DefaultPaper(printer); |
| 290 printer_info->papers = SupportedPapers(printer); |
| 291 |
| 292 ExtractCopies(printer, printer_info); |
| 293 ExtractColor(printer, printer_info); |
| 294 |
| 295 // TODO(skau): Add dpi and default_dpi |
| 296 } |
| 297 |
| 298 } // namespace printing |
OLD | NEW |