1 // Copyright 2014 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/print_settings_conversion.h"
6 
7 #include <stddef.h>
8 
9 #include <algorithm>
10 #include <cmath>
11 #include <memory>
12 #include <string>
13 #include <utility>
14 
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/time/time.h"
18 #include "base/values.h"
19 #include "build/build_config.h"
20 #include "build/chromeos_buildflags.h"
21 #include "printing/mojom/print.mojom.h"
22 #include "printing/print_job_constants.h"
23 #include "printing/print_settings.h"
24 #include "printing/units.h"
25 
26 namespace printing {
27 
28 namespace {
29 
30 // Note: If this code crashes, then the caller has passed in invalid |settings|.
31 // Fix the caller, instead of trying to avoid the crash here.
GetCustomMarginsFromJobSettings(const base::Value & settings)32 PageMargins GetCustomMarginsFromJobSettings(const base::Value& settings) {
33   PageMargins margins_in_points;
34   const base::Value* custom_margins = settings.FindKey(kSettingMarginsCustom);
35   margins_in_points.top = custom_margins->FindIntKey(kSettingMarginTop).value();
36   margins_in_points.bottom =
37       custom_margins->FindIntKey(kSettingMarginBottom).value();
38   margins_in_points.left =
39       custom_margins->FindIntKey(kSettingMarginLeft).value();
40   margins_in_points.right =
41       custom_margins->FindIntKey(kSettingMarginRight).value();
42   return margins_in_points;
43 }
44 
SetMarginsToJobSettings(const std::string & json_path,const PageMargins & margins,base::DictionaryValue * job_settings)45 void SetMarginsToJobSettings(const std::string& json_path,
46                              const PageMargins& margins,
47                              base::DictionaryValue* job_settings) {
48   auto dict = std::make_unique<base::DictionaryValue>();
49   dict->SetInteger(kSettingMarginTop, margins.top);
50   dict->SetInteger(kSettingMarginBottom, margins.bottom);
51   dict->SetInteger(kSettingMarginLeft, margins.left);
52   dict->SetInteger(kSettingMarginRight, margins.right);
53   job_settings->Set(json_path, std::move(dict));
54 }
55 
SetSizeToJobSettings(const std::string & json_path,const gfx::Size & size,base::DictionaryValue * job_settings)56 void SetSizeToJobSettings(const std::string& json_path,
57                           const gfx::Size& size,
58                           base::DictionaryValue* job_settings) {
59   auto dict = std::make_unique<base::DictionaryValue>();
60   dict->SetInteger("width", size.width());
61   dict->SetInteger("height", size.height());
62   job_settings->Set(json_path, std::move(dict));
63 }
64 
SetRectToJobSettings(const std::string & json_path,const gfx::Rect & rect,base::DictionaryValue * job_settings)65 void SetRectToJobSettings(const std::string& json_path,
66                           const gfx::Rect& rect,
67                           base::DictionaryValue* job_settings) {
68   auto dict = std::make_unique<base::DictionaryValue>();
69   dict->SetInteger("x", rect.x());
70   dict->SetInteger("y", rect.y());
71   dict->SetInteger("width", rect.width());
72   dict->SetInteger("height", rect.height());
73   job_settings->Set(json_path, std::move(dict));
74 }
75 
76 }  // namespace
77 
GetPageRangesFromJobSettings(const base::Value & job_settings)78 PageRanges GetPageRangesFromJobSettings(const base::Value& job_settings) {
79   PageRanges page_ranges;
80   const base::Value* page_range_array =
81       job_settings.FindListKey(kSettingPageRange);
82   if (page_range_array) {
83     for (const base::Value& page_range : page_range_array->GetList()) {
84       if (!page_range.is_dict())
85         continue;
86 
87       base::Optional<int> from = page_range.FindIntKey(kSettingPageRangeFrom);
88       base::Optional<int> to = page_range.FindIntKey(kSettingPageRangeTo);
89       if (!from.has_value() || !to.has_value())
90         continue;
91 
92       // Page numbers are 1-based in the dictionary.
93       // Page numbers are 0-based for the printing context.
94       page_ranges.push_back(PageRange{from.value() - 1, to.value() - 1});
95     }
96   }
97   return page_ranges;
98 }
99 
PrintSettingsFromJobSettings(const base::Value & job_settings)100 std::unique_ptr<PrintSettings> PrintSettingsFromJobSettings(
101     const base::Value& job_settings) {
102   auto settings = std::make_unique<PrintSettings>();
103   base::Optional<bool> display_header_footer =
104       job_settings.FindBoolKey(kSettingHeaderFooterEnabled);
105   if (!display_header_footer.has_value())
106     return nullptr;
107 
108   settings->set_display_header_footer(display_header_footer.value());
109   if (settings->display_header_footer()) {
110     const std::string* title =
111         job_settings.FindStringKey(kSettingHeaderFooterTitle);
112     const std::string* url =
113         job_settings.FindStringKey(kSettingHeaderFooterURL);
114     if (!title || !url)
115       return nullptr;
116 
117     settings->set_title(base::UTF8ToUTF16(*title));
118     settings->set_url(base::UTF8ToUTF16(*url));
119   }
120 
121   base::Optional<bool> backgrounds =
122       job_settings.FindBoolKey(kSettingShouldPrintBackgrounds);
123   base::Optional<bool> selection_only =
124       job_settings.FindBoolKey(kSettingShouldPrintSelectionOnly);
125   if (!backgrounds.has_value() || !selection_only.has_value())
126     return nullptr;
127 
128   settings->set_should_print_backgrounds(backgrounds.value());
129   settings->set_selection_only(selection_only.value());
130 
131   PrintSettings::RequestedMedia requested_media;
132   const base::Value* media_size_value = job_settings.FindKeyOfType(
133       kSettingMediaSize, base::Value::Type::DICTIONARY);
134   if (media_size_value) {
135     base::Optional<int> width_microns =
136         media_size_value->FindIntKey(kSettingMediaSizeWidthMicrons);
137     base::Optional<int> height_microns =
138         media_size_value->FindIntKey(kSettingMediaSizeHeightMicrons);
139     if (width_microns.has_value() && height_microns.has_value()) {
140       requested_media.size_microns =
141           gfx::Size(width_microns.value(), height_microns.value());
142     }
143 
144     const std::string* vendor_id =
145         media_size_value->FindStringKey(kSettingMediaSizeVendorId);
146     if (vendor_id && !vendor_id->empty())
147       requested_media.vendor_id = *vendor_id;
148   }
149   settings->set_requested_media(requested_media);
150 
151   mojom::MarginType margin_type = static_cast<mojom::MarginType>(
152       job_settings.FindIntKey(kSettingMarginsType)
153           .value_or(static_cast<int>(mojom::MarginType::kDefaultMargins)));
154   if (margin_type != mojom::MarginType::kDefaultMargins &&
155       margin_type != mojom::MarginType::kNoMargins &&
156       margin_type != mojom::MarginType::kCustomMargins &&
157       margin_type != mojom::MarginType::kPrintableAreaMargins) {
158     margin_type = mojom::MarginType::kDefaultMargins;
159   }
160   settings->set_margin_type(margin_type);
161 
162   if (margin_type == mojom::MarginType::kCustomMargins)
163     settings->SetCustomMargins(GetCustomMarginsFromJobSettings(job_settings));
164 
165   settings->set_ranges(GetPageRangesFromJobSettings(job_settings));
166 
167   base::Optional<bool> collate = job_settings.FindBoolKey(kSettingCollate);
168   base::Optional<int> copies = job_settings.FindIntKey(kSettingCopies);
169   base::Optional<int> color = job_settings.FindIntKey(kSettingColor);
170   base::Optional<int> duplex_mode = job_settings.FindIntKey(kSettingDuplexMode);
171   base::Optional<bool> landscape = job_settings.FindBoolKey(kSettingLandscape);
172   base::Optional<int> scale_factor =
173       job_settings.FindIntKey(kSettingScaleFactor);
174   base::Optional<bool> rasterize_pdf =
175       job_settings.FindBoolKey(kSettingRasterizePdf);
176   base::Optional<int> pages_per_sheet =
177       job_settings.FindIntKey(kSettingPagesPerSheet);
178 
179   if (!collate.has_value() || !copies.has_value() || !color.has_value() ||
180       !duplex_mode.has_value() || !landscape.has_value() ||
181       !scale_factor.has_value() || !rasterize_pdf.has_value() ||
182       !pages_per_sheet.has_value()) {
183     return nullptr;
184   }
185 
186   base::Optional<int> dpi_horizontal =
187       job_settings.FindIntKey(kSettingDpiHorizontal);
188   base::Optional<int> dpi_vertical =
189       job_settings.FindIntKey(kSettingDpiVertical);
190   if (!dpi_horizontal.has_value() || !dpi_vertical.has_value())
191     return nullptr;
192   settings->set_dpi_xy(dpi_horizontal.value(), dpi_vertical.value());
193 
194   settings->set_collate(collate.value());
195   settings->set_copies(copies.value());
196   settings->SetOrientation(landscape.value());
197   settings->set_device_name(
198       base::UTF8ToUTF16(*job_settings.FindStringKey(kSettingDeviceName)));
199   settings->set_duplex_mode(
200       static_cast<mojom::DuplexMode>(duplex_mode.value()));
201   settings->set_color(static_cast<mojom::ColorModel>(color.value()));
202   settings->set_scale_factor(static_cast<double>(scale_factor.value()) / 100.0);
203   settings->set_rasterize_pdf(rasterize_pdf.value());
204   settings->set_pages_per_sheet(pages_per_sheet.value());
205   base::Optional<bool> is_modifiable =
206       job_settings.FindBoolKey(kSettingPreviewModifiable);
207   if (is_modifiable.has_value()) {
208     settings->set_is_modifiable(is_modifiable.value());
209 #if defined(OS_WIN)
210     settings->set_print_text_with_gdi(is_modifiable.value());
211 #endif
212   }
213 
214 #if defined(OS_CHROMEOS) || ((defined(OS_LINUX) || defined(OS_BSD)) && defined(USE_CUPS))
215   const base::Value* advanced_settings =
216       job_settings.FindDictKey(kSettingAdvancedSettings);
217   if (advanced_settings) {
218     for (const auto& item : advanced_settings->DictItems())
219       settings->advanced_settings().emplace(item.first, item.second.Clone());
220   }
221 #endif  // defined(OS_CHROMEOS) || ((defined(OS_LINUX) || defined(OS_BSD)) && defined(USE_CUPS))
222 
223 #if BUILDFLAG(IS_ASH)
224   bool send_user_info =
225       job_settings.FindBoolKey(kSettingSendUserInfo).value_or(false);
226   settings->set_send_user_info(send_user_info);
227   if (send_user_info) {
228     const std::string* username = job_settings.FindStringKey(kSettingUsername);
229     if (username)
230       settings->set_username(*username);
231   }
232 
233   const std::string* pin_value = job_settings.FindStringKey(kSettingPinValue);
234   if (pin_value)
235     settings->set_pin_value(*pin_value);
236 #endif  // BUILDFLAG(IS_ASH)
237 
238   return settings;
239 }
240 
PrintSettingsToJobSettingsDebug(const PrintSettings & settings,base::DictionaryValue * job_settings)241 void PrintSettingsToJobSettingsDebug(const PrintSettings& settings,
242                                      base::DictionaryValue* job_settings) {
243   job_settings->SetBoolean(kSettingHeaderFooterEnabled,
244                            settings.display_header_footer());
245   job_settings->SetString(kSettingHeaderFooterTitle, settings.title());
246   job_settings->SetString(kSettingHeaderFooterURL, settings.url());
247   job_settings->SetBoolean(kSettingShouldPrintBackgrounds,
248                            settings.should_print_backgrounds());
249   job_settings->SetBoolean(kSettingShouldPrintSelectionOnly,
250                            settings.selection_only());
251   job_settings->SetInteger(kSettingMarginsType,
252                            static_cast<int>(settings.margin_type()));
253   if (!settings.ranges().empty()) {
254     auto page_range_array = std::make_unique<base::ListValue>();
255     for (size_t i = 0; i < settings.ranges().size(); ++i) {
256       auto dict = std::make_unique<base::DictionaryValue>();
257       dict->SetInteger(kSettingPageRangeFrom, settings.ranges()[i].from + 1);
258       dict->SetInteger(kSettingPageRangeTo, settings.ranges()[i].to + 1);
259       page_range_array->Append(std::move(dict));
260     }
261     job_settings->Set(kSettingPageRange, std::move(page_range_array));
262   }
263 
264   job_settings->SetBoolean(kSettingCollate, settings.collate());
265   job_settings->SetInteger(kSettingCopies, settings.copies());
266   job_settings->SetInteger(kSettingColor, static_cast<int>(settings.color()));
267   job_settings->SetInteger(kSettingDuplexMode,
268                            static_cast<int>(settings.duplex_mode()));
269   job_settings->SetBoolean(kSettingLandscape, settings.landscape());
270   job_settings->SetString(kSettingDeviceName, settings.device_name());
271   job_settings->SetInteger(kSettingPagesPerSheet, settings.pages_per_sheet());
272 
273   // Following values are not read form JSON by InitSettings, so do not have
274   // common public constants. So just serialize in "debug" section.
275   auto debug = std::make_unique<base::DictionaryValue>();
276   debug->SetInteger("dpi", settings.dpi());
277   debug->SetInteger("deviceUnitsPerInch", settings.device_units_per_inch());
278   debug->SetBoolean("support_alpha_blend", settings.should_print_backgrounds());
279   debug->SetString("media_vendor_id", settings.requested_media().vendor_id);
280   SetSizeToJobSettings("media_size", settings.requested_media().size_microns,
281                        debug.get());
282   SetMarginsToJobSettings("requested_custom_margins_in_points",
283                           settings.requested_custom_margins_in_points(),
284                           debug.get());
285   const PageSetup& page_setup = settings.page_setup_device_units();
286   SetMarginsToJobSettings("effective_margins", page_setup.effective_margins(),
287                           debug.get());
288   SetSizeToJobSettings("physical_size", page_setup.physical_size(),
289                        debug.get());
290   SetRectToJobSettings("overlay_area", page_setup.overlay_area(), debug.get());
291   SetRectToJobSettings("content_area", page_setup.content_area(), debug.get());
292   SetRectToJobSettings("printable_area", page_setup.printable_area(),
293                        debug.get());
294   job_settings->Set("debug", std::move(debug));
295 }
296 
297 }  // namespace printing
298