cups_helper.cc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  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.
  4. #include "printing/backend/cups_helper.h"
  5. #include <cups/ppd.h>
  6. #include <stddef.h>
  7. #include <stdio.h>
  8. #include <unistd.h>
  9. #include <vector>
  10. #include "base/files/file_path.h"
  11. #include "base/files/file_util.h"
  12. #include "base/files/scoped_file.h"
  13. #include "base/logging.h"
  14. #include "base/strings/string_number_conversions.h"
  15. #include "base/strings/string_util.h"
  16. #include "base/time/time.h"
  17. #include "build/build_config.h"
  18. #include "printing/backend/print_backend.h"
  19. #include "printing/backend/print_backend_consts.h"
  20. #include "printing/mojom/print.mojom.h"
  21. #include "printing/print_job_constants.h"
  22. #include "printing/printing_utils.h"
  23. #include "printing/units.h"
  24. #include "url/gurl.h"
  25. using base::EqualsCaseInsensitiveASCII;
  26. namespace printing {
  27. // This section contains helper code for PPD parsing for semantic capabilities.
  28. namespace {
  29. // Function availability can be tested by checking whether its address is not
  30. // nullptr. Weak symbols remove the need for platform specific build flags and
  31. // allow for appropriate CUPS usage on platforms with non-uniform version
  32. // support, namely Linux.
  33. #define WEAK_CUPS_FN(x) extern "C" __attribute__((weak)) decltype(x) x
  34. WEAK_CUPS_FN(httpConnect2);
  35. // Timeout for establishing a CUPS connection. It is expected that cupsd is
  36. // able to start and respond on all systems within this duration.
  37. constexpr base::TimeDelta kCupsTimeout = base::Seconds(5);
  38. // CUPS default max copies value (parsed from kCupsMaxCopies PPD attribute).
  39. constexpr int32_t kDefaultMaxCopies = 9999;
  40. constexpr char kCupsMaxCopies[] = "cupsMaxCopies";
  41. constexpr char kColorDevice[] = "ColorDevice";
  42. constexpr char kColorModel[] = "ColorModel";
  43. constexpr char kColorMode[] = "ColorMode";
  44. constexpr char kProcessColorModel[] = "ProcessColorModel";
  45. constexpr char kPrintoutMode[] = "PrintoutMode";
  46. constexpr char kDraftGray[] = "Draft.Gray";
  47. constexpr char kHighGray[] = "High.Gray";
  48. constexpr char kDuplex[] = "Duplex";
  49. constexpr char kDuplexNone[] = "None";
  50. constexpr char kDuplexNoTumble[] = "DuplexNoTumble";
  51. constexpr char kDuplexTumble[] = "DuplexTumble";
  52. constexpr char kPageSize[] = "PageSize";
  53. // Brother printer specific options.
  54. constexpr char kBrotherDuplex[] = "BRDuplex";
  55. constexpr char kBrotherMonoColor[] = "BRMonoColor";
  56. constexpr char kBrotherPrintQuality[] = "BRPrintQuality";
  57. // Epson printer specific options.
  58. constexpr char kEpsonInk[] = "Ink";
  59. constexpr char kEpsonColor[] = "COLOR";
  60. constexpr char kEpsonMono[] = "MONO";
  61. // HP printer specific options.
  62. constexpr char kHpColorMode[] = "HPColorMode";
  63. constexpr char kHpColorPrint[] = "ColorPrint";
  64. constexpr char kHpGrayscalePrint[] = "GrayscalePrint";
  65. // Samsung printer specific options.
  66. constexpr char kSamsungColorTrue[] = "True";
  67. constexpr char kSamsungColorFalse[] = "False";
  68. // Sharp printer specific options.
  69. constexpr char kSharpARCMode[] = "ARCMode";
  70. constexpr char kSharpCMColor[] = "CMColor";
  71. constexpr char kSharpCMBW[] = "CMBW";
  72. // Xerox printer specific options.
  73. constexpr char kXeroxXRXColor[] = "XRXColor";
  74. constexpr char kXeroxAutomatic[] = "Automatic";
  75. constexpr char kXeroxBW[] = "BW";
  76. int32_t GetCopiesMax(ppd_file_t* ppd) {
  77. ppd_attr_t* attr = ppdFindAttr(ppd, kCupsMaxCopies, nullptr);
  78. if (!attr || !attr->value) {
  79. return kDefaultMaxCopies;
  80. }
  81. int32_t ret;
  82. return base::StringToInt(attr->value, &ret) ? ret : kDefaultMaxCopies;
  83. }
  84. void GetDuplexSettings(ppd_file_t* ppd,
  85. std::vector<mojom::DuplexMode>* duplex_modes,
  86. mojom::DuplexMode* duplex_default) {
  87. ppd_choice_t* duplex_choice = ppdFindMarkedChoice(ppd, kDuplex);
  88. ppd_option_t* option = ppdFindOption(ppd, kDuplex);
  89. if (!option)
  90. option = ppdFindOption(ppd, kBrotherDuplex);
  91. if (!option)
  92. return;
  93. if (!duplex_choice)
  94. duplex_choice = ppdFindChoice(option, option->defchoice);
  95. if (ppdFindChoice(option, kDuplexNone))
  96. duplex_modes->push_back(mojom::DuplexMode::kSimplex);
  97. if (ppdFindChoice(option, kDuplexNoTumble))
  98. duplex_modes->push_back(mojom::DuplexMode::kLongEdge);
  99. if (ppdFindChoice(option, kDuplexTumble))
  100. duplex_modes->push_back(mojom::DuplexMode::kShortEdge);
  101. if (!duplex_choice)
  102. return;
  103. const char* choice = duplex_choice->choice;
  104. if (EqualsCaseInsensitiveASCII(choice, kDuplexNone)) {
  105. *duplex_default = mojom::DuplexMode::kSimplex;
  106. } else if (EqualsCaseInsensitiveASCII(choice, kDuplexTumble)) {
  107. *duplex_default = mojom::DuplexMode::kShortEdge;
  108. } else {
  109. *duplex_default = mojom::DuplexMode::kLongEdge;
  110. }
  111. }
  112. void GetResolutionSettings(ppd_file_t* ppd,
  113. std::vector<gfx::Size>* dpis,
  114. gfx::Size* default_dpi) {
  115. static constexpr const char* kResolutions[] = {
  116. "Resolution", "JCLResolution", "SetResolution",
  117. "CNRes_PGP", "HPPrintQuality", "LXResolution"};
  118. ppd_option_t* res;
  119. for (const char* res_name : kResolutions) {
  120. res = ppdFindOption(ppd, res_name);
  121. if (res)
  122. break;
  123. }
  124. if (!res)
  125. return;
  126. for (int i = 0; i < res->num_choices; i++) {
  127. char* choice = res->choices[i].choice;
  128. DCHECK(choice);
  129. int len = strlen(choice);
  130. if (len == 0) {
  131. VLOG(1) << "Bad PPD resolution choice: null string";
  132. continue;
  133. }
  134. int n = 0; // number of chars successfully parsed by sscanf()
  135. int dpi_x;
  136. int dpi_y;
  137. sscanf(choice, "%ddpi%n", &dpi_x, &n);
  138. if (n == len) {
  139. dpi_y = dpi_x;
  140. } else {
  141. sscanf(choice, "%dx%ddpi%n", &dpi_x, &dpi_y, &n);
  142. if (n != len) {
  143. VLOG(1) << "Bad PPD resolution choice: " << choice;
  144. continue;
  145. }
  146. }
  147. if (dpi_x <= 0 || dpi_y <= 0) {
  148. VLOG(1) << "Invalid PPD resolution dimensions: " << dpi_x << " " << dpi_y;
  149. continue;
  150. }
  151. dpis->push_back({dpi_x, dpi_y});
  152. if (!strcmp(choice, res->defchoice))
  153. *default_dpi = dpis->back();
  154. }
  155. }
  156. bool GetBasicColorModelSettings(ppd_file_t* ppd,
  157. mojom::ColorModel* color_model_for_black,
  158. mojom::ColorModel* color_model_for_color,
  159. bool* color_is_default) {
  160. ppd_option_t* color_model = ppdFindOption(ppd, kColorModel);
  161. if (!color_model)
  162. return false;
  163. if (ppdFindChoice(color_model, kBlack))
  164. *color_model_for_black = mojom::ColorModel::kBlack;
  165. else if (ppdFindChoice(color_model, kGray))
  166. *color_model_for_black = mojom::ColorModel::kGray;
  167. else if (ppdFindChoice(color_model, kGrayscale))
  168. *color_model_for_black = mojom::ColorModel::kGrayscale;
  169. if (ppdFindChoice(color_model, kColor))
  170. *color_model_for_color = mojom::ColorModel::kColor;
  171. else if (ppdFindChoice(color_model, kCMYK))
  172. *color_model_for_color = mojom::ColorModel::kCMYK;
  173. else if (ppdFindChoice(color_model, kRGB))
  174. *color_model_for_color = mojom::ColorModel::kRGB;
  175. else if (ppdFindChoice(color_model, kRGBA))
  176. *color_model_for_color = mojom::ColorModel::kRGBA;
  177. else if (ppdFindChoice(color_model, kRGB16))
  178. *color_model_for_color = mojom::ColorModel::kRGB16;
  179. else if (ppdFindChoice(color_model, kCMY))
  180. *color_model_for_color = mojom::ColorModel::kCMY;
  181. else if (ppdFindChoice(color_model, kKCMY))
  182. *color_model_for_color = mojom::ColorModel::kKCMY;
  183. else if (ppdFindChoice(color_model, kCMY_K))
  184. *color_model_for_color = mojom::ColorModel::kCMYPlusK;
  185. ppd_choice_t* marked_choice = ppdFindMarkedChoice(ppd, kColorModel);
  186. if (!marked_choice)
  187. marked_choice = ppdFindChoice(color_model, color_model->defchoice);
  188. if (marked_choice) {
  189. *color_is_default =
  190. !EqualsCaseInsensitiveASCII(marked_choice->choice, kBlack) &&
  191. !EqualsCaseInsensitiveASCII(marked_choice->choice, kGray) &&
  192. !EqualsCaseInsensitiveASCII(marked_choice->choice, kGrayscale);
  193. }
  194. return true;
  195. }
  196. bool GetPrintOutModeColorSettings(ppd_file_t* ppd,
  197. mojom::ColorModel* color_model_for_black,
  198. mojom::ColorModel* color_model_for_color,
  199. bool* color_is_default) {
  200. ppd_option_t* printout_mode = ppdFindOption(ppd, kPrintoutMode);
  201. if (!printout_mode)
  202. return false;
  203. *color_model_for_color = mojom::ColorModel::kPrintoutModeNormal;
  204. *color_model_for_black = mojom::ColorModel::kPrintoutModeNormal;
  205. // Check to see if NORMAL_GRAY value is supported by PrintoutMode.
  206. // If NORMAL_GRAY is not supported, NORMAL value is used to
  207. // represent grayscale. If NORMAL_GRAY is supported, NORMAL is used to
  208. // represent color.
  209. if (ppdFindChoice(printout_mode, kNormalGray))
  210. *color_model_for_black = mojom::ColorModel::kPrintoutModeNormalGray;
  211. // Get the default marked choice to identify the default color setting
  212. // value.
  213. ppd_choice_t* printout_mode_choice = ppdFindMarkedChoice(ppd, kPrintoutMode);
  214. if (!printout_mode_choice) {
  215. printout_mode_choice =
  216. ppdFindChoice(printout_mode, printout_mode->defchoice);
  217. }
  218. if (printout_mode_choice) {
  219. if (EqualsCaseInsensitiveASCII(printout_mode_choice->choice, kNormalGray) ||
  220. EqualsCaseInsensitiveASCII(printout_mode_choice->choice, kHighGray) ||
  221. EqualsCaseInsensitiveASCII(printout_mode_choice->choice, kDraftGray)) {
  222. *color_model_for_black = mojom::ColorModel::kPrintoutModeNormalGray;
  223. *color_is_default = false;
  224. }
  225. }
  226. return true;
  227. }
  228. bool GetColorModeSettings(ppd_file_t* ppd,
  229. mojom::ColorModel* color_model_for_black,
  230. mojom::ColorModel* color_model_for_color,
  231. bool* color_is_default) {
  232. // Samsung printers use "ColorMode" attribute in their PPDs.
  233. ppd_option_t* color_mode_option = ppdFindOption(ppd, kColorMode);
  234. if (!color_mode_option)
  235. return false;
  236. if (ppdFindChoice(color_mode_option, kColor) ||
  237. ppdFindChoice(color_mode_option, kSamsungColorTrue)) {
  238. *color_model_for_color = mojom::ColorModel::kColorModeColor;
  239. }
  240. if (ppdFindChoice(color_mode_option, kMonochrome) ||
  241. ppdFindChoice(color_mode_option, kSamsungColorFalse)) {
  242. *color_model_for_black = mojom::ColorModel::kColorModeMonochrome;
  243. }
  244. ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kColorMode);
  245. if (!mode_choice) {
  246. mode_choice =
  247. ppdFindChoice(color_mode_option, color_mode_option->defchoice);
  248. }
  249. if (mode_choice) {
  250. *color_is_default =
  251. EqualsCaseInsensitiveASCII(mode_choice->choice, kColor) ||
  252. EqualsCaseInsensitiveASCII(mode_choice->choice, kSamsungColorTrue);
  253. }
  254. return true;
  255. }
  256. bool GetBrotherColorSettings(ppd_file_t* ppd,
  257. mojom::ColorModel* color_model_for_black,
  258. mojom::ColorModel* color_model_for_color,
  259. bool* color_is_default) {
  260. // Some Brother printers use "BRMonoColor" attribute in their PPDs.
  261. // Some Brother printers use "BRPrintQuality" attribute in their PPDs.
  262. ppd_option_t* color_mode_option = ppdFindOption(ppd, kBrotherMonoColor);
  263. if (!color_mode_option)
  264. color_mode_option = ppdFindOption(ppd, kBrotherPrintQuality);
  265. if (!color_mode_option)
  266. return false;
  267. if (ppdFindChoice(color_mode_option, kFullColor))
  268. *color_model_for_color = mojom::ColorModel::kBrotherCUPSColor;
  269. else if (ppdFindChoice(color_mode_option, kColor))
  270. *color_model_for_color = mojom::ColorModel::kBrotherBRScript3Color;
  271. if (ppdFindChoice(color_mode_option, kMono))
  272. *color_model_for_black = mojom::ColorModel::kBrotherCUPSMono;
  273. else if (ppdFindChoice(color_mode_option, kBlack))
  274. *color_model_for_black = mojom::ColorModel::kBrotherBRScript3Black;
  275. ppd_choice_t* marked_choice = ppdFindMarkedChoice(ppd, kColorMode);
  276. if (!marked_choice) {
  277. marked_choice =
  278. ppdFindChoice(color_mode_option, color_mode_option->defchoice);
  279. }
  280. if (marked_choice) {
  281. *color_is_default =
  282. !EqualsCaseInsensitiveASCII(marked_choice->choice, kBlack) &&
  283. !EqualsCaseInsensitiveASCII(marked_choice->choice, kMono);
  284. }
  285. return true;
  286. }
  287. bool GetHPColorSettings(ppd_file_t* ppd,
  288. mojom::ColorModel* color_model_for_black,
  289. mojom::ColorModel* color_model_for_color,
  290. bool* color_is_default) {
  291. // Some HP printers use "Color/Color Model" attribute in their PPDs.
  292. ppd_option_t* color_mode_option = ppdFindOption(ppd, kColor);
  293. if (!color_mode_option)
  294. return false;
  295. if (ppdFindChoice(color_mode_option, kColor))
  296. *color_model_for_color = mojom::ColorModel::kHPColorColor;
  297. if (ppdFindChoice(color_mode_option, kBlack))
  298. *color_model_for_black = mojom::ColorModel::kHPColorBlack;
  299. ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kColorMode);
  300. if (!mode_choice) {
  301. mode_choice =
  302. ppdFindChoice(color_mode_option, color_mode_option->defchoice);
  303. }
  304. if (mode_choice) {
  305. *color_is_default = EqualsCaseInsensitiveASCII(mode_choice->choice, kColor);
  306. }
  307. return true;
  308. }
  309. bool GetHPColorModeSettings(ppd_file_t* ppd,
  310. mojom::ColorModel* color_model_for_black,
  311. mojom::ColorModel* color_model_for_color,
  312. bool* color_is_default) {
  313. // Some HP printers use "HPColorMode/Mode" attribute in their PPDs.
  314. ppd_option_t* color_mode_option = ppdFindOption(ppd, kHpColorMode);
  315. if (!color_mode_option)
  316. return false;
  317. if (ppdFindChoice(color_mode_option, kHpColorPrint))
  318. *color_model_for_color = mojom::ColorModel::kHPColorColor;
  319. if (ppdFindChoice(color_mode_option, kHpGrayscalePrint))
  320. *color_model_for_black = mojom::ColorModel::kHPColorBlack;
  321. ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kHpColorMode);
  322. if (!mode_choice) {
  323. mode_choice =
  324. ppdFindChoice(color_mode_option, color_mode_option->defchoice);
  325. }
  326. if (mode_choice) {
  327. *color_is_default =
  328. EqualsCaseInsensitiveASCII(mode_choice->choice, kHpColorPrint);
  329. }
  330. return true;
  331. }
  332. bool GetEpsonInkSettings(ppd_file_t* ppd,
  333. mojom::ColorModel* color_model_for_black,
  334. mojom::ColorModel* color_model_for_color,
  335. bool* color_is_default) {
  336. // Epson printers use "Ink" attribute in their PPDs.
  337. ppd_option_t* color_mode_option = ppdFindOption(ppd, kEpsonInk);
  338. if (!color_mode_option)
  339. return false;
  340. if (ppdFindChoice(color_mode_option, kEpsonColor))
  341. *color_model_for_color = mojom::ColorModel::kEpsonInkColor;
  342. if (ppdFindChoice(color_mode_option, kEpsonMono))
  343. *color_model_for_black = mojom::ColorModel::kEpsonInkMono;
  344. ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kEpsonInk);
  345. if (!mode_choice) {
  346. mode_choice =
  347. ppdFindChoice(color_mode_option, color_mode_option->defchoice);
  348. }
  349. if (mode_choice) {
  350. *color_is_default = EqualsCaseInsensitiveASCII(mode_choice->choice, kColor);
  351. }
  352. return true;
  353. }
  354. bool GetSharpARCModeSettings(ppd_file_t* ppd,
  355. mojom::ColorModel* color_model_for_black,
  356. mojom::ColorModel* color_model_for_color,
  357. bool* color_is_default) {
  358. // Sharp printers use "ARCMode" attribute in their PPDs.
  359. ppd_option_t* color_mode_option = ppdFindOption(ppd, kSharpARCMode);
  360. if (!color_mode_option)
  361. return false;
  362. if (ppdFindChoice(color_mode_option, kSharpCMColor))
  363. *color_model_for_color = mojom::ColorModel::kSharpARCModeCMColor;
  364. if (ppdFindChoice(color_mode_option, kSharpCMBW))
  365. *color_model_for_black = mojom::ColorModel::kSharpARCModeCMBW;
  366. ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kSharpARCMode);
  367. if (!mode_choice) {
  368. mode_choice =
  369. ppdFindChoice(color_mode_option, color_mode_option->defchoice);
  370. }
  371. if (mode_choice) {
  372. // Many Sharp printers use "CMAuto" as the default color mode.
  373. *color_is_default =
  374. !EqualsCaseInsensitiveASCII(mode_choice->choice, kSharpCMBW);
  375. }
  376. return true;
  377. }
  378. bool GetXeroxColorSettings(ppd_file_t* ppd,
  379. mojom::ColorModel* color_model_for_black,
  380. mojom::ColorModel* color_model_for_color,
  381. bool* color_is_default) {
  382. // Some Xerox printers use "XRXColor" attribute in their PPDs.
  383. ppd_option_t* color_mode_option = ppdFindOption(ppd, kXeroxXRXColor);
  384. if (!color_mode_option)
  385. return false;
  386. if (ppdFindChoice(color_mode_option, kXeroxAutomatic))
  387. *color_model_for_color = mojom::ColorModel::kXeroxXRXColorAutomatic;
  388. if (ppdFindChoice(color_mode_option, kXeroxBW))
  389. *color_model_for_black = mojom::ColorModel::kXeroxXRXColorBW;
  390. ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kXeroxXRXColor);
  391. if (!mode_choice) {
  392. mode_choice =
  393. ppdFindChoice(color_mode_option, color_mode_option->defchoice);
  394. }
  395. if (mode_choice) {
  396. // Many Xerox printers use "Automatic" as the default color mode.
  397. *color_is_default =
  398. !EqualsCaseInsensitiveASCII(mode_choice->choice, kXeroxBW);
  399. }
  400. return true;
  401. }
  402. bool GetProcessColorModelSettings(ppd_file_t* ppd,
  403. mojom::ColorModel* color_model_for_black,
  404. mojom::ColorModel* color_model_for_color,
  405. bool* color_is_default) {
  406. // Canon printers use "ProcessColorModel" attribute in their PPDs.
  407. ppd_option_t* color_mode_option = ppdFindOption(ppd, kProcessColorModel);
  408. if (!color_mode_option)
  409. return false;
  410. if (ppdFindChoice(color_mode_option, kRGB))
  411. *color_model_for_color = mojom::ColorModel::kProcessColorModelRGB;
  412. else if (ppdFindChoice(color_mode_option, kCMYK))
  413. *color_model_for_color = mojom::ColorModel::kProcessColorModelCMYK;
  414. if (ppdFindChoice(color_mode_option, kGreyscale))
  415. *color_model_for_black = mojom::ColorModel::kProcessColorModelGreyscale;
  416. ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kProcessColorModel);
  417. if (!mode_choice) {
  418. mode_choice =
  419. ppdFindChoice(color_mode_option, color_mode_option->defchoice);
  420. }
  421. if (mode_choice) {
  422. *color_is_default =
  423. !EqualsCaseInsensitiveASCII(mode_choice->choice, kGreyscale);
  424. }
  425. return true;
  426. }
  427. bool GetColorModelSettings(ppd_file_t* ppd,
  428. mojom::ColorModel* cm_black,
  429. mojom::ColorModel* cm_color,
  430. bool* is_color) {
  431. bool is_color_device = false;
  432. ppd_attr_t* attr = ppdFindAttr(ppd, kColorDevice, nullptr);
  433. if (attr && attr->value)
  434. is_color_device = ppd->color_device;
  435. *is_color = is_color_device;
  436. return (is_color_device &&
  437. GetBasicColorModelSettings(ppd, cm_black, cm_color, is_color)) ||
  438. GetPrintOutModeColorSettings(ppd, cm_black, cm_color, is_color) ||
  439. GetColorModeSettings(ppd, cm_black, cm_color, is_color) ||
  440. GetHPColorSettings(ppd, cm_black, cm_color, is_color) ||
  441. GetHPColorModeSettings(ppd, cm_black, cm_color, is_color) ||
  442. GetBrotherColorSettings(ppd, cm_black, cm_color, is_color) ||
  443. GetEpsonInkSettings(ppd, cm_black, cm_color, is_color) ||
  444. GetSharpARCModeSettings(ppd, cm_black, cm_color, is_color) ||
  445. GetXeroxColorSettings(ppd, cm_black, cm_color, is_color) ||
  446. GetProcessColorModelSettings(ppd, cm_black, cm_color, is_color);
  447. }
  448. // Default port for IPP print servers.
  449. const int kDefaultIPPServerPort = 631;
  450. } // namespace
  451. // Helper wrapper around http_t structure, with connection and cleanup
  452. // functionality.
  453. HttpConnectionCUPS::HttpConnectionCUPS(const GURL& print_server_url,
  454. http_encryption_t encryption,
  455. bool blocking)
  456. : http_(nullptr) {
  457. // If we have an empty url, use default print server.
  458. if (print_server_url.is_empty())
  459. return;
  460. int port = print_server_url.IntPort();
  461. if (port == url::PORT_UNSPECIFIED)
  462. port = kDefaultIPPServerPort;
  463. if (httpConnect2) {
  464. http_ = httpConnect2(print_server_url.host().c_str(), port,
  465. /*addrlist=*/nullptr, AF_UNSPEC, encryption,
  466. blocking ? 1 : 0, kCupsTimeout.InMilliseconds(),
  467. /*cancel=*/nullptr);
  468. } else {
  469. // Continue to use deprecated CUPS calls because because older Linux
  470. // distribution such as RHEL/CentOS 7 are shipped with CUPS 1.6.
  471. http_ =
  472. httpConnectEncrypt(print_server_url.host().c_str(), port, encryption);
  473. }
  474. if (!http_) {
  475. LOG(ERROR) << "CP_CUPS: Failed connecting to print server: "
  476. << print_server_url;
  477. return;
  478. }
  479. if (!httpConnect2)
  480. httpBlocking(http_, blocking ? 1 : 0);
  481. }
  482. HttpConnectionCUPS::~HttpConnectionCUPS() {
  483. if (http_)
  484. httpClose(http_);
  485. }
  486. http_t* HttpConnectionCUPS::http() {
  487. return http_;
  488. }
  489. bool ParsePpdCapabilities(cups_dest_t* dest,
  490. base::StringPiece locale,
  491. base::StringPiece printer_capabilities,
  492. PrinterSemanticCapsAndDefaults* printer_info) {
  493. // A file created while in a sandbox will be automatically deleted once all
  494. // handles to it have been closed. This precludes the use of multiple
  495. // operations against a file path.
  496. //
  497. // Underlying CUPS libraries process the PPD using standard I/O file
  498. // descriptors, so `FILE` stream APIs that don't support that are not an
  499. // option (e.g., can't use fmemopen()).
  500. //
  501. // Previous attempts to just read & write with a single disk `FILE` stream
  502. // demonstrated occasional data corruption in the wild, so resort to working
  503. // directly with lower-level file descriptors.
  504. base::FilePath temp_dir;
  505. if (!base::GetTempDir(&temp_dir))
  506. return false;
  507. base::FilePath ppd_file_path;
  508. base::ScopedFD ppd_fd =
  509. base::CreateAndOpenFdForTemporaryFileInDir(temp_dir, &ppd_file_path);
  510. if (!ppd_fd.is_valid() ||
  511. !base::WriteFileDescriptor(ppd_fd.get(), printer_capabilities) ||
  512. lseek(ppd_fd.get(), 0, SEEK_SET) == -1) {
  513. return false;
  514. }
  515. // We release ownership of `ppd_fd` here because ppdOpenFd() assumes ownership
  516. // of it in all but one case (see below).
  517. int unowned_ppd_fd = ppd_fd.release();
  518. ppd_file_t* ppd = ppdOpenFd(unowned_ppd_fd);
  519. if (!ppd) {
  520. int line = 0;
  521. ppd_status_t ppd_status = ppdLastError(&line);
  522. LOG(ERROR) << "Failed to open PDD file: error " << ppd_status << " at line "
  523. << line << ", " << ppdErrorString(ppd_status);
  524. if (ppd_status == PPD_FILE_OPEN_ERROR) {
  525. // Normally ppdOpenFd assumes ownership of the file descriptor we give it,
  526. // regardless of success or failure. The one exception is when it fails
  527. // with PPD_FILE_OPEN_ERROR. In that case ownership is retained by the
  528. // caller, so we must explicitly close it.
  529. close(unowned_ppd_fd);
  530. }
  531. return false;
  532. }
  533. ppdMarkDefaults(ppd);
  534. if (dest)
  535. cupsMarkOptions(ppd, dest->num_options, dest->options);
  536. PrinterSemanticCapsAndDefaults caps;
  537. caps.collate_capable = true;
  538. caps.collate_default = true;
  539. caps.copies_max = GetCopiesMax(ppd);
  540. GetDuplexSettings(ppd, &caps.duplex_modes, &caps.duplex_default);
  541. GetResolutionSettings(ppd, &caps.dpis, &caps.default_dpi);
  542. mojom::ColorModel cm_black = mojom::ColorModel::kUnknownColorModel;
  543. mojom::ColorModel cm_color = mojom::ColorModel::kUnknownColorModel;
  544. bool is_color = false;
  545. if (!GetColorModelSettings(ppd, &cm_black, &cm_color, &is_color)) {
  546. VLOG(1) << "Unknown printer color model";
  547. }
  548. caps.color_changeable =
  549. ((cm_color != mojom::ColorModel::kUnknownColorModel) &&
  550. (cm_black != mojom::ColorModel::kUnknownColorModel) &&
  551. (cm_color != cm_black));
  552. caps.color_default = is_color;
  553. caps.color_model = cm_color;
  554. caps.bw_model = cm_black;
  555. if (ppd->num_sizes > 0 && ppd->sizes) {
  556. VLOG(1) << "Paper list size - " << ppd->num_sizes;
  557. ppd_option_t* paper_option = ppdFindOption(ppd, kPageSize);
  558. bool is_default_found = false;
  559. for (int i = 0; i < ppd->num_sizes; ++i) {
  560. gfx::Size paper_size_microns(
  561. ConvertUnit(ppd->sizes[i].width, kPointsPerInch, kMicronsPerInch),
  562. ConvertUnit(ppd->sizes[i].length, kPointsPerInch, kMicronsPerInch));
  563. if (!paper_size_microns.IsEmpty()) {
  564. PrinterSemanticCapsAndDefaults::Paper paper;
  565. paper.size_um = paper_size_microns;
  566. paper.vendor_id = ppd->sizes[i].name;
  567. if (paper_option) {
  568. ppd_choice_t* paper_choice =
  569. ppdFindChoice(paper_option, ppd->sizes[i].name);
  570. // Human readable paper name should be UTF-8 encoded, but some PPDs
  571. // do not follow this standard.
  572. if (paper_choice && base::IsStringUTF8(paper_choice->text)) {
  573. paper.display_name = paper_choice->text;
  574. }
  575. }
  576. caps.papers.push_back(paper);
  577. if (ppd->sizes[i].marked) {
  578. caps.default_paper = paper;
  579. is_default_found = true;
  580. }
  581. }
  582. }
  583. if (!is_default_found) {
  584. gfx::Size locale_paper_microns =
  585. GetDefaultPaperSizeFromLocaleMicrons(locale);
  586. for (const PrinterSemanticCapsAndDefaults::Paper& paper : caps.papers) {
  587. // Set epsilon to 500 microns to allow tolerance of rounded paper sizes.
  588. // While the above utility function returns paper sizes in microns, they
  589. // are still rounded to the nearest millimeter (1000 microns).
  590. constexpr int kSizeEpsilon = 500;
  591. if (SizesEqualWithinEpsilon(paper.size_um, locale_paper_microns,
  592. kSizeEpsilon)) {
  593. caps.default_paper = paper;
  594. is_default_found = true;
  595. break;
  596. }
  597. }
  598. // If no default was set in the PPD or if the locale default is not within
  599. // the printer's capabilities, select the first on the list.
  600. if (!is_default_found)
  601. caps.default_paper = caps.papers[0];
  602. }
  603. }
  604. ppdClose(ppd);
  605. *printer_info = caps;
  606. return true;
  607. }
  608. } // namespace printing