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.
5 #include "printing/printing_context_chromeos.h"
9 #include <unicode/ulocdata.h>
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"
32 using ScopedCupsOption = std::unique_ptr<cups_option_t, OptionDeleter>;
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;
47 case BROTHER_CUPS_COLOR:
48 case BROTHER_BRSCRIPT3_COLOR:
50 case PRINTOUTMODE_NORMAL:
51 case PROCESSCOLORMODEL_CMYK:
52 case PROCESSCOLORMODEL_RGB:
53 mode_string = CUPS_PRINT_COLOR_MODE_COLOR;
58 case COLORMODE_MONOCHROME:
59 case BROTHER_CUPS_MONO:
60 case BROTHER_BRSCRIPT3_BLACK:
62 case PRINTOUTMODE_NORMAL_GRAY:
63 case PROCESSCOLORMODEL_GREYSCALE:
64 mode_string = CUPS_PRINT_COLOR_MODE_MONOCHROME;
67 mode_string = nullptr;
68 LOG(WARNING) << "Unrecognized color mode";
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';
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);
93 base::StringPiece GetCollateString(bool collate) {
94 return collate ? kCollated : kUncollated;
97 std::vector<ScopedCupsOption> SettingsToCupsOptions(
98 const PrintSettings& settings) {
99 const char* sides = nullptr;
100 switch (settings.duplex_mode()) {
102 sides = CUPS_SIDES_ONE_SIDED;
105 sides = CUPS_SIDES_TWO_SIDED_PORTRAIT;
108 sides = CUPS_SIDES_TWO_SIDED_LANDSCAPE;
114 std::vector<ScopedCupsOption> options;
116 ConstructOption(kIppColor,
117 GetColorModelForMode(settings.color()))); // color
118 options.push_back(ConstructOption(kIppDuplex, sides)); // duplexing
120 ConstructOption(kIppMedia,
121 settings.requested_media().vendor_id)); // paper size
123 ConstructOption(kIppCopies,
124 base::IntToString(settings.copies()))); // copies
126 ConstructOption(kIppCollate,
127 GetCollateString(settings.collate()))); // collate
132 void SetPrintableArea(PrintSettings* settings,
133 const PrintSettings::RequestedMedia& media,
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);
142 gfx::Rect paper_rect(0, 0, paper_size.width(), paper_size.height());
143 settings->SetPrinterPrintableArea(paper_size, paper_rect, flip);
150 std::unique_ptr<PrintingContext> PrintingContext::Create(Delegate* delegate) {
151 return std::make_unique<PrintingContextChromeos>(delegate);
154 PrintingContextChromeos::PrintingContextChromeos(Delegate* delegate)
155 : PrintingContext(delegate),
156 connection_(GURL(), HTTP_ENCRYPT_NEVER, true) {}
158 PrintingContextChromeos::~PrintingContextChromeos() {
162 void PrintingContextChromeos::AskUserForSettings(
166 PrintSettingsCallback callback) {
167 // We don't want to bring up a dialog here. Ever. This should not be called.
171 PrintingContext::Result PrintingContextChromeos::UseDefaultSettings() {
172 DCHECK(!in_print_job_);
176 std::string device_name = base::UTF16ToUTF8(settings_.device_name());
177 if (device_name.empty())
180 // TODO(skau): https://crbug.com/613779. See UpdatePrinterSettings for more
182 if (settings_.dpi() == 0) {
183 DVLOG(1) << "Using Default DPI";
184 settings_.set_dpi(kDefaultPdfDpi);
187 // Retrieve device information and set it
188 if (InitializeDevice(device_name) != OK) {
189 LOG(ERROR) << "Could not initialize printer";
193 // Set printable area
195 PrinterSemanticCapsAndDefaults::Paper paper = DefaultPaper(*printer_);
197 PrintSettings::RequestedMedia media;
198 media.vendor_id = paper.vendor_id;
199 media.size_microns = paper.size_um;
200 settings_.set_requested_media(media);
202 SetPrintableArea(&settings_, media, true /* flip landscape */);
207 gfx::Size PrintingContextChromeos::GetPdfPaperSizeDeviceUnits() {
210 UErrorCode error = U_ZERO_ERROR;
211 ulocdata_getPaperSize(delegate_->GetAppLocale().c_str(), &height, &width,
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: "
218 static_cast<int>(kLetterWidthInch * settings_.device_units_per_inch());
220 static_cast<int>(kLetterHeightInch * settings_.device_units_per_inch());
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;
226 height *= multiplier;
228 return gfx::Size(width, height);
231 PrintingContext::Result PrintingContextChromeos::UpdatePrinterSettings(
232 bool external_preview,
233 bool show_system_dialog,
235 DCHECK(!show_system_dialog);
237 if (InitializeDevice(base::UTF16ToUTF8(settings_.device_name())) != OK)
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);
249 // compute paper size
250 PrintSettings::RequestedMedia media = settings_.requested_media();
252 if (media.IsDefault()) {
254 PrinterSemanticCapsAndDefaults::Paper paper = DefaultPaper(*printer_);
256 media.vendor_id = paper.vendor_id;
257 media.size_microns = paper.size_um;
258 settings_.set_requested_media(media);
261 SetPrintableArea(&settings_, media, true);
266 PrintingContext::Result PrintingContextChromeos::InitializeDevice(
267 const std::string& device) {
268 DCHECK(!in_print_job_);
270 std::unique_ptr<CupsPrinter> printer = connection_.GetPrinter(device);
272 LOG(WARNING) << "Could not initialize device";
276 printer_ = std::move(printer);
281 PrintingContext::Result PrintingContextChromeos::NewDocument(
282 const base::string16& document_name) {
283 DCHECK(!in_print_job_);
284 in_print_job_ = true;
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_);
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()));
295 DVLOG(1) << "Unsupported option skipped " << option->name << ", "
300 ipp_status_t create_status = printer_->CreateJob(&job_id_, title, options);
303 DLOG(WARNING) << "Creating cups job failed"
304 << ippErrorString(create_status);
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";
317 PrintingContext::Result PrintingContextChromeos::NewPage() {
321 DCHECK(in_print_job_);
323 // Intentional No-op.
328 PrintingContext::Result PrintingContextChromeos::PageDone() {
332 DCHECK(in_print_job_);
334 // Intentional No-op.
339 PrintingContext::Result PrintingContextChromeos::DocumentDone() {
343 DCHECK(in_print_job_);
345 if (!printer_->FinishDocument()) {
346 LOG(WARNING) << "Finishing document failed";
350 ipp_status_t job_status = printer_->CloseJob(job_id_);
353 if (job_status != IPP_STATUS_OK) {
354 LOG(WARNING) << "Closing job failed";
362 void PrintingContextChromeos::Cancel() {
363 abort_printing_ = true;
364 in_print_job_ = false;
367 void PrintingContextChromeos::ReleaseContext() {
371 printing::NativeDrawingContext PrintingContextChromeos::context() const {
372 // Intentional No-op.
376 PrintingContext::Result PrintingContextChromeos::StreamData(
377 const std::vector<char>& buffer) {
381 DCHECK(in_print_job_);
384 if (!printer_->StreamData(buffer))
390 } // namespace printing