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