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/printing_context_chromeos.h" | |
6 | |
7 #include <cups/cups.h> | |
8 #include <stdint.h> | |
9 #include <unicode/ulocdata.h> | |
10 | |
11 #include <memory> | |
12 #include <vector> | |
13 | |
14 #include "base/logging.h" | |
15 #include "base/memory/ptr_util.h" | |
16 #include "base/strings/string_util.h" | |
17 #include "base/strings/stringprintf.h" | |
18 #include "base/strings/utf_string_conversions.h" | |
19 #include "printing/backend/cups_connection.h" | |
20 #include "printing/backend/cups_ipp_util.h" | |
21 #include "printing/backend/cups_printer.h" | |
22 #include "printing/metafile.h" | |
23 #include "printing/print_job_constants.h" | |
24 #include "printing/print_settings.h" | |
25 #include "printing/printing_context_no_system_dialog.h" | |
26 #include "printing/units.h" | |
27 | |
28 namespace printing { | |
29 | |
30 namespace { | |
31 | |
32 const char kPageSize[] = "media"; | |
33 const char kResolution[] = "printer-resolution"; | |
34 | |
35 // PWG 5100.13: JPS3 | |
36 const char kColorMode[] = "print-color-mode"; | |
37 | |
38 const char kColorAuto[] = "auto"; | |
39 const char kPwgColor[] = "color"; | |
40 const char kPwgMonochrome[] = "monochrome"; | |
41 const char kColorBiLevel[] = "bi-level"; | |
42 const char kHighlight[] = "highlight"; | |
43 const char kProcessBiLevel[] = "process-bi-level"; | |
44 const char kProcessMonochrome[] = "process-monochrome"; | |
45 | |
46 bool use_native_cups_ = false; | |
Lei Zhang
2016/07/08 01:35:26
g_use_native_cups ... but do you really need this?
skau
2016/07/09 00:28:33
Nope. I had it in for debugging.
| |
47 | |
48 // convert from a ColorMode setting to a print-color-mode value from PWG 5100.13 | |
49 const char* GetColorModelForMode(int color_mode) { | |
50 const char* mode_string; | |
51 switch (color_mode) { | |
52 case COLOR: | |
53 case CMYK: | |
54 case CMY: | |
55 case KCMY: | |
56 case CMY_K: | |
57 case RGB: | |
58 case RGB16: | |
59 case RGBA: | |
60 case COLORMODE_COLOR: | |
61 case HP_COLOR_COLOR: | |
62 case PRINTOUTMODE_NORMAL: | |
63 case PROCESSCOLORMODEL_CMYK: | |
64 case PROCESSCOLORMODEL_RGB: | |
65 mode_string = kPwgColor; | |
66 break; | |
67 case GRAY: | |
68 case BLACK: | |
69 case GRAYSCALE: | |
70 case COLORMODE_MONOCHROME: | |
71 case HP_COLOR_BLACK: | |
72 case PRINTOUTMODE_NORMAL_GRAY: | |
73 case PROCESSCOLORMODEL_GREYSCALE: | |
74 mode_string = kPwgMonochrome; | |
75 break; | |
76 default: | |
77 mode_string = nullptr; | |
78 LOG(WARNING) << "Unrecognized color mode"; | |
79 break; | |
80 } | |
81 | |
82 return mode_string; | |
83 } | |
84 | |
85 char* CopyStringToChar(const base::StringPiece value, char** dst) { | |
86 int value_len = value.size() + 1; | |
87 *dst = new char[value_len]; | |
88 value.copy(*dst, value_len); | |
89 (*dst)[value_len - 1] = '\0'; | |
90 | |
91 return *dst; | |
92 } | |
93 | |
94 cups_option_t ConstructOption(base::StringPiece name, base::StringPiece value) { | |
95 cups_option_t opt; | |
96 opt.name = CopyStringToChar(name, &opt.name); | |
97 opt.value = CopyStringToChar(value, &opt.value); | |
98 | |
99 return opt; | |
100 } | |
101 | |
102 std::vector<cups_option_t> SettingsToCupsOptions( | |
103 const PrintSettings& settings) { | |
104 const char* sides = NULL; | |
105 switch (settings.duplex_mode()) { | |
106 case SIMPLEX: | |
107 sides = CUPS_SIDES_ONE_SIDED; | |
108 break; | |
109 case LONG_EDGE: | |
110 sides = CUPS_SIDES_TWO_SIDED_PORTRAIT; | |
111 break; | |
112 case SHORT_EDGE: | |
113 sides = CUPS_SIDES_TWO_SIDED_LANDSCAPE; | |
114 break; | |
115 default: | |
116 NOTREACHED(); | |
117 } | |
118 | |
119 std::vector<cups_option_t> cups_options = { | |
120 ConstructOption(kColorMode, | |
121 GetColorModelForMode(settings.color())), // color | |
122 ConstructOption(CUPS_SIDES, sides), // duplexing | |
123 ConstructOption(kPageSize, | |
124 settings.requested_media().vendor_id) // paper size | |
125 }; | |
126 | |
127 return cups_options; | |
128 } | |
129 | |
130 void SetPrintableArea(PrintSettings* settings, | |
131 const PrintSettings::RequestedMedia& media, | |
132 bool flip) { | |
133 if (!media.size_microns.IsEmpty()) { | |
134 float deviceMicronsPerDeviceUnit = | |
135 (kHundrethsMMPerInch * 10.0f) / settings->device_units_per_inch(); | |
136 gfx::Size paper_size = | |
137 gfx::Size(media.size_microns.width() / deviceMicronsPerDeviceUnit, | |
138 media.size_microns.height() / deviceMicronsPerDeviceUnit); | |
139 | |
140 gfx::Rect paper_rect(0, 0, paper_size.width(), paper_size.height()); | |
141 settings->SetPrinterPrintableArea(paper_size, paper_rect, flip); | |
142 } | |
143 } | |
144 | |
145 } // namespace | |
146 | |
147 // static | |
148 std::unique_ptr<PrintingContext> PrintingContext::Create(Delegate* delegate) { | |
149 if (PrintBackend::GetNativeCupsEnabled()) { | |
150 use_native_cups_ = true; | |
151 return base::WrapUnique(new PrintingContextChromeos(delegate)); | |
152 } | |
153 | |
154 use_native_cups_ = false; | |
155 return base::WrapUnique(new PrintingContextNoSystemDialog(delegate)); | |
156 } | |
157 | |
158 PrintingContextChromeos::PrintingContextChromeos(Delegate* delegate) | |
159 : PrintingContext(delegate), | |
160 connection_(GURL(), HTTP_ENCRYPT_NEVER, true) {} | |
161 | |
162 PrintingContextChromeos::~PrintingContextChromeos() { | |
163 ReleaseContext(); | |
164 } | |
165 | |
166 void PrintingContextChromeos::AskUserForSettings( | |
167 int max_pages, | |
168 bool has_selection, | |
169 bool is_scripted, | |
170 const PrintSettingsCallback& callback) { | |
171 // We don't want to bring up a dialog here. Ever. Just signal the callback. | |
172 callback.Run(OK); | |
173 } | |
174 | |
175 PrintingContext::Result PrintingContextChromeos::UseDefaultSettings() { | |
176 DCHECK(!in_print_job_); | |
177 | |
178 ResetSettings(); | |
179 | |
180 std::string device_name = base::UTF16ToUTF8(settings_.device_name()); | |
181 if (device_name.empty()) { | |
182 LOG(WARNING) << "No printer selected"; | |
183 return OnError(); | |
184 } | |
185 | |
186 if (settings_.dpi() == 0) { | |
187 LOG(WARNING) << "Using Default DPI"; | |
188 settings_.set_dpi(kDefaultPdfDpi); | |
189 } | |
190 | |
191 // Retrieve device information and set it | |
192 if (InitializeDevice(device_name) != OK) { | |
193 LOG(ERROR) << "Could not initialize printer"; | |
194 return OnError(); | |
195 } | |
196 | |
197 // Set printable area | |
198 DCHECK(printer_); | |
199 PrinterSemanticCapsAndDefaults::Paper paper = DefaultPaper(*printer_); | |
200 | |
201 PrintSettings::RequestedMedia media; | |
202 media.vendor_id = paper.vendor_id; | |
203 media.size_microns = paper.size_um; | |
204 settings_.set_requested_media(media); | |
205 | |
206 SetPrintableArea(&settings_, media, true /* flip landscape */); | |
207 | |
208 return OK; | |
209 } | |
210 | |
211 gfx::Size PrintingContextChromeos::GetPdfPaperSizeDeviceUnits() { | |
212 int32_t width = 0; | |
213 int32_t height = 0; | |
214 UErrorCode error = U_ZERO_ERROR; | |
215 ulocdata_getPaperSize(delegate_->GetAppLocale().c_str(), &height, &width, | |
216 &error); | |
217 if (error > U_ZERO_ERROR) { | |
218 // If the call failed, assume a paper size of 8.5 x 11 inches. | |
219 LOG(WARNING) << "ulocdata_getPaperSize failed, using 8.5 x 11, error: " | |
220 << error; | |
221 width = | |
222 static_cast<int>(kLetterWidthInch * settings_.device_units_per_inch()); | |
223 height = | |
224 static_cast<int>(kLetterHeightInch * settings_.device_units_per_inch()); | |
225 } else { | |
226 // ulocdata_getPaperSize returns the width and height in mm. | |
227 // Convert this to pixels based on the dpi. | |
228 float multiplier = 100 * settings_.device_units_per_inch(); | |
229 multiplier /= kHundrethsMMPerInch; | |
230 width *= multiplier; | |
231 height *= multiplier; | |
232 } | |
233 return gfx::Size(width, height); | |
234 } | |
235 | |
236 PrintingContext::Result PrintingContextChromeos::UpdatePrinterSettings( | |
237 bool external_preview, | |
238 bool show_system_dialog, | |
239 int page_count) { | |
240 DCHECK(!show_system_dialog); | |
241 | |
242 if (InitializeDevice(base::UTF16ToUTF8(settings_.device_name())) != OK) | |
243 return OnError(); | |
244 | |
245 // HACK We should get this from the device driver | |
Lei Zhang
2016/07/08 01:35:26
What's going on here?
skau
2016/07/09 00:28:33
Setting the dpi using printer the reported printer
| |
246 if (settings_.dpi() == 0) { | |
247 LOG(WARNING) << "Using Default DPI"; | |
248 settings_.set_dpi(kDefaultPdfDpi); | |
249 } | |
250 | |
251 // compute paper size | |
252 PrintSettings::RequestedMedia media = settings_.requested_media(); | |
253 | |
254 if (media.IsDefault()) { | |
255 DCHECK(printer_); | |
256 PrinterSemanticCapsAndDefaults::Paper paper = DefaultPaper(*printer_); | |
257 | |
258 media.vendor_id = paper.vendor_id; | |
259 media.size_microns = paper.size_um; | |
260 settings_.set_requested_media(media); | |
261 } | |
262 | |
263 SetPrintableArea(&settings_, media, true); | |
264 | |
265 options_ = SettingsToCupsOptions(settings_); | |
266 | |
267 return OK; | |
268 } | |
269 | |
270 PrintingContext::Result PrintingContextChromeos::InitializeDevice( | |
271 const std::string& device) { | |
272 DCHECK(!in_print_job_); | |
273 | |
274 CupsPrinter* printer = connection_.GetPrinter(device); | |
275 if (printer == nullptr) { | |
276 LOG(WARNING) << "Could not initialize device"; | |
277 return OnError(); | |
278 } | |
279 | |
280 printer_.reset(printer); | |
281 | |
282 return OK; | |
283 } | |
284 | |
285 PrintingContext::Result PrintingContextChromeos::InitWithSettings( | |
286 const PrintSettings& settings) { | |
287 DCHECK(!in_print_job_); | |
288 | |
289 settings_ = settings; | |
290 | |
291 return OK; | |
292 } | |
293 | |
294 PrintingContext::Result PrintingContextChromeos::NewDocument( | |
295 const base::string16& document_name) { | |
296 DCHECK(!in_print_job_); | |
297 in_print_job_ = true; | |
298 | |
299 std::string converted_name = base::UTF16ToUTF8(document_name); | |
300 std::string title = base::UTF16ToUTF8(settings_.title()); | |
301 | |
302 bool all_supported = true; | |
303 | |
304 for (auto option : options_) { | |
305 bool supported = printer_->CheckOptionSupported(option.name, option.value); | |
306 all_supported = all_supported && supported; | |
307 } | |
308 | |
309 if (!all_supported) { | |
310 LOG(WARNING) << "Unsupported options detected"; | |
311 return OnError(); | |
312 } | |
313 | |
314 ipp_status_t create_status = printer_->CreateJob(&job_id_, title, options_); | |
315 | |
316 if (job_id_ == 0) { | |
317 LOG(ERROR) << "Creating cups job failed" << ippErrorString(create_status); | |
318 return OnError(); | |
319 } | |
320 | |
321 // we only send one document, so it's always the last one | |
322 if (!printer_->StartDocument(job_id_, converted_name, true, options_)) { | |
323 LOG(ERROR) << "Starting document failed"; | |
324 return OnError(); | |
325 } | |
326 | |
327 return OK; | |
328 } | |
329 | |
330 PrintingContext::Result PrintingContextChromeos::NewPage() { | |
331 if (abort_printing_) | |
332 return CANCEL; | |
333 DCHECK(in_print_job_); | |
334 | |
335 // Intentional No-op. | |
336 | |
337 return OK; | |
338 } | |
339 | |
340 PrintingContext::Result PrintingContextChromeos::StreamData(char* buffer, | |
341 int len) { | |
342 if (abort_printing_) | |
343 return CANCEL; | |
344 DCHECK(in_print_job_); | |
345 DCHECK(printer_); | |
346 DCHECK(use_native_cups_); | |
347 | |
348 if (!printer_->StreamData(buffer, len)) | |
349 return OnError(); | |
350 | |
351 return OK; | |
352 } | |
353 | |
354 PrintingContext::Result PrintingContextChromeos::PageDone() { | |
355 if (abort_printing_) | |
356 return CANCEL; | |
357 DCHECK(in_print_job_); | |
358 | |
359 // Intentional No-op. | |
360 | |
361 return OK; | |
362 } | |
363 | |
364 PrintingContext::Result PrintingContextChromeos::DocumentDone() { | |
365 if (abort_printing_) | |
366 return CANCEL; | |
367 DCHECK(in_print_job_); | |
368 | |
369 if (!printer_->FinishDocument()) { | |
370 LOG(WARNING) << "Finishing document failed"; | |
371 return OnError(); | |
372 } | |
373 | |
374 ipp_status_t job_status = printer_->CloseJob(job_id_); | |
375 job_id_ = 0; | |
376 | |
377 if (job_status != IPP_STATUS_OK) { | |
378 LOG(WARNING) << "Closing job failed"; | |
379 return OnError(); | |
380 } | |
381 | |
382 ResetSettings(); | |
383 return OK; | |
384 } | |
385 | |
386 void PrintingContextChromeos::Cancel() { | |
387 abort_printing_ = true; | |
388 in_print_job_ = false; | |
389 } | |
390 | |
391 void PrintingContextChromeos::ReleaseContext() { | |
392 printer_.reset(nullptr); | |
393 options_.clear(); | |
394 } | |
395 | |
396 gfx::NativeDrawingContext PrintingContextChromeos::context() const { | |
397 // Intentional No-op. | |
398 return NULL; | |
399 } | |
400 | |
401 } // namespace printing | |
OLD | NEW |