summaryrefslogtreecommitdiff
path: root/chromium/printing
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/printing
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/printing')
-rw-r--r--chromium/printing/DEPS10
-rw-r--r--chromium/printing/OWNERS8
-rw-r--r--chromium/printing/backend/cups_helper.cc396
-rw-r--r--chromium/printing/backend/cups_helper.h46
-rw-r--r--chromium/printing/backend/cups_helper_unittest.cc159
-rw-r--r--chromium/printing/backend/print_backend.cc51
-rw-r--r--chromium/printing/backend/print_backend.h112
-rw-r--r--chromium/printing/backend/print_backend_chromeos.cc74
-rw-r--r--chromium/printing/backend/print_backend_consts.cc16
-rw-r--r--chromium/printing/backend/print_backend_consts.h19
-rw-r--r--chromium/printing/backend/print_backend_cups.cc386
-rw-r--r--chromium/printing/backend/print_backend_dummy.cc24
-rw-r--r--chromium/printing/backend/print_backend_unittest.cc26
-rw-r--r--chromium/printing/backend/print_backend_win.cc255
-rw-r--r--chromium/printing/backend/printing_info_win.cc66
-rw-r--r--chromium/printing/backend/printing_info_win.h91
-rw-r--r--chromium/printing/backend/win_helper.cc344
-rw-r--r--chromium/printing/backend/win_helper.h152
-rwxr-xr-xchromium/printing/cups_config_helper.py67
-rw-r--r--chromium/printing/emf_win.cc678
-rw-r--r--chromium/printing/emf_win.h207
-rw-r--r--chromium/printing/emf_win_unittest.cc230
-rw-r--r--chromium/printing/image.cc160
-rw-r--r--chromium/printing/image.h101
-rw-r--r--chromium/printing/image_linux.cc15
-rw-r--r--chromium/printing/image_mac.cc48
-rw-r--r--chromium/printing/image_win.cc89
-rw-r--r--chromium/printing/metafile.h168
-rw-r--r--chromium/printing/metafile_impl.h26
-rw-r--r--chromium/printing/metafile_skia_wrapper.cc65
-rw-r--r--chromium/printing/metafile_skia_wrapper.h39
-rw-r--r--chromium/printing/page_number.cc83
-rw-r--r--chromium/printing/page_number.h73
-rw-r--r--chromium/printing/page_number_unittest.cc30
-rw-r--r--chromium/printing/page_range.cc25
-rw-r--r--chromium/printing/page_range.h33
-rw-r--r--chromium/printing/page_range_unittest.cc36
-rw-r--r--chromium/printing/page_setup.cc165
-rw-r--r--chromium/printing/page_setup.h103
-rw-r--r--chromium/printing/page_setup_unittest.cc274
-rw-r--r--chromium/printing/page_size_margins.cc29
-rw-r--r--chromium/printing/page_size_margins.h32
-rw-r--r--chromium/printing/pdf_metafile_cg_mac.cc319
-rw-r--r--chromium/printing/pdf_metafile_cg_mac.h93
-rw-r--r--chromium/printing/pdf_metafile_cg_mac_unittest.cc67
-rw-r--r--chromium/printing/pdf_metafile_skia.cc248
-rw-r--r--chromium/printing/pdf_metafile_skia.h84
-rw-r--r--chromium/printing/pdf_render_settings.h44
-rw-r--r--chromium/printing/print_destination_interface.h32
-rw-r--r--chromium/printing/print_destination_none.cc13
-rw-r--r--chromium/printing/print_destination_win.cc55
-rw-r--r--chromium/printing/print_dialog_gtk_interface.h57
-rw-r--r--chromium/printing/print_job_constants.cc154
-rw-r--r--chromium/printing/print_job_constants.h136
-rw-r--r--chromium/printing/print_settings.cc252
-rw-r--r--chromium/printing/print_settings.h150
-rw-r--r--chromium/printing/print_settings_initializer.cc49
-rw-r--r--chromium/printing/print_settings_initializer.h35
-rw-r--r--chromium/printing/print_settings_initializer_gtk.cc84
-rw-r--r--chromium/printing/print_settings_initializer_gtk.h40
-rw-r--r--chromium/printing/print_settings_initializer_mac.cc70
-rw-r--r--chromium/printing/print_settings_initializer_mac.h32
-rw-r--r--chromium/printing/print_settings_initializer_win.cc65
-rw-r--r--chromium/printing/print_settings_initializer_win.h36
-rw-r--r--chromium/printing/printed_document.cc235
-rw-r--r--chromium/printing/printed_document.h180
-rw-r--r--chromium/printing/printed_document_gtk.cc35
-rw-r--r--chromium/printing/printed_document_mac.cc40
-rw-r--r--chromium/printing/printed_document_win.cc84
-rw-r--r--chromium/printing/printed_page.cc41
-rw-r--r--chromium/printing/printed_page.h69
-rw-r--r--chromium/printing/printed_page_unittest.cc67
-rw-r--r--chromium/printing/printed_pages_source.h24
-rw-r--r--chromium/printing/printing.gyp329
-rw-r--r--chromium/printing/printing_context.cc87
-rw-r--r--chromium/printing/printing_context.h136
-rw-r--r--chromium/printing/printing_context_gtk.cc159
-rw-r--r--chromium/printing/printing_context_gtk.h63
-rw-r--r--chromium/printing/printing_context_mac.h101
-rw-r--r--chromium/printing/printing_context_mac.mm519
-rw-r--r--chromium/printing/printing_context_no_system_dialog.cc158
-rw-r--r--chromium/printing/printing_context_no_system_dialog.h48
-rw-r--r--chromium/printing/printing_context_win.cc778
-rw-r--r--chromium/printing/printing_context_win.h96
-rw-r--r--chromium/printing/printing_context_win_unittest.cc174
-rw-r--r--chromium/printing/printing_export.h29
-rw-r--r--chromium/printing/printing_test.h40
-rw-r--r--chromium/printing/run_all_unittests.cc9
-rw-r--r--chromium/printing/units.cc55
-rw-r--r--chromium/printing/units.h47
-rw-r--r--chromium/printing/units_unittest.cc63
91 files changed, 10792 insertions, 0 deletions
diff --git a/chromium/printing/DEPS b/chromium/printing/DEPS
new file mode 100644
index 00000000000..9c706cd6ef0
--- /dev/null
+++ b/chromium/printing/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+ "+skia/ext",
+ "+third_party/icu/source/common/unicode",
+ "+third_party/icu/source/i18n/unicode",
+ "+third_party/skia",
+ "+ui/aura",
+ "+ui/base/text",
+ "+ui/gfx",
+ "+win8/util",
+]
diff --git a/chromium/printing/OWNERS b/chromium/printing/OWNERS
new file mode 100644
index 00000000000..f8be0e6feea
--- /dev/null
+++ b/chromium/printing/OWNERS
@@ -0,0 +1,8 @@
+abodenha@chromium.org
+dpapad@chromium.org
+gene@chromium.org
+kmadhusu@chromium.org
+scottbyer@chromium.org
+thestig@chromium.org
+vandebo@chromium.org
+vitalybuka@chromium.org
diff --git a/chromium/printing/backend/cups_helper.cc b/chromium/printing/backend/cups_helper.cc
new file mode 100644
index 00000000000..1c143a5dd51
--- /dev/null
+++ b/chromium/printing/backend/cups_helper.cc
@@ -0,0 +1,396 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/backend/cups_helper.h"
+
+#include <cups/ppd.h>
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "printing/backend/print_backend.h"
+#include "printing/backend/print_backend_consts.h"
+#include "url/gurl.h"
+
+// This section contains helper code for PPD parsing for semantic capabilities.
+namespace {
+
+const char kColorDevice[] = "ColorDevice";
+const char kColorModel[] = "ColorModel";
+const char kColorMode[] = "ColorMode";
+const char kProcessColorModel[] = "ProcessColorModel";
+const char kPrintoutMode[] = "PrintoutMode";
+const char kDraftGray[] = "Draft.Gray";
+const char kHighGray[] = "High.Gray";
+
+const char kDuplex[] = "Duplex";
+const char kDuplexNone[] = "None";
+
+#if !defined(OS_MACOSX)
+void ParseLpOptions(const base::FilePath& filepath,
+ const std::string& printer_name,
+ int* num_options, cups_option_t** options) {
+ std::string content;
+ if (!file_util::ReadFileToString(filepath, &content))
+ return;
+
+ const char kDest[] = "dest";
+ const char kDefault[] = "default";
+ const size_t kDestLen = sizeof(kDest) - 1;
+ const size_t kDefaultLen = sizeof(kDefault) - 1;
+ std::vector<std::string> lines;
+ base::SplitString(content, '\n', &lines);
+
+ for (size_t i = 0; i < lines.size(); ++i) {
+ std::string line = lines[i];
+ if (line.empty())
+ continue;
+
+ if (base::strncasecmp (line.c_str(), kDefault, kDefaultLen) == 0 &&
+ isspace(line[kDefaultLen])) {
+ line = line.substr(kDefaultLen);
+ } else if (base::strncasecmp (line.c_str(), kDest, kDestLen) == 0 &&
+ isspace(line[kDestLen])) {
+ line = line.substr(kDestLen);
+ } else {
+ continue;
+ }
+
+ TrimWhitespaceASCII(line, TRIM_ALL, &line);
+ if (line.empty())
+ continue;
+
+ size_t space_found = line.find(' ');
+ if (space_found == std::string::npos)
+ continue;
+
+ std::string name = line.substr(0, space_found);
+ if (name.empty())
+ continue;
+
+ if (base::strncasecmp(printer_name.c_str(), name.c_str(),
+ name.length()) != 0) {
+ continue; // This is not the required printer.
+ }
+
+ line = line.substr(space_found + 1);
+ TrimWhitespaceASCII(line, TRIM_ALL, &line); // Remove extra spaces.
+ if (line.empty())
+ continue;
+ // Parse the selected printer custom options.
+ *num_options = cupsParseOptions(line.c_str(), 0, options);
+ }
+}
+
+void MarkLpOptions(const std::string& printer_name, ppd_file_t** ppd) {
+ cups_option_t* options = NULL;
+ int num_options = 0;
+ ppdMarkDefaults(*ppd);
+
+ const char kSystemLpOptionPath[] = "/etc/cups/lpoptions";
+ const char kUserLpOptionPath[] = ".cups/lpoptions";
+
+ std::vector<base::FilePath> file_locations;
+ file_locations.push_back(base::FilePath(kSystemLpOptionPath));
+ file_locations.push_back(base::FilePath(
+ file_util::GetHomeDir().Append(kUserLpOptionPath)));
+
+ for (std::vector<base::FilePath>::const_iterator it = file_locations.begin();
+ it != file_locations.end(); ++it) {
+ num_options = 0;
+ options = NULL;
+ ParseLpOptions(*it, printer_name, &num_options, &options);
+ if (num_options > 0 && options) {
+ cupsMarkOptions(*ppd, num_options, options);
+ cupsFreeOptions(num_options, options);
+ }
+ }
+}
+#endif // !defined(OS_MACOSX)
+
+bool GetBasicColorModelSettings(ppd_file_t* ppd,
+ int* color_model_for_black,
+ int* color_model_for_color,
+ bool* color_is_default) {
+ ppd_option_t* color_model = ppdFindOption(ppd, kColorModel);
+ if (!color_model)
+ return false;
+
+ if (ppdFindChoice(color_model, printing::kBlack))
+ *color_model_for_black = printing::BLACK;
+ else if (ppdFindChoice(color_model, printing::kGray))
+ *color_model_for_black = printing::GRAY;
+ else if (ppdFindChoice(color_model, printing::kGrayscale))
+ *color_model_for_black = printing::GRAYSCALE;
+
+ if (ppdFindChoice(color_model, printing::kColor))
+ *color_model_for_color = printing::COLOR;
+ else if (ppdFindChoice(color_model, printing::kCMYK))
+ *color_model_for_color = printing::CMYK;
+ else if (ppdFindChoice(color_model, printing::kRGB))
+ *color_model_for_color = printing::RGB;
+ else if (ppdFindChoice(color_model, printing::kRGBA))
+ *color_model_for_color = printing::RGBA;
+ else if (ppdFindChoice(color_model, printing::kRGB16))
+ *color_model_for_color = printing::RGB16;
+ else if (ppdFindChoice(color_model, printing::kCMY))
+ *color_model_for_color = printing::CMY;
+ else if (ppdFindChoice(color_model, printing::kKCMY))
+ *color_model_for_color = printing::KCMY;
+ else if (ppdFindChoice(color_model, printing::kCMY_K))
+ *color_model_for_color = printing::CMY_K;
+
+ ppd_choice_t* marked_choice = ppdFindMarkedChoice(ppd, kColorModel);
+ if (!marked_choice)
+ marked_choice = ppdFindChoice(color_model, color_model->defchoice);
+
+ if (marked_choice) {
+ *color_is_default =
+ (base::strcasecmp(marked_choice->choice, printing::kBlack) != 0) &&
+ (base::strcasecmp(marked_choice->choice, printing::kGray) != 0) &&
+ (base::strcasecmp(marked_choice->choice, printing::kGrayscale) != 0);
+ }
+ return true;
+}
+
+bool GetPrintOutModeColorSettings(ppd_file_t* ppd,
+ int* color_model_for_black,
+ int* color_model_for_color,
+ bool* color_is_default) {
+ ppd_option_t* printout_mode = ppdFindOption(ppd, kPrintoutMode);
+ if (!printout_mode)
+ return false;
+
+ *color_model_for_color = printing::PRINTOUTMODE_NORMAL;
+ *color_model_for_black = printing::PRINTOUTMODE_NORMAL;
+
+ // Check to see if NORMAL_GRAY value is supported by PrintoutMode.
+ // If NORMAL_GRAY is not supported, NORMAL value is used to
+ // represent grayscale. If NORMAL_GRAY is supported, NORMAL is used to
+ // represent color.
+ if (ppdFindChoice(printout_mode, printing::kNormalGray))
+ *color_model_for_black = printing::PRINTOUTMODE_NORMAL_GRAY;
+
+ // Get the default marked choice to identify the default color setting
+ // value.
+ ppd_choice_t* printout_mode_choice = ppdFindMarkedChoice(ppd, kPrintoutMode);
+ if (!printout_mode_choice) {
+ printout_mode_choice = ppdFindChoice(printout_mode,
+ printout_mode->defchoice);
+ }
+ if (printout_mode_choice) {
+ if ((base::strcasecmp(printout_mode_choice->choice,
+ printing::kNormalGray) == 0) ||
+ (base::strcasecmp(printout_mode_choice->choice, kHighGray) == 0) ||
+ (base::strcasecmp(printout_mode_choice->choice, kDraftGray) == 0)) {
+ *color_model_for_black = printing::PRINTOUTMODE_NORMAL_GRAY;
+ *color_is_default = false;
+ }
+ }
+ return true;
+}
+
+bool GetColorModeSettings(ppd_file_t* ppd,
+ int* color_model_for_black,
+ int* color_model_for_color,
+ bool* color_is_default) {
+ // Samsung printers use "ColorMode" attribute in their ppds.
+ ppd_option_t* color_mode_option = ppdFindOption(ppd, kColorMode);
+ if (!color_mode_option)
+ return false;
+
+ if (ppdFindChoice(color_mode_option, printing::kColor))
+ *color_model_for_color = printing::COLORMODE_COLOR;
+
+ if (ppdFindChoice(color_mode_option, printing::kMonochrome))
+ *color_model_for_black = printing::COLORMODE_MONOCHROME;
+
+ ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kColorMode);
+ if (!mode_choice) {
+ mode_choice = ppdFindChoice(color_mode_option,
+ color_mode_option->defchoice);
+ }
+
+ if (mode_choice) {
+ *color_is_default =
+ (base::strcasecmp(mode_choice->choice, printing::kColor) == 0);
+ }
+ return true;
+}
+
+bool GetHPColorSettings(ppd_file_t* ppd,
+ int* color_model_for_black,
+ int* color_model_for_color,
+ bool* color_is_default) {
+ // HP printers use "Color/Color Model" attribute in their ppds.
+ ppd_option_t* color_mode_option = ppdFindOption(ppd, printing::kColor);
+ if (!color_mode_option)
+ return false;
+
+ if (ppdFindChoice(color_mode_option, printing::kColor))
+ *color_model_for_color = printing::HP_COLOR_COLOR;
+ if (ppdFindChoice(color_mode_option, printing::kBlack))
+ *color_model_for_black = printing::HP_COLOR_BLACK;
+
+ ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kColorMode);
+ if (!mode_choice) {
+ mode_choice = ppdFindChoice(color_mode_option,
+ color_mode_option->defchoice);
+ }
+ if (mode_choice) {
+ *color_is_default =
+ (base::strcasecmp(mode_choice->choice, printing::kColor) == 0);
+ }
+ return true;
+}
+
+bool GetProcessColorModelSettings(ppd_file_t* ppd,
+ int* color_model_for_black,
+ int* color_model_for_color,
+ bool* color_is_default) {
+ // Canon printers use "ProcessColorModel" attribute in their ppds.
+ ppd_option_t* color_mode_option = ppdFindOption(ppd, kProcessColorModel);
+ if (!color_mode_option)
+ return false;
+
+ if (ppdFindChoice(color_mode_option, printing::kRGB))
+ *color_model_for_color = printing::PROCESSCOLORMODEL_RGB;
+ else if (ppdFindChoice(color_mode_option, printing::kCMYK))
+ *color_model_for_color = printing::PROCESSCOLORMODEL_CMYK;
+
+ if (ppdFindChoice(color_mode_option, printing::kGreyscale))
+ *color_model_for_black = printing::PROCESSCOLORMODEL_GREYSCALE;
+
+ ppd_choice_t* mode_choice = ppdFindMarkedChoice(ppd, kProcessColorModel);
+ if (!mode_choice) {
+ mode_choice = ppdFindChoice(color_mode_option,
+ color_mode_option->defchoice);
+ }
+
+ if (mode_choice) {
+ *color_is_default =
+ (base::strcasecmp(mode_choice->choice, printing::kGreyscale) != 0);
+ }
+ return true;
+}
+
+bool GetColorModelSettings(ppd_file_t* ppd,
+ int* cm_black,
+ int* cm_color,
+ bool* is_color) {
+ bool is_color_device = false;
+ ppd_attr_t* attr = ppdFindAttr(ppd, kColorDevice, NULL);
+ if (attr && attr->value)
+ is_color_device = ppd->color_device;
+
+ *is_color = is_color_device;
+ return (is_color_device &&
+ GetBasicColorModelSettings(ppd, cm_black, cm_color, is_color)) ||
+ GetPrintOutModeColorSettings(ppd, cm_black, cm_color, is_color) ||
+ GetColorModeSettings(ppd, cm_black, cm_color, is_color) ||
+ GetHPColorSettings(ppd, cm_black, cm_color, is_color) ||
+ GetProcessColorModelSettings(ppd, cm_black, cm_color, is_color);
+}
+
+} // namespace
+
+namespace printing {
+
+// Default port for IPP print servers.
+static const int kDefaultIPPServerPort = 631;
+
+// Helper wrapper around http_t structure, with connection and cleanup
+// functionality.
+HttpConnectionCUPS::HttpConnectionCUPS(const GURL& print_server_url,
+ http_encryption_t encryption)
+ : http_(NULL) {
+ // If we have an empty url, use default print server.
+ if (print_server_url.is_empty())
+ return;
+
+ int port = print_server_url.IntPort();
+ if (port == url_parse::PORT_UNSPECIFIED)
+ port = kDefaultIPPServerPort;
+
+ http_ = httpConnectEncrypt(print_server_url.host().c_str(), port,
+ encryption);
+ if (http_ == NULL) {
+ LOG(ERROR) << "CP_CUPS: Failed connecting to print server: " <<
+ print_server_url;
+ }
+}
+
+HttpConnectionCUPS::~HttpConnectionCUPS() {
+ if (http_ != NULL)
+ httpClose(http_);
+}
+
+void HttpConnectionCUPS::SetBlocking(bool blocking) {
+ httpBlocking(http_, blocking ? 1 : 0);
+}
+
+http_t* HttpConnectionCUPS::http() {
+ return http_;
+}
+
+bool parsePpdCapabilities(
+ const std::string& printer_name,
+ const std::string& printer_capabilities,
+ PrinterSemanticCapsAndDefaults* printer_info) {
+ base::FilePath ppd_file_path;
+ if (!file_util::CreateTemporaryFile(&ppd_file_path))
+ return false;
+
+ int data_size = printer_capabilities.length();
+ if (data_size != file_util::WriteFile(
+ ppd_file_path,
+ printer_capabilities.data(),
+ data_size)) {
+ base::DeleteFile(ppd_file_path, false);
+ return false;
+ }
+
+ ppd_file_t* ppd = ppdOpenFile(ppd_file_path.value().c_str());
+ if (!ppd)
+ return false;
+
+ printing::PrinterSemanticCapsAndDefaults caps;
+#if !defined(OS_MACOSX)
+ MarkLpOptions(printer_name, &ppd);
+#endif
+ ppd_choice_t* duplex_choice = ppdFindMarkedChoice(ppd, kDuplex);
+ if (!duplex_choice) {
+ ppd_option_t* option = ppdFindOption(ppd, kDuplex);
+ if (option)
+ duplex_choice = ppdFindChoice(option, option->defchoice);
+ }
+
+ if (duplex_choice) {
+ caps.duplex_capable = true;
+ if (base::strcasecmp(duplex_choice->choice, kDuplexNone) != 0)
+ caps.duplex_default = printing::LONG_EDGE;
+ else
+ caps.duplex_default = printing::SIMPLEX;
+ }
+
+ bool is_color = false;
+ int cm_color = 0, cm_black = 0;
+ if (!GetColorModelSettings(ppd, &cm_black, &cm_color, &is_color)) {
+ VLOG(1) << "Unknown printer color model";
+ }
+
+ caps.color_changeable = (cm_color && cm_black && (cm_color != cm_black));
+ caps.color_default = is_color;
+
+ ppdClose(ppd);
+ base::DeleteFile(ppd_file_path, false);
+
+ *printer_info = caps;
+ return true;
+}
+
+} // namespace printing
diff --git a/chromium/printing/backend/cups_helper.h b/chromium/printing/backend/cups_helper.h
new file mode 100644
index 00000000000..04ff08ad6cb
--- /dev/null
+++ b/chromium/printing/backend/cups_helper.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_BACKEND_CUPS_HELPER_H_
+#define PRINTING_BACKEND_CUPS_HELPER_H_
+
+#include <cups/cups.h>
+
+#include <string>
+
+#include "printing/printing_export.h"
+
+class GURL;
+
+// These are helper functions for dealing with CUPS.
+namespace printing {
+
+struct PrinterSemanticCapsAndDefaults;
+
+// Helper wrapper around http_t structure, with connection and cleanup
+// functionality.
+class PRINTING_EXPORT HttpConnectionCUPS {
+ public:
+ HttpConnectionCUPS(const GURL& print_server_url,
+ http_encryption_t encryption);
+ ~HttpConnectionCUPS();
+
+ void SetBlocking(bool blocking);
+
+ http_t* http();
+
+ private:
+ http_t* http_;
+};
+
+// Helper function to parse and convert PPD capabilitites to
+// semantic options.
+PRINTING_EXPORT bool parsePpdCapabilities(
+ const std::string& printer_name,
+ const std::string& printer_capabilities,
+ PrinterSemanticCapsAndDefaults* printer_info);
+
+} // namespace printing
+
+#endif // PRINTING_BACKEND_CUPS_HELPER_H_
diff --git a/chromium/printing/backend/cups_helper_unittest.cc b/chromium/printing/backend/cups_helper_unittest.cc
new file mode 100644
index 00000000000..7a111e3616c
--- /dev/null
+++ b/chromium/printing/backend/cups_helper_unittest.cc
@@ -0,0 +1,159 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/backend/cups_helper.h"
+#include "printing/backend/print_backend.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(PrintBackendCupsHelperTest, TestPpdParsingNoColorDuplexLongEdge) {
+ std::string test_ppd_data;
+ test_ppd_data.append(
+ "*PPD-Adobe: \"4.3\"\n\n"
+ "*OpenGroup: General/General\n\n"
+ "*OpenUI *ColorModel/Color Model: PickOne\n"
+ "*DefaultColorModel: Gray\n"
+ "*ColorModel Gray/Grayscale: \""
+ "<</cupsColorSpace 0/cupsColorOrder 0>>"
+ "setpagedevice\"\n"
+ "*ColorModel Black/Inverted Grayscale: \""
+ "<</cupsColorSpace 3/cupsColorOrder 0>>"
+ "setpagedevice\"\n"
+ "*CloseUI: *ColorModel\n"
+ "*OpenUI *Duplex/2-Sided Printing: PickOne\n"
+ "*DefaultDuplex: DuplexTumble\n"
+ "*Duplex None/Off: \"<</Duplex false>>"
+ "setpagedevice\"\n"
+ "*Duplex DuplexNoTumble/LongEdge: \""
+ "<</Duplex true/Tumble false>>setpagedevice\"\n"
+ "*Duplex DuplexTumble/ShortEdge: \""
+ "<</Duplex true/Tumble true>>setpagedevice\"\n"
+ "*CloseUI: *Duplex\n\n"
+ "*CloseGroup: General\n");
+
+ printing::PrinterSemanticCapsAndDefaults caps;
+ EXPECT_TRUE(printing::parsePpdCapabilities("test", test_ppd_data, &caps));
+ EXPECT_FALSE(caps.color_changeable);
+ EXPECT_FALSE(caps.color_default);
+ EXPECT_TRUE(caps.duplex_capable);
+ EXPECT_EQ(caps.duplex_default, printing::LONG_EDGE);
+}
+
+// Test duplex detection code, which regressed in http://crbug.com/103999.
+TEST(PrintBackendCupsHelperTest, TestPpdParsingNoColorDuplexSimples) {
+ std::string test_ppd_data;
+ test_ppd_data.append(
+ "*PPD-Adobe: \"4.3\"\n\n"
+ "*OpenGroup: General/General\n\n"
+ "*OpenUI *Duplex/Double-Sided Printing: PickOne\n"
+ "*DefaultDuplex: None\n"
+ "*Duplex None/Off: "
+ "\"<</Duplex false>>setpagedevice\"\n"
+ "*Duplex DuplexNoTumble/Long Edge (Standard): "
+ "\"<</Duplex true/Tumble false>>setpagedevice\"\n"
+ "*Duplex DuplexTumble/Short Edge (Flip): "
+ "\"<</Duplex true/Tumble true>>setpagedevice\"\n"
+ "*CloseUI: *Duplex\n\n"
+ "*CloseGroup: General\n");
+
+ printing::PrinterSemanticCapsAndDefaults caps;
+ EXPECT_TRUE(printing::parsePpdCapabilities("test", test_ppd_data, &caps));
+ EXPECT_FALSE(caps.color_changeable);
+ EXPECT_FALSE(caps.color_default);
+ EXPECT_TRUE(caps.duplex_capable);
+ EXPECT_EQ(caps.duplex_default, printing::SIMPLEX);
+}
+
+TEST(PrintBackendCupsHelperTest, TestPpdParsingNoColorNoDuplex) {
+ std::string test_ppd_data;
+ test_ppd_data.append(
+ "*PPD-Adobe: \"4.3\"\n\n"
+ "*OpenGroup: General/General\n\n"
+ "*OpenUI *ColorModel/Color Model: PickOne\n"
+ "*DefaultColorModel: Gray\n"
+ "*ColorModel Gray/Grayscale: \""
+ "<</cupsColorSpace 0/cupsColorOrder 0>>"
+ "setpagedevice\"\n"
+ "*ColorModel Black/Inverted Grayscale: \""
+ "<</cupsColorSpace 3/cupsColorOrder 0>>"
+ "setpagedevice\"\n"
+ "*CloseUI: *ColorModel\n"
+ "*CloseGroup: General\n");
+
+ printing::PrinterSemanticCapsAndDefaults caps;
+ EXPECT_TRUE(printing::parsePpdCapabilities("test", test_ppd_data, &caps));
+ EXPECT_FALSE(caps.color_changeable);
+ EXPECT_FALSE(caps.color_default);
+ EXPECT_FALSE(caps.duplex_capable);
+ EXPECT_EQ(caps.duplex_default, printing::UNKNOWN_DUPLEX_MODE);
+}
+
+TEST(PrintBackendCupsHelperTest, TestPpdParsingColorTrueDuplexLongEdge) {
+ std::string test_ppd_data;
+ test_ppd_data.append(
+ "*PPD-Adobe: \"4.3\"\n\n"
+ "*ColorDevice: True\n"
+ "*DefaultColorSpace: CMYK\n\n"
+ "*OpenGroup: General/General\n\n"
+ "*OpenUI *ColorModel/Color Model: PickOne\n"
+ "*DefaultColorModel: CMYK\n"
+ "*ColorModel CMYK/Color: "
+ "\"(cmyk) RCsetdevicecolor\"\n"
+ "*ColorModel Gray/Black and White: "
+ "\"(gray) RCsetdevicecolor\"\n"
+ "*CloseUI: *ColorModel\n"
+ "*OpenUI *Duplex/2-Sided Printing: PickOne\n"
+ "*DefaultDuplex: DuplexTumble\n"
+ "*Duplex None/Off: \"<</Duplex false>>"
+ "setpagedevice\"\n"
+ "*Duplex DuplexNoTumble/LongEdge: \""
+ "<</Duplex true/Tumble false>>setpagedevice\"\n"
+ "*Duplex DuplexTumble/ShortEdge: \""
+ "<</Duplex true/Tumble true>>setpagedevice\"\n"
+ "*CloseUI: *Duplex\n\n"
+ "*CloseGroup: General\n");
+
+ printing::PrinterSemanticCapsAndDefaults caps;
+ EXPECT_TRUE(printing::parsePpdCapabilities("test", test_ppd_data, &caps));
+ EXPECT_TRUE(caps.color_changeable);
+ EXPECT_TRUE(caps.color_default);
+ EXPECT_TRUE(caps.duplex_capable);
+ EXPECT_EQ(caps.duplex_default, printing::LONG_EDGE);
+}
+
+TEST(PrintBackendCupsHelperTest, TestPpdParsingColorFalseDuplexLongEdge) {
+ std::string test_ppd_data;
+ test_ppd_data.append(
+ "*PPD-Adobe: \"4.3\"\n\n"
+ "*ColorDevice: True\n"
+ "*DefaultColorSpace: CMYK\n\n"
+ "*OpenGroup: General/General\n\n"
+ "*OpenUI *ColorModel/Color Model: PickOne\n"
+ "*DefaultColorModel: Grayscale\n"
+ "*ColorModel Color/Color: "
+ "\"%% FoomaticRIPOptionSetting: ColorModel=Color\"\n"
+ "*FoomaticRIPOptionSetting ColorModel=Color: "
+ "\"JCLDatamode=Color GSCmdLine=Color\"\n"
+ "*ColorModel Grayscale/Grayscale: "
+ "\"%% FoomaticRIPOptionSetting: ColorModel=Grayscale\"\n"
+ "*FoomaticRIPOptionSetting ColorModel=Grayscale: "
+ "\"JCLDatamode=Grayscale GSCmdLine=Grayscale\"\n"
+ "*CloseUI: *ColorModel\n"
+ "*OpenUI *Duplex/2-Sided Printing: PickOne\n"
+ "*DefaultDuplex: DuplexTumble\n"
+ "*Duplex None/Off: \"<</Duplex false>>"
+ "setpagedevice\"\n"
+ "*Duplex DuplexNoTumble/LongEdge: \""
+ "<</Duplex true/Tumble false>>setpagedevice\"\n"
+ "*Duplex DuplexTumble/ShortEdge: \""
+ "<</Duplex true/Tumble true>>setpagedevice\"\n"
+ "*CloseUI: *Duplex\n\n"
+ "*CloseGroup: General\n");
+
+ printing::PrinterSemanticCapsAndDefaults caps;
+ EXPECT_TRUE(printing::parsePpdCapabilities("test", test_ppd_data, &caps));
+ EXPECT_TRUE(caps.color_changeable);
+ EXPECT_FALSE(caps.color_default);
+ EXPECT_TRUE(caps.duplex_capable);
+ EXPECT_EQ(caps.duplex_default, printing::LONG_EDGE);
+}
diff --git a/chromium/printing/backend/print_backend.cc b/chromium/printing/backend/print_backend.cc
new file mode 100644
index 00000000000..958343f4ac8
--- /dev/null
+++ b/chromium/printing/backend/print_backend.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/backend/print_backend.h"
+
+#include <algorithm>
+
+#include "third_party/icu/source/common/unicode/uchar.h"
+#include "ui/base/text/text_elider.h"
+
+namespace {
+
+const wchar_t kDefaultDocumentTitle[] = L"Untitled Document";
+const int kMaxDocumentTitleLength = 50;
+
+} // namespace
+
+namespace printing {
+
+PrinterBasicInfo::PrinterBasicInfo()
+ : printer_status(0),
+ is_default(false) {}
+
+PrinterBasicInfo::~PrinterBasicInfo() {}
+
+PrinterSemanticCapsAndDefaults::PrinterSemanticCapsAndDefaults()
+ : color_changeable(false),
+ duplex_capable(false),
+ color_default(false),
+ duplex_default(UNKNOWN_DUPLEX_MODE) {}
+
+PrinterSemanticCapsAndDefaults::~PrinterSemanticCapsAndDefaults() {}
+
+PrinterCapsAndDefaults::PrinterCapsAndDefaults() {}
+
+PrinterCapsAndDefaults::~PrinterCapsAndDefaults() {}
+
+PrintBackend::~PrintBackend() {}
+
+string16 PrintBackend::SimplifyDocumentTitle(const string16& title) {
+ string16 no_controls(title);
+ no_controls.erase(
+ std::remove_if(no_controls.begin(), no_controls.end(), &u_iscntrl),
+ no_controls.end());
+ string16 result;
+ ui::ElideString(no_controls, kMaxDocumentTitleLength, &result);
+ return result;
+}
+
+} // namespace printing
diff --git a/chromium/printing/backend/print_backend.h b/chromium/printing/backend/print_backend.h
new file mode 100644
index 00000000000..6a50e74aeb3
--- /dev/null
+++ b/chromium/printing/backend/print_backend.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_BACKEND_PRINT_BACKEND_H_
+#define PRINTING_BACKEND_PRINT_BACKEND_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/strings/string16.h"
+#include "printing/print_job_constants.h"
+#include "printing/printing_export.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+// This is the interface for platform-specific code for a print backend
+namespace printing {
+
+struct PRINTING_EXPORT PrinterBasicInfo {
+ PrinterBasicInfo();
+ ~PrinterBasicInfo();
+
+ std::string printer_name;
+ std::string printer_description;
+ int printer_status;
+ int is_default;
+ std::map<std::string, std::string> options;
+};
+
+typedef std::vector<PrinterBasicInfo> PrinterList;
+
+struct PRINTING_EXPORT PrinterSemanticCapsAndDefaults {
+ PrinterSemanticCapsAndDefaults();
+ ~PrinterSemanticCapsAndDefaults();
+
+ // Capabilities.
+ bool color_changeable;
+ bool duplex_capable;
+
+ // Current defaults.
+ bool color_default;
+ DuplexMode duplex_default;
+};
+
+struct PRINTING_EXPORT PrinterCapsAndDefaults {
+ PrinterCapsAndDefaults();
+ ~PrinterCapsAndDefaults();
+
+ std::string printer_capabilities;
+ std::string caps_mime_type;
+ std::string printer_defaults;
+ std::string defaults_mime_type;
+};
+
+// PrintBackend class will provide interface for different print backends
+// (Windows, CUPS) to implement. User will call CreateInstance() to
+// obtain available print backend.
+// Please note, that PrintBackend is not platform specific, but rather
+// print system specific. For example, CUPS is available on both Linux and Mac,
+// but not available on ChromeOS, etc. This design allows us to add more
+// functionality on some platforms, while reusing core (CUPS) functions.
+class PRINTING_EXPORT PrintBackend
+ : public base::RefCountedThreadSafe<PrintBackend> {
+ public:
+ // Enumerates the list of installed local and network printers.
+ virtual bool EnumeratePrinters(PrinterList* printer_list) = 0;
+
+ // Get the default printer name. Empty string if no default printer.
+ virtual std::string GetDefaultPrinterName() = 0;
+
+ // Gets the semantic capabilities and defaults for a specific printer.
+ // This is usually a lighter implementation than GetPrinterCapsAndDefaults().
+ // NOTE: on some old platforms (WinXP without XPS pack)
+ // GetPrinterCapsAndDefaults() will fail, while this function will succeed.
+ virtual bool GetPrinterSemanticCapsAndDefaults(
+ const std::string& printer_name,
+ PrinterSemanticCapsAndDefaults* printer_info) = 0;
+
+ // Gets the capabilities and defaults for a specific printer.
+ virtual bool GetPrinterCapsAndDefaults(
+ const std::string& printer_name,
+ PrinterCapsAndDefaults* printer_info) = 0;
+
+ // Gets the information about driver for a specific printer.
+ virtual std::string GetPrinterDriverInfo(
+ const std::string& printer_name) = 0;
+
+ // Returns true if printer_name points to a valid printer.
+ virtual bool IsValidPrinter(const std::string& printer_name) = 0;
+
+ // Simplify title to resolve issue with some drivers.
+ static string16 SimplifyDocumentTitle(const string16& title);
+
+ // Allocate a print backend. If |print_backend_settings| is NULL, default
+ // settings will be used.
+ // Return NULL if no print backend available.
+ static scoped_refptr<PrintBackend> CreateInstance(
+ const base::DictionaryValue* print_backend_settings);
+
+ protected:
+ friend class base::RefCountedThreadSafe<PrintBackend>;
+ virtual ~PrintBackend();
+};
+
+} // namespace printing
+
+#endif // PRINTING_BACKEND_PRINT_BACKEND_H_
diff --git a/chromium/printing/backend/print_backend_chromeos.cc b/chromium/printing/backend/print_backend_chromeos.cc
new file mode 100644
index 00000000000..c206d51b0f1
--- /dev/null
+++ b/chromium/printing/backend/print_backend_chromeos.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/backend/print_backend.h"
+
+#include "base/logging.h"
+
+namespace printing {
+
+// Provides a stubbed out PrintBackend implementation for use on ChromeOS.
+class PrintBackendChromeOS : public PrintBackend {
+ public:
+ PrintBackendChromeOS();
+
+ // PrintBackend implementation.
+ virtual bool EnumeratePrinters(PrinterList* printer_list) OVERRIDE;
+ virtual std::string GetDefaultPrinterName() OVERRIDE;
+ virtual bool GetPrinterSemanticCapsAndDefaults(
+ const std::string& printer_name,
+ PrinterSemanticCapsAndDefaults* printer_info) OVERRIDE;
+ virtual bool GetPrinterCapsAndDefaults(
+ const std::string& printer_name,
+ PrinterCapsAndDefaults* printer_info) OVERRIDE;
+ virtual std::string GetPrinterDriverInfo(
+ const std::string& printer_name) OVERRIDE;
+ virtual bool IsValidPrinter(const std::string& printer_name) OVERRIDE;
+
+ protected:
+ virtual ~PrintBackendChromeOS() {}
+};
+
+PrintBackendChromeOS::PrintBackendChromeOS() {}
+
+bool PrintBackendChromeOS::EnumeratePrinters(PrinterList* printer_list) {
+ return true;
+}
+
+bool PrintBackendChromeOS::GetPrinterSemanticCapsAndDefaults(
+ const std::string& printer_name,
+ PrinterSemanticCapsAndDefaults* printer_info) {
+ NOTREACHED();
+ return false;
+}
+
+bool PrintBackendChromeOS::GetPrinterCapsAndDefaults(
+ const std::string& printer_name,
+ PrinterCapsAndDefaults* printer_info) {
+ NOTREACHED();
+ return false;
+}
+
+std::string PrintBackendChromeOS::GetPrinterDriverInfo(
+ const std::string& printer_name) {
+ NOTREACHED();
+ return std::string();
+}
+
+std::string PrintBackendChromeOS::GetDefaultPrinterName() {
+ return std::string();
+}
+
+bool PrintBackendChromeOS::IsValidPrinter(const std::string& printer_name) {
+ NOTREACHED();
+ return true;
+}
+
+scoped_refptr<PrintBackend> PrintBackend::CreateInstance(
+ const base::DictionaryValue* print_backend_settings) {
+ return new PrintBackendChromeOS();
+}
+
+} // namespace printing
+
diff --git a/chromium/printing/backend/print_backend_consts.cc b/chromium/printing/backend/print_backend_consts.cc
new file mode 100644
index 00000000000..8b2f6db07ae
--- /dev/null
+++ b/chromium/printing/backend/print_backend_consts.cc
@@ -0,0 +1,16 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Constant defines used in the print backend code
+
+#include "printing/backend/print_backend_consts.h"
+
+const char kLocationTagName[] = "location";
+const char kDriverNameTagName[] = "drivername";
+const char kDriverInfoTagName[] = "system_driverinfo";
+const char kCUPSPrintServerURL[] = "print_server_url";
+const char kCUPSBlocking[] = "cups_blocking";
+const char kCUPSEncryption[] = "cups_encryption";
+const char kValueTrue[] = "true";
+const char kValueFalse[] = "false";
diff --git a/chromium/printing/backend/print_backend_consts.h b/chromium/printing/backend/print_backend_consts.h
new file mode 100644
index 00000000000..0cab35e5b20
--- /dev/null
+++ b/chromium/printing/backend/print_backend_consts.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_BACKEND_PRINT_BACKEND_CONSTS_H_
+#define PRINTING_BACKEND_PRINT_BACKEND_CONSTS_H_
+
+#include "printing/printing_export.h"
+
+PRINTING_EXPORT extern const char kLocationTagName[];
+PRINTING_EXPORT extern const char kDriverNameTagName[];
+PRINTING_EXPORT extern const char kDriverInfoTagName[];
+PRINTING_EXPORT extern const char kCUPSPrintServerURL[];
+PRINTING_EXPORT extern const char kCUPSBlocking[];
+PRINTING_EXPORT extern const char kCUPSEncryption[];
+PRINTING_EXPORT extern const char kValueTrue[];
+PRINTING_EXPORT extern const char kValueFalse[];
+
+#endif // PRINTING_BACKEND_PRINT_BACKEND_CONSTS_H_
diff --git a/chromium/printing/backend/print_backend_cups.cc b/chromium/printing/backend/print_backend_cups.cc
new file mode 100644
index 00000000000..44c38bb96c3
--- /dev/null
+++ b/chromium/printing/backend/print_backend_cups.cc
@@ -0,0 +1,386 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/backend/print_backend.h"
+
+#include "build/build_config.h"
+
+#include <dlfcn.h>
+#include <errno.h>
+#include <pthread.h>
+
+#if defined(OS_MACOSX)
+#include <AvailabilityMacros.h>
+#else
+#include <gcrypt.h>
+#endif
+
+#include "base/debug/leak_annotations.h"
+#include "base/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/synchronization/lock.h"
+#include "base/values.h"
+#include "printing/backend/cups_helper.h"
+#include "printing/backend/print_backend_consts.h"
+#include "url/gurl.h"
+
+#if (CUPS_VERSION_MAJOR == 1 && CUPS_VERSION_MINOR < 4)
+const int CUPS_PRINTER_SCANNER = 0x2000000; // Scanner-only device
+#endif
+
+#if !defined(OS_MACOSX)
+GCRY_THREAD_OPTION_PTHREAD_IMPL;
+
+namespace {
+
+// Init GCrypt library (needed for CUPS) using pthreads.
+// There exists a bug in CUPS library, where it crashed with: "ath.c:184:
+// _gcry_ath_mutex_lock: Assertion `*lock == ((ath_mutex_t) 0)' failed."
+// It happened when multiple threads tried printing simultaneously.
+// Google search for 'gnutls thread safety' provided a solution that
+// initialized gcrypt and gnutls.
+
+// TODO(phajdan.jr): Remove this after https://bugs.g10code.com/gnupg/issue1197
+// gets fixed on all Linux distros we support (i.e. when they ship libgcrypt
+// with the fix).
+
+// Initially, we linked with -lgnutls and simply called gnutls_global_init(),
+// but this did not work well since we build one binary on Ubuntu Hardy and
+// expect it to run on many Linux distros. (See http://crbug.com/46954)
+// So instead we use dlopen() and dlsym() to dynamically load and call
+// gnutls_global_init().
+
+class GcryptInitializer {
+ public:
+ GcryptInitializer() {
+ Init();
+ }
+
+ private:
+ void Init() {
+ const char* kGnuTlsFiles[] = {
+ "libgnutls.so.28",
+ "libgnutls.so.26",
+ "libgnutls.so",
+ };
+ gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
+ for (size_t i = 0; i < arraysize(kGnuTlsFiles); ++i) {
+ void* gnutls_lib = dlopen(kGnuTlsFiles[i], RTLD_NOW);
+ if (!gnutls_lib) {
+ VLOG(1) << "Cannot load " << kGnuTlsFiles[i];
+ continue;
+ }
+ const char* kGnuTlsInitFuncName = "gnutls_global_init";
+ int (*pgnutls_global_init)(void) = reinterpret_cast<int(*)()>(
+ dlsym(gnutls_lib, kGnuTlsInitFuncName));
+ if (!pgnutls_global_init) {
+ VLOG(1) << "Could not find " << kGnuTlsInitFuncName
+ << " in " << kGnuTlsFiles[i];
+ continue;
+ }
+ {
+ // GnuTLS has a genuine small memory leak that is easier to annotate
+ // than suppress. See http://crbug.com/176888#c7
+ // TODO(earthdok): remove this once the leak is fixed.
+ ANNOTATE_SCOPED_MEMORY_LEAK;
+ if ((*pgnutls_global_init)() != 0)
+ LOG(ERROR) << "gnutls_global_init() failed";
+ }
+ return;
+ }
+ LOG(ERROR) << "Cannot find libgnutls";
+ }
+};
+
+base::LazyInstance<GcryptInitializer> g_gcrypt_initializer =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+#endif // !defined(OS_MACOSX)
+
+namespace printing {
+
+static const char kCUPSPrinterInfoOpt[] = "printer-info";
+static const char kCUPSPrinterStateOpt[] = "printer-state";
+static const char kCUPSPrinterTypeOpt[] = "printer-type";
+static const char kCUPSPrinterMakeModelOpt[] = "printer-make-and-model";
+
+class PrintBackendCUPS : public PrintBackend {
+ public:
+ PrintBackendCUPS(const GURL& print_server_url,
+ http_encryption_t encryption, bool blocking);
+
+ // PrintBackend implementation.
+ virtual bool EnumeratePrinters(PrinterList* printer_list) OVERRIDE;
+ virtual std::string GetDefaultPrinterName() OVERRIDE;
+ virtual bool GetPrinterSemanticCapsAndDefaults(
+ const std::string& printer_name,
+ PrinterSemanticCapsAndDefaults* printer_info) OVERRIDE;
+ virtual bool GetPrinterCapsAndDefaults(
+ const std::string& printer_name,
+ PrinterCapsAndDefaults* printer_info) OVERRIDE;
+ virtual std::string GetPrinterDriverInfo(
+ const std::string& printer_name) OVERRIDE;
+ virtual bool IsValidPrinter(const std::string& printer_name) OVERRIDE;
+
+ protected:
+ virtual ~PrintBackendCUPS() {}
+
+ private:
+ // Following functions are wrappers around corresponding CUPS functions.
+ // <functions>2() are called when print server is specified, and plain
+ // version in another case. There is an issue specifing CUPS_HTTP_DEFAULT
+ // in the <functions>2(), it does not work in CUPS prior to 1.4.
+ int GetDests(cups_dest_t** dests);
+ base::FilePath GetPPD(const char* name);
+
+ GURL print_server_url_;
+ http_encryption_t cups_encryption_;
+ bool blocking_;
+};
+
+PrintBackendCUPS::PrintBackendCUPS(const GURL& print_server_url,
+ http_encryption_t encryption,
+ bool blocking)
+ : print_server_url_(print_server_url),
+ cups_encryption_(encryption),
+ blocking_(blocking) {
+}
+
+bool PrintBackendCUPS::EnumeratePrinters(PrinterList* printer_list) {
+ DCHECK(printer_list);
+ printer_list->clear();
+
+ cups_dest_t* destinations = NULL;
+ int num_dests = GetDests(&destinations);
+ if ((num_dests == 0) && (cupsLastError() > IPP_OK_EVENTS_COMPLETE)) {
+ VLOG(1) << "CUPS: Error getting printers from CUPS server"
+ << ", server: " << print_server_url_
+ << ", error: " << static_cast<int>(cupsLastError());
+ return false;
+ }
+
+ for (int printer_index = 0; printer_index < num_dests; printer_index++) {
+ const cups_dest_t& printer = destinations[printer_index];
+
+ // CUPS can have 'printers' that are actually scanners. (not MFC)
+ // At least on Mac. Check for scanners and skip them.
+ const char* type_str = cupsGetOption(kCUPSPrinterTypeOpt,
+ printer.num_options, printer.options);
+ if (type_str != NULL) {
+ int type;
+ if (base::StringToInt(type_str, &type) && (type & CUPS_PRINTER_SCANNER))
+ continue;
+ }
+
+ PrinterBasicInfo printer_info;
+ printer_info.printer_name = printer.name;
+ printer_info.is_default = printer.is_default;
+
+ const char* info = cupsGetOption(kCUPSPrinterInfoOpt,
+ printer.num_options, printer.options);
+ if (info != NULL)
+ printer_info.printer_description = info;
+
+ const char* state = cupsGetOption(kCUPSPrinterStateOpt,
+ printer.num_options, printer.options);
+ if (state != NULL)
+ base::StringToInt(state, &printer_info.printer_status);
+
+ const char* drv_info = cupsGetOption(kCUPSPrinterMakeModelOpt,
+ printer.num_options,
+ printer.options);
+ if (drv_info)
+ printer_info.options[kDriverInfoTagName] = *drv_info;
+
+ // Store printer options.
+ for (int opt_index = 0; opt_index < printer.num_options; opt_index++) {
+ printer_info.options[printer.options[opt_index].name] =
+ printer.options[opt_index].value;
+ }
+
+ printer_list->push_back(printer_info);
+ }
+
+ cupsFreeDests(num_dests, destinations);
+
+ VLOG(1) << "CUPS: Enumerated printers"
+ << ", server: " << print_server_url_
+ << ", # of printers: " << printer_list->size();
+ return true;
+}
+
+std::string PrintBackendCUPS::GetDefaultPrinterName() {
+ // Not using cupsGetDefault() because it lies about the default printer.
+ cups_dest_t* dests;
+ int num_dests = GetDests(&dests);
+ cups_dest_t* dest = cupsGetDest(NULL, NULL, num_dests, dests);
+ std::string name = dest ? std::string(dest->name) : std::string();
+ cupsFreeDests(num_dests, dests);
+ return name;
+}
+
+bool PrintBackendCUPS::GetPrinterSemanticCapsAndDefaults(
+ const std::string& printer_name,
+ PrinterSemanticCapsAndDefaults* printer_info) {
+ PrinterCapsAndDefaults info;
+ if (!GetPrinterCapsAndDefaults(printer_name, &info) )
+ return false;
+
+ return parsePpdCapabilities(
+ printer_name, info.printer_capabilities, printer_info);
+}
+
+bool PrintBackendCUPS::GetPrinterCapsAndDefaults(
+ const std::string& printer_name,
+ PrinterCapsAndDefaults* printer_info) {
+ DCHECK(printer_info);
+
+ VLOG(1) << "CUPS: Getting caps and defaults"
+ << ", printer name: " << printer_name;
+
+ base::FilePath ppd_path(GetPPD(printer_name.c_str()));
+ // In some cases CUPS failed to get ppd file.
+ if (ppd_path.empty()) {
+ LOG(ERROR) << "CUPS: Failed to get PPD"
+ << ", printer name: " << printer_name;
+ return false;
+ }
+
+ std::string content;
+ bool res = file_util::ReadFileToString(ppd_path, &content);
+
+ base::DeleteFile(ppd_path, false);
+
+ if (res) {
+ printer_info->printer_capabilities.swap(content);
+ printer_info->caps_mime_type = "application/pagemaker";
+ // In CUPS, printer defaults is a part of PPD file. Nothing to upload here.
+ printer_info->printer_defaults.clear();
+ printer_info->defaults_mime_type.clear();
+ }
+
+ return res;
+}
+
+std::string PrintBackendCUPS::GetPrinterDriverInfo(
+ const std::string& printer_name) {
+ cups_dest_t* destinations = NULL;
+ int num_dests = GetDests(&destinations);
+ std::string result;
+ for (int printer_index = 0; printer_index < num_dests; printer_index++) {
+ const cups_dest_t& printer = destinations[printer_index];
+ if (printer_name == printer.name) {
+ const char* info = cupsGetOption(kCUPSPrinterMakeModelOpt,
+ printer.num_options,
+ printer.options);
+ if (info)
+ result = *info;
+ }
+ }
+
+ cupsFreeDests(num_dests, destinations);
+ return result;
+}
+
+bool PrintBackendCUPS::IsValidPrinter(const std::string& printer_name) {
+ // This is not very efficient way to get specific printer info. CUPS 1.4
+ // supports cupsGetNamedDest() function. However, CUPS 1.4 is not available
+ // everywhere (for example, it supported from Mac OS 10.6 only).
+ PrinterList printer_list;
+ EnumeratePrinters(&printer_list);
+
+ PrinterList::iterator it;
+ for (it = printer_list.begin(); it != printer_list.end(); ++it)
+ if (it->printer_name == printer_name)
+ return true;
+ return false;
+}
+
+scoped_refptr<PrintBackend> PrintBackend::CreateInstance(
+ const DictionaryValue* print_backend_settings) {
+#if !defined(OS_MACOSX)
+ // Initialize gcrypt library.
+ g_gcrypt_initializer.Get();
+#endif
+
+ std::string print_server_url_str, cups_blocking;
+ int encryption = HTTP_ENCRYPT_NEVER;
+ if (print_backend_settings) {
+ print_backend_settings->GetString(kCUPSPrintServerURL,
+ &print_server_url_str);
+
+ print_backend_settings->GetString(kCUPSBlocking,
+ &cups_blocking);
+
+ print_backend_settings->GetInteger(kCUPSEncryption, &encryption);
+ }
+ GURL print_server_url(print_server_url_str.c_str());
+ return new PrintBackendCUPS(print_server_url,
+ static_cast<http_encryption_t>(encryption),
+ cups_blocking == kValueTrue);
+}
+
+int PrintBackendCUPS::GetDests(cups_dest_t** dests) {
+ if (print_server_url_.is_empty()) { // Use default (local) print server.
+ return cupsGetDests(dests);
+ } else {
+ HttpConnectionCUPS http(print_server_url_, cups_encryption_);
+ http.SetBlocking(blocking_);
+ return cupsGetDests2(http.http(), dests);
+ }
+}
+
+base::FilePath PrintBackendCUPS::GetPPD(const char* name) {
+ // cupsGetPPD returns a filename stored in a static buffer in CUPS.
+ // Protect this code with lock.
+ CR_DEFINE_STATIC_LOCAL(base::Lock, ppd_lock, ());
+ base::AutoLock ppd_autolock(ppd_lock);
+ base::FilePath ppd_path;
+ const char* ppd_file_path = NULL;
+ if (print_server_url_.is_empty()) { // Use default (local) print server.
+ ppd_file_path = cupsGetPPD(name);
+ if (ppd_file_path)
+ ppd_path = base::FilePath(ppd_file_path);
+ } else {
+ // cupsGetPPD2 gets stuck sometimes in an infinite time due to network
+ // configuration/issues. To prevent that, use non-blocking http connection
+ // here.
+ // Note: After looking at CUPS sources, it looks like non-blocking
+ // connection will timeout after 10 seconds of no data period. And it will
+ // return the same way as if data was completely and sucessfully downloaded.
+ HttpConnectionCUPS http(print_server_url_, cups_encryption_);
+ http.SetBlocking(blocking_);
+ ppd_file_path = cupsGetPPD2(http.http(), name);
+ // Check if the get full PPD, since non-blocking call may simply return
+ // normally after timeout expired.
+ if (ppd_file_path) {
+ // There is no reliable way right now to detect full and complete PPD
+ // get downloaded. If we reach http timeout, it may simply return
+ // downloaded part as a full response. It might be good enough to check
+ // http->data_remaining or http->_data_remaining, unfortunately http_t
+ // is an internal structure and fields are not exposed in CUPS headers.
+ // httpGetLength or httpGetLength2 returning the full content size.
+ // Comparing file size against that content length might be unreliable
+ // since some http reponses are encoded and content_length > file size.
+ // Let's just check for the obvious CUPS and http errors here.
+ ppd_path = base::FilePath(ppd_file_path);
+ ipp_status_t error_code = cupsLastError();
+ int http_error = httpError(http.http());
+ if (error_code > IPP_OK_EVENTS_COMPLETE || http_error != 0) {
+ LOG(ERROR) << "Error downloading PPD file"
+ << ", name: " << name
+ << ", CUPS error: " << static_cast<int>(error_code)
+ << ", HTTP error: " << http_error;
+ base::DeleteFile(ppd_path, false);
+ ppd_path.clear();
+ }
+ }
+ }
+ return ppd_path;
+}
+
+} // namespace printing
diff --git a/chromium/printing/backend/print_backend_dummy.cc b/chromium/printing/backend/print_backend_dummy.cc
new file mode 100644
index 00000000000..fdc3b7ff363
--- /dev/null
+++ b/chromium/printing/backend/print_backend_dummy.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This is dummy implementation for all configurations where there is no
+// print backend.
+#if !defined(PRINT_BACKEND_AVAILABLE)
+
+#include "printing/backend/print_backend.h"
+
+#include "base/logging.h"
+#include "base/values.h"
+
+namespace printing {
+
+scoped_refptr<PrintBackend> PrintBackend::CreateInstance(
+ const DictionaryValue* print_backend_settings) {
+ NOTREACHED();
+ return NULL;
+}
+
+} // namespace printing
+
+#endif // PRINT_BACKEND_AVAILABLE
diff --git a/chromium/printing/backend/print_backend_unittest.cc b/chromium/printing/backend/print_backend_unittest.cc
new file mode 100644
index 00000000000..356a34627bf
--- /dev/null
+++ b/chromium/printing/backend/print_backend_unittest.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/strings/utf_string_conversions.h"
+#include "printing/backend/print_backend.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace printing {
+
+std::string Simplify(const char* title) {
+ return UTF16ToUTF8(PrintBackend::SimplifyDocumentTitle(ASCIIToUTF16(title)));
+}
+
+TEST(PrintBackendTest, SimplifyDocumentTitle) {
+ EXPECT_STREQ("", Simplify("").c_str());
+ EXPECT_STREQ("Long string. Long string...ng string. Long string.",
+ Simplify("Long string. Long string. Long string. Long string. "
+ "Long string. Long string. Long string.").c_str());
+ EXPECT_STREQ("Control Characters",
+ Simplify("C\ron\ntrol Charac\15ters").c_str());
+ EXPECT_STREQ("", Simplify("\n\r\n\r\t\r").c_str());
+}
+
+} // namespace printing
+
diff --git a/chromium/printing/backend/print_backend_win.cc b/chromium/printing/backend/print_backend_win.cc
new file mode 100644
index 00000000000..0d1958fd39c
--- /dev/null
+++ b/chromium/printing/backend/print_backend_win.cc
@@ -0,0 +1,255 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/backend/print_backend.h"
+
+#include <objidl.h>
+#include <winspool.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/win/scoped_bstr.h"
+#include "base/win/scoped_comptr.h"
+#include "base/win/scoped_hglobal.h"
+#include "printing/backend/print_backend_consts.h"
+#include "printing/backend/printing_info_win.h"
+#include "printing/backend/win_helper.h"
+
+
+namespace {
+
+HRESULT StreamOnHGlobalToString(IStream* stream, std::string* out) {
+ DCHECK(stream);
+ DCHECK(out);
+ HGLOBAL hdata = NULL;
+ HRESULT hr = GetHGlobalFromStream(stream, &hdata);
+ if (SUCCEEDED(hr)) {
+ DCHECK(hdata);
+ base::win::ScopedHGlobal<char> locked_data(hdata);
+ out->assign(locked_data.release(), locked_data.Size());
+ }
+ return hr;
+}
+
+} // namespace
+
+namespace printing {
+
+class PrintBackendWin : public PrintBackend {
+ public:
+ PrintBackendWin() {}
+
+ // PrintBackend implementation.
+ virtual bool EnumeratePrinters(PrinterList* printer_list) OVERRIDE;
+ virtual std::string GetDefaultPrinterName() OVERRIDE;
+ virtual bool GetPrinterSemanticCapsAndDefaults(
+ const std::string& printer_name,
+ PrinterSemanticCapsAndDefaults* printer_info) OVERRIDE;
+ virtual bool GetPrinterCapsAndDefaults(
+ const std::string& printer_name,
+ PrinterCapsAndDefaults* printer_info) OVERRIDE;
+ virtual std::string GetPrinterDriverInfo(
+ const std::string& printer_name) OVERRIDE;
+ virtual bool IsValidPrinter(const std::string& printer_name) OVERRIDE;
+
+ protected:
+ virtual ~PrintBackendWin() {}
+};
+
+bool PrintBackendWin::EnumeratePrinters(PrinterList* printer_list) {
+ DCHECK(printer_list);
+ DWORD bytes_needed = 0;
+ DWORD count_returned = 0;
+ const DWORD kLevel = 4;
+ BOOL ret = EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL,
+ kLevel, NULL, 0, &bytes_needed, &count_returned);
+ if (!bytes_needed)
+ return false;
+ scoped_ptr<BYTE[]> printer_info_buffer(new BYTE[bytes_needed]);
+ ret = EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS, NULL, kLevel,
+ printer_info_buffer.get(), bytes_needed, &bytes_needed,
+ &count_returned);
+ DCHECK(ret);
+ if (!ret)
+ return false;
+
+ std::string default_printer = GetDefaultPrinterName();
+ PRINTER_INFO_4* printer_info =
+ reinterpret_cast<PRINTER_INFO_4*>(printer_info_buffer.get());
+ for (DWORD index = 0; index < count_returned; index++) {
+ ScopedPrinterHandle printer;
+ PrinterBasicInfo info;
+ if (printer.OpenPrinter(printer_info[index].pPrinterName) &&
+ InitBasicPrinterInfo(printer, &info)) {
+ info.is_default = (info.printer_name == default_printer);
+ printer_list->push_back(info);
+ }
+ }
+ return true;
+}
+
+std::string PrintBackendWin::GetDefaultPrinterName() {
+ DWORD size = MAX_PATH;
+ TCHAR default_printer_name[MAX_PATH];
+ if (!::GetDefaultPrinter(default_printer_name, &size))
+ return std::string();
+ return WideToUTF8(default_printer_name);
+}
+
+bool PrintBackendWin::GetPrinterSemanticCapsAndDefaults(
+ const std::string& printer_name,
+ PrinterSemanticCapsAndDefaults* printer_info) {
+ ScopedPrinterHandle printer_handle;
+ if (!printer_handle.OpenPrinter(UTF8ToWide(printer_name).c_str())) {
+ LOG(WARNING) << "Failed to open printer, error = " << GetLastError();
+ return false;
+ }
+
+ PrinterInfo5 info_5;
+ if (!info_5.Init(printer_handle)) {
+ return false;
+ }
+ DCHECK_EQ(info_5.get()->pPrinterName, UTF8ToUTF16(printer_name));
+
+ PrinterSemanticCapsAndDefaults caps;
+
+ // Get printer capabilities. For more info see here:
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/dd183552(v=vs.85).aspx
+ caps.color_changeable = (::DeviceCapabilities(info_5.get()->pPrinterName,
+ info_5.get()->pPortName,
+ DC_COLORDEVICE,
+ NULL,
+ NULL) == 1);
+
+ caps.duplex_capable = (::DeviceCapabilities(info_5.get()->pPrinterName,
+ info_5.get()->pPortName,
+ DC_DUPLEX,
+ NULL,
+ NULL) == 1);
+
+ UserDefaultDevMode user_settings;
+
+ if (user_settings.Init(printer_handle)) {
+ if ((user_settings.get()->dmFields & DM_COLOR) == DM_COLOR)
+ caps.color_default = (user_settings.get()->dmColor == DMCOLOR_COLOR);
+
+ if ((user_settings.get()->dmFields & DM_DUPLEX) == DM_DUPLEX) {
+ switch (user_settings.get()->dmDuplex) {
+ case DMDUP_SIMPLEX:
+ caps.duplex_default = SIMPLEX;
+ break;
+ case DMDUP_VERTICAL:
+ caps.duplex_default = LONG_EDGE;
+ break;
+ case DMDUP_HORIZONTAL:
+ caps.duplex_default = SHORT_EDGE;
+ break;
+ default:
+ NOTREACHED();
+ }
+ }
+ } else {
+ LOG(WARNING) << "Fallback to color/simplex mode.";
+ caps.color_default = caps.color_changeable;
+ caps.duplex_default = SIMPLEX;
+ }
+
+ *printer_info = caps;
+ return true;
+}
+
+bool PrintBackendWin::GetPrinterCapsAndDefaults(
+ const std::string& printer_name,
+ PrinterCapsAndDefaults* printer_info) {
+ ScopedXPSInitializer xps_initializer;
+ if (!xps_initializer.initialized()) {
+ // TODO(sanjeevr): Handle legacy proxy case (with no prntvpt.dll)
+ return false;
+ }
+ if (!IsValidPrinter(printer_name)) {
+ return false;
+ }
+ DCHECK(printer_info);
+ HPTPROVIDER provider = NULL;
+ std::wstring printer_name_wide = UTF8ToWide(printer_name);
+ HRESULT hr = XPSModule::OpenProvider(printer_name_wide, 1, &provider);
+ if (provider) {
+ base::win::ScopedComPtr<IStream> print_capabilities_stream;
+ hr = CreateStreamOnHGlobal(NULL, TRUE,
+ print_capabilities_stream.Receive());
+ DCHECK(SUCCEEDED(hr));
+ if (print_capabilities_stream) {
+ base::win::ScopedBstr error;
+ hr = XPSModule::GetPrintCapabilities(provider,
+ NULL,
+ print_capabilities_stream,
+ error.Receive());
+ DCHECK(SUCCEEDED(hr));
+ if (FAILED(hr)) {
+ return false;
+ }
+ hr = StreamOnHGlobalToString(print_capabilities_stream.get(),
+ &printer_info->printer_capabilities);
+ DCHECK(SUCCEEDED(hr));
+ printer_info->caps_mime_type = "text/xml";
+ }
+ ScopedPrinterHandle printer_handle;
+ if (printer_handle.OpenPrinter(printer_name_wide.c_str())) {
+ LONG devmode_size = DocumentProperties(
+ NULL, printer_handle, const_cast<LPTSTR>(printer_name_wide.c_str()),
+ NULL, NULL, 0);
+ if (devmode_size <= 0)
+ return false;
+ scoped_ptr<BYTE[]> devmode_out_buffer(new BYTE[devmode_size]);
+ DEVMODE* devmode_out =
+ reinterpret_cast<DEVMODE*>(devmode_out_buffer.get());
+ DocumentProperties(
+ NULL, printer_handle, const_cast<LPTSTR>(printer_name_wide.c_str()),
+ devmode_out, NULL, DM_OUT_BUFFER);
+ base::win::ScopedComPtr<IStream> printer_defaults_stream;
+ hr = CreateStreamOnHGlobal(NULL, TRUE,
+ printer_defaults_stream.Receive());
+ DCHECK(SUCCEEDED(hr));
+ if (printer_defaults_stream) {
+ hr = XPSModule::ConvertDevModeToPrintTicket(provider,
+ devmode_size,
+ devmode_out,
+ kPTJobScope,
+ printer_defaults_stream);
+ DCHECK(SUCCEEDED(hr));
+ if (SUCCEEDED(hr)) {
+ hr = StreamOnHGlobalToString(printer_defaults_stream.get(),
+ &printer_info->printer_defaults);
+ DCHECK(SUCCEEDED(hr));
+ printer_info->defaults_mime_type = "text/xml";
+ }
+ }
+ }
+ XPSModule::CloseProvider(provider);
+ }
+ return true;
+}
+
+// Gets the information about driver for a specific printer.
+std::string PrintBackendWin::GetPrinterDriverInfo(
+ const std::string& printer_name) {
+ ScopedPrinterHandle printer;
+ if (!printer.OpenPrinter(UTF8ToWide(printer_name).c_str())) {
+ return std::string();
+ }
+ return GetDriverInfo(printer);
+}
+
+bool PrintBackendWin::IsValidPrinter(const std::string& printer_name) {
+ ScopedPrinterHandle printer_handle;
+ return printer_handle.OpenPrinter(UTF8ToWide(printer_name).c_str());
+}
+
+scoped_refptr<PrintBackend> PrintBackend::CreateInstance(
+ const base::DictionaryValue* print_backend_settings) {
+ return new PrintBackendWin;
+}
+
+} // namespace printing
diff --git a/chromium/printing/backend/printing_info_win.cc b/chromium/printing/backend/printing_info_win.cc
new file mode 100644
index 00000000000..0e4509430d2
--- /dev/null
+++ b/chromium/printing/backend/printing_info_win.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/backend/printing_info_win.h"
+
+#include "base/logging.h"
+
+namespace printing {
+
+namespace internal {
+
+uint8* GetDriverInfo(HANDLE printer, int level) {
+ DWORD size = 0;
+ ::GetPrinterDriver(printer, NULL, level, NULL, 0, &size);
+ if (size == 0) {
+ return NULL;
+ }
+ scoped_ptr<uint8[]> buffer(new uint8[size]);
+ memset(buffer.get(), 0, size);
+ if (!::GetPrinterDriver(printer, NULL, level, buffer.get(), size, &size)) {
+ return NULL;
+ }
+ return buffer.release();
+}
+
+uint8* GetPrinterInfo(HANDLE printer, int level) {
+ DWORD size = 0;
+ ::GetPrinter(printer, level, NULL, 0, &size);
+ if (size == 0) {
+ LOG(WARNING) << "Failed to get size of PRINTER_INFO_" << level <<
+ ", error = " << GetLastError();
+ return NULL;
+ }
+ scoped_ptr<uint8[]> buffer(new uint8[size]);
+ memset(buffer.get(), 0, size);
+ if (!::GetPrinter(printer, level, buffer.get(), size, &size)) {
+ LOG(WARNING) << "Failed to get PRINTER_INFO_" << level <<
+ ", error = " << GetLastError();
+ return NULL;
+ }
+ return buffer.release();
+}
+
+} // namespace internal
+
+UserDefaultDevMode::UserDefaultDevMode() : dev_mode_(NULL) {
+}
+
+bool UserDefaultDevMode::Init(HANDLE printer) {
+ if (info_9_.Init(printer))
+ dev_mode_ = info_9_.get()->pDevMode;
+
+ if (!dev_mode_ && info_8_.Init(printer))
+ dev_mode_ = info_8_.get()->pDevMode;
+
+ if (!dev_mode_ && info_2_.Init(printer))
+ dev_mode_ = info_2_.get()->pDevMode;
+
+ return dev_mode_ != NULL;
+}
+
+UserDefaultDevMode::~UserDefaultDevMode() {
+}
+
+} // namespace printing
diff --git a/chromium/printing/backend/printing_info_win.h b/chromium/printing/backend/printing_info_win.h
new file mode 100644
index 00000000000..931ae6df0c4
--- /dev/null
+++ b/chromium/printing/backend/printing_info_win.h
@@ -0,0 +1,91 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_BACKEND_PRINTING_INFO_WIN_H_
+#define PRINTING_BACKEND_PRINTING_INFO_WIN_H_
+
+#include <objidl.h>
+#include <winspool.h>
+
+#include "base/memory/scoped_ptr.h"
+#include "printing/printing_export.h"
+
+namespace printing {
+
+namespace internal {
+
+PRINTING_EXPORT uint8* GetDriverInfo(HANDLE printer, int level);
+PRINTING_EXPORT uint8* GetPrinterInfo(HANDLE printer, int level);
+
+// This class is designed to work with PRINTER_INFO_X structures
+// and calls GetPrinter internally with correctly allocated buffer.
+template <typename PrinterInfoType, int level>
+class PrinterInfo {
+ public:
+ bool Init(HANDLE printer) {
+ buffer_.reset(GetPrinterInfo(printer, level));
+ return buffer_;
+ }
+
+ const PrinterInfoType* get() const {
+ return reinterpret_cast<const PrinterInfoType*>(buffer_.get());
+ }
+
+ private:
+ scoped_ptr<uint8[]> buffer_;
+};
+
+// This class is designed to work with DRIVER_INFO_X structures
+// and calls GetDriverInfo internally with correctly allocated buffer.
+template <typename DriverInfoType, int level>
+class DriverInfo {
+ public:
+ bool Init(HANDLE printer) {
+ buffer_.reset(GetDriverInfo(printer, level));
+ return buffer_;
+ }
+
+ const DriverInfoType* get() const {
+ return reinterpret_cast<const DriverInfoType*>(buffer_.get());
+ }
+
+ private:
+ scoped_ptr<uint8[]> buffer_;
+};
+
+} // namespace internal
+
+typedef internal::PrinterInfo<PRINTER_INFO_2, 2> PrinterInfo2;
+typedef internal::PrinterInfo<PRINTER_INFO_5, 5> PrinterInfo5;
+typedef internal::PrinterInfo<PRINTER_INFO_8, 8> PrinterInfo8;
+typedef internal::PrinterInfo<PRINTER_INFO_9, 9> PrinterInfo9;
+
+typedef internal::DriverInfo<DRIVER_INFO_6, 6> DriverInfo6;
+
+// Retrieves DEVMODE from PRINTER_INFO_* structures.
+// Requests in following order:
+// 9 (user-default),
+// 8 (admin-default),
+// 2 (printer-default).
+class PRINTING_EXPORT UserDefaultDevMode {
+ public:
+ UserDefaultDevMode();
+ ~UserDefaultDevMode();
+
+ bool Init(HANDLE printer);
+
+ const DEVMODE* get() const {
+ return dev_mode_;
+ }
+
+ private:
+ PrinterInfo2 info_2_;
+ PrinterInfo8 info_8_;
+ PrinterInfo9 info_9_;
+ const DEVMODE* dev_mode_;
+};
+
+} // namespace printing
+
+#endif // PRINTING_BACKEND_PRINTING_INFO_WIN_H_
diff --git a/chromium/printing/backend/win_helper.cc b/chromium/printing/backend/win_helper.cc
new file mode 100644
index 00000000000..3832dff69a0
--- /dev/null
+++ b/chromium/printing/backend/win_helper.cc
@@ -0,0 +1,344 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/backend/win_helper.h"
+
+#include <algorithm>
+
+#include "base/file_version_info.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/utf_string_conversions.h"
+#include "printing/backend/print_backend.h"
+#include "printing/backend/print_backend_consts.h"
+#include "printing/backend/printing_info_win.h"
+
+namespace {
+
+typedef HRESULT (WINAPI* PTOpenProviderProc)(PCWSTR printer_name,
+ DWORD version,
+ HPTPROVIDER* provider);
+
+typedef HRESULT (WINAPI* PTGetPrintCapabilitiesProc)(HPTPROVIDER provider,
+ IStream* print_ticket,
+ IStream* capabilities,
+ BSTR* error_message);
+
+typedef HRESULT (WINAPI* PTConvertDevModeToPrintTicketProc)(
+ HPTPROVIDER provider,
+ ULONG devmode_size_in_bytes,
+ PDEVMODE devmode,
+ EPrintTicketScope scope,
+ IStream* print_ticket);
+
+typedef HRESULT (WINAPI* PTConvertPrintTicketToDevModeProc)(
+ HPTPROVIDER provider,
+ IStream* print_ticket,
+ EDefaultDevmodeType base_devmode_type,
+ EPrintTicketScope scope,
+ ULONG* devmode_byte_count,
+ PDEVMODE* devmode,
+ BSTR* error_message);
+
+typedef HRESULT (WINAPI* PTMergeAndValidatePrintTicketProc)(
+ HPTPROVIDER provider,
+ IStream* base_ticket,
+ IStream* delta_ticket,
+ EPrintTicketScope scope,
+ IStream* result_ticket,
+ BSTR* error_message);
+
+typedef HRESULT (WINAPI* PTReleaseMemoryProc)(PVOID buffer);
+
+typedef HRESULT (WINAPI* PTCloseProviderProc)(HPTPROVIDER provider);
+
+typedef HRESULT (WINAPI* StartXpsPrintJobProc)(
+ const LPCWSTR printer_name,
+ const LPCWSTR job_name,
+ const LPCWSTR output_file_name,
+ HANDLE progress_event,
+ HANDLE completion_event,
+ UINT8* printable_pages_on,
+ UINT32 printable_pages_on_count,
+ IXpsPrintJob** xps_print_job,
+ IXpsPrintJobStream** document_stream,
+ IXpsPrintJobStream** print_ticket_stream);
+
+PTOpenProviderProc g_open_provider_proc = NULL;
+PTGetPrintCapabilitiesProc g_get_print_capabilities_proc = NULL;
+PTConvertDevModeToPrintTicketProc g_convert_devmode_to_print_ticket_proc = NULL;
+PTConvertPrintTicketToDevModeProc g_convert_print_ticket_to_devmode_proc = NULL;
+PTMergeAndValidatePrintTicketProc g_merge_and_validate_print_ticket_proc = NULL;
+PTReleaseMemoryProc g_release_memory_proc = NULL;
+PTCloseProviderProc g_close_provider_proc = NULL;
+StartXpsPrintJobProc g_start_xps_print_job_proc = NULL;
+
+} // namespace
+
+
+namespace printing {
+
+bool XPSModule::Init() {
+ static bool initialized = InitImpl();
+ return initialized;
+}
+
+bool XPSModule::InitImpl() {
+ HMODULE prntvpt_module = LoadLibrary(L"prntvpt.dll");
+ if (prntvpt_module == NULL)
+ return false;
+ g_open_provider_proc = reinterpret_cast<PTOpenProviderProc>(
+ GetProcAddress(prntvpt_module, "PTOpenProvider"));
+ if (!g_open_provider_proc) {
+ NOTREACHED();
+ return false;
+ }
+ g_get_print_capabilities_proc = reinterpret_cast<PTGetPrintCapabilitiesProc>(
+ GetProcAddress(prntvpt_module, "PTGetPrintCapabilities"));
+ if (!g_get_print_capabilities_proc) {
+ NOTREACHED();
+ return false;
+ }
+ g_convert_devmode_to_print_ticket_proc =
+ reinterpret_cast<PTConvertDevModeToPrintTicketProc>(
+ GetProcAddress(prntvpt_module, "PTConvertDevModeToPrintTicket"));
+ if (!g_convert_devmode_to_print_ticket_proc) {
+ NOTREACHED();
+ return false;
+ }
+ g_convert_print_ticket_to_devmode_proc =
+ reinterpret_cast<PTConvertPrintTicketToDevModeProc>(
+ GetProcAddress(prntvpt_module, "PTConvertPrintTicketToDevMode"));
+ if (!g_convert_print_ticket_to_devmode_proc) {
+ NOTREACHED();
+ return false;
+ }
+ g_merge_and_validate_print_ticket_proc =
+ reinterpret_cast<PTMergeAndValidatePrintTicketProc>(
+ GetProcAddress(prntvpt_module, "PTMergeAndValidatePrintTicket"));
+ if (!g_merge_and_validate_print_ticket_proc) {
+ NOTREACHED();
+ return false;
+ }
+ g_release_memory_proc =
+ reinterpret_cast<PTReleaseMemoryProc>(
+ GetProcAddress(prntvpt_module, "PTReleaseMemory"));
+ if (!g_release_memory_proc) {
+ NOTREACHED();
+ return false;
+ }
+ g_close_provider_proc =
+ reinterpret_cast<PTCloseProviderProc>(
+ GetProcAddress(prntvpt_module, "PTCloseProvider"));
+ if (!g_close_provider_proc) {
+ NOTREACHED();
+ return false;
+ }
+ return true;
+}
+
+HRESULT XPSModule::OpenProvider(const string16& printer_name,
+ DWORD version,
+ HPTPROVIDER* provider) {
+ return g_open_provider_proc(printer_name.c_str(), version, provider);
+}
+
+HRESULT XPSModule::GetPrintCapabilities(HPTPROVIDER provider,
+ IStream* print_ticket,
+ IStream* capabilities,
+ BSTR* error_message) {
+ return g_get_print_capabilities_proc(provider,
+ print_ticket,
+ capabilities,
+ error_message);
+}
+
+HRESULT XPSModule::ConvertDevModeToPrintTicket(HPTPROVIDER provider,
+ ULONG devmode_size_in_bytes,
+ PDEVMODE devmode,
+ EPrintTicketScope scope,
+ IStream* print_ticket) {
+ return g_convert_devmode_to_print_ticket_proc(provider,
+ devmode_size_in_bytes,
+ devmode,
+ scope,
+ print_ticket);
+}
+
+HRESULT XPSModule::ConvertPrintTicketToDevMode(
+ HPTPROVIDER provider,
+ IStream* print_ticket,
+ EDefaultDevmodeType base_devmode_type,
+ EPrintTicketScope scope,
+ ULONG* devmode_byte_count,
+ PDEVMODE* devmode,
+ BSTR* error_message) {
+ return g_convert_print_ticket_to_devmode_proc(provider,
+ print_ticket,
+ base_devmode_type,
+ scope,
+ devmode_byte_count,
+ devmode,
+ error_message);
+}
+
+HRESULT XPSModule::MergeAndValidatePrintTicket(HPTPROVIDER provider,
+ IStream* base_ticket,
+ IStream* delta_ticket,
+ EPrintTicketScope scope,
+ IStream* result_ticket,
+ BSTR* error_message) {
+ return g_merge_and_validate_print_ticket_proc(provider,
+ base_ticket,
+ delta_ticket,
+ scope,
+ result_ticket,
+ error_message);
+}
+
+HRESULT XPSModule::ReleaseMemory(PVOID buffer) {
+ return g_release_memory_proc(buffer);
+}
+
+HRESULT XPSModule::CloseProvider(HPTPROVIDER provider) {
+ return g_close_provider_proc(provider);
+}
+
+ScopedXPSInitializer::ScopedXPSInitializer() : initialized_(false) {
+ if (!XPSModule::Init())
+ return;
+ // Calls to XPS APIs typically require the XPS provider to be opened with
+ // PTOpenProvider. PTOpenProvider calls CoInitializeEx with
+ // COINIT_MULTITHREADED. We have seen certain buggy HP printer driver DLLs
+ // that call CoInitializeEx with COINIT_APARTMENTTHREADED in the context of
+ // PTGetPrintCapabilities. This call fails but the printer driver calls
+ // CoUninitialize anyway. This results in the apartment being torn down too
+ // early and the msxml DLL being unloaded which in turn causes code in
+ // unidrvui.dll to have a dangling pointer to an XML document which causes a
+ // crash. To protect ourselves from such drivers we make sure we always have
+ // an extra CoInitialize (calls to CoInitialize/CoUninitialize are
+ // refcounted).
+ HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+ // If this succeeded we are done because the PTOpenProvider call will provide
+ // the extra refcount on the apartment. If it failed because someone already
+ // called CoInitializeEx with COINIT_APARTMENTTHREADED, we try the other model
+ // to provide the additional refcount (since we don't know which model buggy
+ // printer drivers will use).
+ if (!SUCCEEDED(hr))
+ hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+ DCHECK(SUCCEEDED(hr));
+ initialized_ = true;
+}
+
+ScopedXPSInitializer::~ScopedXPSInitializer() {
+ if (initialized_)
+ CoUninitialize();
+ initialized_ = false;
+}
+
+bool XPSPrintModule::Init() {
+ static bool initialized = InitImpl();
+ return initialized;
+}
+
+bool XPSPrintModule::InitImpl() {
+ HMODULE xpsprint_module = LoadLibrary(L"xpsprint.dll");
+ if (xpsprint_module == NULL)
+ return false;
+ g_start_xps_print_job_proc = reinterpret_cast<StartXpsPrintJobProc>(
+ GetProcAddress(xpsprint_module, "StartXpsPrintJob"));
+ if (!g_start_xps_print_job_proc) {
+ NOTREACHED();
+ return false;
+ }
+ return true;
+}
+
+HRESULT XPSPrintModule::StartXpsPrintJob(
+ const LPCWSTR printer_name,
+ const LPCWSTR job_name,
+ const LPCWSTR output_file_name,
+ HANDLE progress_event,
+ HANDLE completion_event,
+ UINT8* printable_pages_on,
+ UINT32 printable_pages_on_count,
+ IXpsPrintJob** xps_print_job,
+ IXpsPrintJobStream** document_stream,
+ IXpsPrintJobStream** print_ticket_stream) {
+ return g_start_xps_print_job_proc(printer_name,
+ job_name,
+ output_file_name,
+ progress_event,
+ completion_event,
+ printable_pages_on,
+ printable_pages_on_count,
+ xps_print_job,
+ document_stream,
+ print_ticket_stream);
+}
+
+bool InitBasicPrinterInfo(HANDLE printer, PrinterBasicInfo* printer_info) {
+ DCHECK(printer);
+ DCHECK(printer_info);
+ if (!printer)
+ return false;
+
+ PrinterInfo2 info_2;
+ if (!info_2.Init(printer))
+ return false;
+
+ printer_info->printer_name = WideToUTF8(info_2.get()->pPrinterName);
+ if (info_2.get()->pComment)
+ printer_info->printer_description = WideToUTF8(info_2.get()->pComment);
+ if (info_2.get()->pLocation)
+ printer_info->options[kLocationTagName] =
+ WideToUTF8(info_2.get()->pLocation);
+ if (info_2.get()->pDriverName)
+ printer_info->options[kDriverNameTagName] =
+ WideToUTF8(info_2.get()->pDriverName);
+ printer_info->printer_status = info_2.get()->Status;
+
+ std::string driver_info = GetDriverInfo(printer);
+ if (!driver_info.empty())
+ printer_info->options[kDriverInfoTagName] = driver_info;
+ return true;
+}
+
+std::string GetDriverInfo(HANDLE printer) {
+ DCHECK(printer);
+ std::string driver_info;
+
+ if (!printer)
+ return driver_info;
+
+ DriverInfo6 info_6;
+ if (!info_6.Init(printer))
+ return driver_info;
+
+ std::string info[4];
+ if (info_6.get()->pName)
+ info[0] = WideToUTF8(info_6.get()->pName);
+
+ if (info_6.get()->pDriverPath) {
+ scoped_ptr<FileVersionInfo> version_info(
+ FileVersionInfo::CreateFileVersionInfo(
+ base::FilePath(info_6.get()->pDriverPath)));
+ if (version_info.get()) {
+ info[1] = WideToUTF8(version_info->file_version());
+ info[2] = WideToUTF8(version_info->product_name());
+ info[3] = WideToUTF8(version_info->product_version());
+ }
+ }
+
+ for (size_t i = 0; i < arraysize(info); ++i) {
+ std::replace(info[i].begin(), info[i].end(), ';', ',');
+ driver_info.append(info[i]);
+ if (i < arraysize(info) - 1)
+ driver_info.append(";");
+ }
+ return driver_info;
+}
+
+} // namespace printing
diff --git a/chromium/printing/backend/win_helper.h b/chromium/printing/backend/win_helper.h
new file mode 100644
index 00000000000..d340b67d43c
--- /dev/null
+++ b/chromium/printing/backend/win_helper.h
@@ -0,0 +1,152 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_BACKEND_WIN_HELPER_H_
+#define PRINTING_BACKEND_WIN_HELPER_H_
+
+#include <objidl.h>
+#include <winspool.h>
+#include <prntvpt.h>
+#include <xpsprint.h>
+
+#include <string>
+
+#include "base/strings/string16.h"
+#include "base/win/scoped_handle.h"
+#include "printing/printing_export.h"
+
+// These are helper functions for dealing with Windows Printing.
+namespace printing {
+
+struct PRINTING_EXPORT PrinterBasicInfo;
+
+class PrinterHandleTraits {
+ public:
+ typedef HANDLE Handle;
+
+ static bool CloseHandle(HANDLE handle) {
+ return ::ClosePrinter(handle) != FALSE;
+ }
+
+ static bool IsHandleValid(HANDLE handle) {
+ return handle != NULL;
+ }
+
+ static HANDLE NullHandle() {
+ return NULL;
+ }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(PrinterHandleTraits);
+};
+
+class ScopedPrinterHandle
+ : public base::win::GenericScopedHandle<PrinterHandleTraits,
+ base::win::VerifierTraits> {
+ public:
+ bool OpenPrinter(const wchar_t* printer) {
+ // ::OpenPrinter may return error but assign some value into handle.
+ if (!::OpenPrinter(const_cast<LPTSTR>(printer), Receive(), NULL)) {
+ Take();
+ }
+ return IsValid();
+ }
+
+ private:
+ typedef base::win::GenericScopedHandle<PrinterHandleTraits,
+ base::win::VerifierTraits> Base;
+ // Hide Receive to avoid assigning handle when ::OpenPrinter returned error.
+ Base::Receiver Receive() {
+ return Base::Receive();
+ }
+};
+
+// Wrapper class to wrap the XPS APIs (PTxxx APIs). This is needed because these
+// APIs are not available by default on XP. We could delayload prntvpt.dll but
+// this would mean having to add that to every binary that links with
+// printing.lib (which is a LOT of binaries). So choosing the GetProcAddress
+// route instead).
+class PRINTING_EXPORT XPSModule {
+ public:
+ // All the other methods can ONLY be called after a successful call to Init.
+ // Init can be called many times and by multiple threads.
+ static bool Init();
+ static HRESULT OpenProvider(const string16& printer_name,
+ DWORD version,
+ HPTPROVIDER* provider);
+ static HRESULT GetPrintCapabilities(HPTPROVIDER provider,
+ IStream* print_ticket,
+ IStream* capabilities,
+ BSTR* error_message);
+ static HRESULT ConvertDevModeToPrintTicket(HPTPROVIDER provider,
+ ULONG devmode_size_in_bytes,
+ PDEVMODE devmode,
+ EPrintTicketScope scope,
+ IStream* print_ticket);
+ static HRESULT ConvertPrintTicketToDevMode(
+ HPTPROVIDER provider,
+ IStream* print_ticket,
+ EDefaultDevmodeType base_devmode_type,
+ EPrintTicketScope scope,
+ ULONG* devmode_byte_count,
+ PDEVMODE* devmode,
+ BSTR* error_message);
+ static HRESULT MergeAndValidatePrintTicket(HPTPROVIDER provider,
+ IStream* base_ticket,
+ IStream* delta_ticket,
+ EPrintTicketScope scope,
+ IStream* result_ticket,
+ BSTR* error_message);
+ static HRESULT ReleaseMemory(PVOID buffer);
+ static HRESULT CloseProvider(HPTPROVIDER provider);
+
+ private:
+ XPSModule() { }
+ static bool InitImpl();
+};
+
+// See comments in cc file explaining why we need this.
+class PRINTING_EXPORT ScopedXPSInitializer {
+ public:
+ ScopedXPSInitializer();
+ ~ScopedXPSInitializer();
+
+ bool initialized() const { return initialized_; }
+
+ private:
+ bool initialized_;
+};
+
+// Wrapper class to wrap the XPS Print APIs (these are different from the PTxxx
+// which deal with the XML Print Schema). This is needed because these
+// APIs are only available on Windows 7 and higher.
+class PRINTING_EXPORT XPSPrintModule {
+ public:
+ // All the other methods can ONLY be called after a successful call to Init.
+ // Init can be called many times and by multiple threads.
+ static bool Init();
+ static HRESULT StartXpsPrintJob(
+ const LPCWSTR printer_name,
+ const LPCWSTR job_name,
+ const LPCWSTR output_file_name,
+ HANDLE progress_event,
+ HANDLE completion_event,
+ UINT8* printable_pages_on,
+ UINT32 printable_pages_on_count,
+ IXpsPrintJob **xps_print_job,
+ IXpsPrintJobStream **document_stream,
+ IXpsPrintJobStream **print_ticket_stream);
+ private:
+ XPSPrintModule() { }
+ static bool InitImpl();
+};
+
+PRINTING_EXPORT bool InitBasicPrinterInfo(HANDLE printer,
+ PrinterBasicInfo* printer_info);
+
+PRINTING_EXPORT std::string GetDriverInfo(HANDLE printer);
+
+} // namespace printing
+
+#endif // PRINTING_BACKEND_WIN_HELPER_H_
diff --git a/chromium/printing/cups_config_helper.py b/chromium/printing/cups_config_helper.py
new file mode 100755
index 00000000000..3fe5fe04e1a
--- /dev/null
+++ b/chromium/printing/cups_config_helper.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+# Copyright (c) 2011 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""cups-config wrapper.
+
+cups-config, at least on Ubuntu Lucid and Natty, dumps all
+cflags/ldflags/libs when passed the --libs argument. gyp would like
+to keep these separate: cflags are only needed when compiling files
+that use cups directly, while libs are only needed on the final link
+line.
+
+TODO(evan): remove me once
+ https://bugs.launchpad.net/ubuntu/+source/cupsys/+bug/163704
+is fixed.
+"""
+
+import subprocess
+import sys
+
+def usage():
+ print 'usage: %s {--cflags|--ldflags|--libs}' % sys.argv[0]
+
+
+def run_cups_config(mode):
+ """Run cups-config with all --cflags etc modes, parse out the mode we want,
+ and return those flags as a list."""
+
+ cups = subprocess.Popen(['cups-config', '--cflags', '--ldflags', '--libs'],
+ stdout=subprocess.PIPE)
+ flags = cups.communicate()[0].strip()
+
+ flags_subset = []
+ for flag in flags.split():
+ flag_mode = None
+ if flag.startswith('-l'):
+ flag_mode = '--libs'
+ elif (flag.startswith('-L') or flag.startswith('-Wl,')):
+ flag_mode = '--ldflags'
+ elif (flag.startswith('-I') or flag.startswith('-D')):
+ flag_mode = '--cflags'
+
+ # Be conservative: for flags where we don't know which mode they
+ # belong in, always include them.
+ if flag_mode is None or flag_mode == mode:
+ flags_subset.append(flag)
+
+ return flags_subset
+
+
+def main():
+ if len(sys.argv) != 2:
+ usage()
+ return 1
+
+ mode = sys.argv[1]
+ if mode not in ('--cflags', '--libs', '--ldflags'):
+ usage()
+ return 1
+ flags = run_cups_config(mode)
+ print ' '.join(flags)
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/chromium/printing/emf_win.cc b/chromium/printing/emf_win.cc
new file mode 100644
index 00000000000..98c8f8ffa6f
--- /dev/null
+++ b/chromium/printing/emf_win.cc
@@ -0,0 +1,678 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/emf_win.h"
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/win/scoped_gdi_object.h"
+#include "base/win/scoped_hdc.h"
+#include "base/win/scoped_select_object.h"
+#include "skia/ext/vector_platform_device_emf_win.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/gfx/codec/jpeg_codec.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/gdi_util.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+
+namespace {
+
+const int kCustomGdiCommentSignature = 0xdeadbabe;
+struct PageBreakRecord {
+ int signature;
+ enum PageBreakType {
+ START_PAGE,
+ END_PAGE,
+ } type;
+ explicit PageBreakRecord(PageBreakType type_in)
+ : signature(kCustomGdiCommentSignature), type(type_in) {
+ }
+ bool IsValid() const {
+ return (signature == kCustomGdiCommentSignature) &&
+ (type >= START_PAGE) && (type <= END_PAGE);
+ }
+};
+
+int CALLBACK IsAlphaBlendUsedEnumProc(HDC,
+ HANDLETABLE*,
+ const ENHMETARECORD *record,
+ int,
+ LPARAM data) {
+ bool* result = reinterpret_cast<bool*>(data);
+ if (!result)
+ return 0;
+ switch (record->iType) {
+ case EMR_ALPHABLEND: {
+ *result = true;
+ return 0;
+ break;
+ }
+ }
+ return 1;
+}
+
+int CALLBACK RasterizeAlphaBlendProc(HDC metafile_dc,
+ HANDLETABLE* handle_table,
+ const ENHMETARECORD *record,
+ int num_objects,
+ LPARAM data) {
+ HDC bitmap_dc = *reinterpret_cast<HDC*>(data);
+ // Play this command to the bitmap DC.
+ ::PlayEnhMetaFileRecord(bitmap_dc, handle_table, record, num_objects);
+ switch (record->iType) {
+ case EMR_ALPHABLEND: {
+ const EMRALPHABLEND* alpha_blend =
+ reinterpret_cast<const EMRALPHABLEND*>(record);
+ // Don't modify transformation here.
+ // Old implementation did reset transformations for DC to identity matrix.
+ // That was not correct and cause some bugs, like unexpected cropping.
+ // EMRALPHABLEND is rendered into bitmap and metafile contexts with
+ // current transformation. If we don't touch them here BitBlt will copy
+ // same areas.
+ ::BitBlt(metafile_dc,
+ alpha_blend->xDest,
+ alpha_blend->yDest,
+ alpha_blend->cxDest,
+ alpha_blend->cyDest,
+ bitmap_dc,
+ alpha_blend->xDest,
+ alpha_blend->yDest,
+ SRCCOPY);
+ break;
+ }
+ case EMR_CREATEBRUSHINDIRECT:
+ case EMR_CREATECOLORSPACE:
+ case EMR_CREATECOLORSPACEW:
+ case EMR_CREATEDIBPATTERNBRUSHPT:
+ case EMR_CREATEMONOBRUSH:
+ case EMR_CREATEPALETTE:
+ case EMR_CREATEPEN:
+ case EMR_DELETECOLORSPACE:
+ case EMR_DELETEOBJECT:
+ case EMR_EXTCREATEFONTINDIRECTW:
+ // Play object creation command only once.
+ break;
+
+ default:
+ // Play this command to the metafile DC.
+ ::PlayEnhMetaFileRecord(metafile_dc, handle_table, record, num_objects);
+ break;
+ }
+ return 1; // Continue enumeration
+}
+
+// Bitmapt for rasterization.
+class RasterBitmap {
+ public:
+ explicit RasterBitmap(const gfx::Size& raster_size)
+ : saved_object_(NULL) {
+ context_.Set(::CreateCompatibleDC(NULL));
+ if (!context_) {
+ NOTREACHED() << "Bitmap DC creation failed";
+ return;
+ }
+ ::SetGraphicsMode(context_, GM_ADVANCED);
+ void* bits = NULL;
+ gfx::Rect bitmap_rect(raster_size);
+ gfx::CreateBitmapHeader(raster_size.width(), raster_size.height(),
+ &header_.bmiHeader);
+ bitmap_.Set(::CreateDIBSection(context_, &header_, DIB_RGB_COLORS, &bits,
+ NULL, 0));
+ if (!bitmap_)
+ NOTREACHED() << "Raster bitmap creation for printing failed";
+
+ saved_object_ = ::SelectObject(context_, bitmap_);
+ RECT rect = bitmap_rect.ToRECT();
+ ::FillRect(context_, &rect,
+ static_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH)));
+
+ }
+
+ ~RasterBitmap() {
+ ::SelectObject(context_, saved_object_);
+ }
+
+ HDC context() const {
+ return context_;
+ }
+
+ base::win::ScopedCreateDC context_;
+ BITMAPINFO header_;
+ base::win::ScopedBitmap bitmap_;
+ HGDIOBJ saved_object_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RasterBitmap);
+};
+
+
+
+} // namespace
+
+namespace printing {
+
+bool DIBFormatNativelySupported(HDC dc, uint32 escape, const BYTE* bits,
+ int size) {
+ BOOL supported = FALSE;
+ if (ExtEscape(dc, QUERYESCSUPPORT, sizeof(escape),
+ reinterpret_cast<LPCSTR>(&escape), 0, 0) > 0) {
+ ExtEscape(dc, escape, size, reinterpret_cast<LPCSTR>(bits),
+ sizeof(supported), reinterpret_cast<LPSTR>(&supported));
+ }
+ return !!supported;
+}
+
+Emf::Emf() : emf_(NULL), hdc_(NULL), page_count_(0) {
+}
+
+Emf::~Emf() {
+ DCHECK(!hdc_);
+ if (emf_)
+ DeleteEnhMetaFile(emf_);
+}
+
+bool Emf::InitToFile(const base::FilePath& metafile_path) {
+ DCHECK(!emf_ && !hdc_);
+ hdc_ = CreateEnhMetaFile(NULL, metafile_path.value().c_str(), NULL, NULL);
+ DCHECK(hdc_);
+ return hdc_ != NULL;
+}
+
+bool Emf::InitFromFile(const base::FilePath& metafile_path) {
+ DCHECK(!emf_ && !hdc_);
+ emf_ = GetEnhMetaFile(metafile_path.value().c_str());
+ DCHECK(emf_);
+ return emf_ != NULL;
+}
+
+bool Emf::Init() {
+ DCHECK(!emf_ && !hdc_);
+ hdc_ = CreateEnhMetaFile(NULL, NULL, NULL, NULL);
+ DCHECK(hdc_);
+ return hdc_ != NULL;
+}
+
+bool Emf::InitFromData(const void* src_buffer, uint32 src_buffer_size) {
+ DCHECK(!emf_ && !hdc_);
+ emf_ = SetEnhMetaFileBits(src_buffer_size,
+ reinterpret_cast<const BYTE*>(src_buffer));
+ return emf_ != NULL;
+}
+
+bool Emf::FinishDocument() {
+ DCHECK(!emf_ && hdc_);
+ emf_ = CloseEnhMetaFile(hdc_);
+ DCHECK(emf_);
+ hdc_ = NULL;
+ return emf_ != NULL;
+}
+
+bool Emf::Playback(HDC hdc, const RECT* rect) const {
+ DCHECK(emf_ && !hdc_);
+ RECT bounds;
+ if (!rect) {
+ // Get the natural bounds of the EMF buffer.
+ bounds = GetPageBounds(1).ToRECT();
+ rect = &bounds;
+ }
+ return PlayEnhMetaFile(hdc, emf_, rect) != 0;
+}
+
+bool Emf::SafePlayback(HDC context) const {
+ DCHECK(emf_ && !hdc_);
+ XFORM base_matrix;
+ if (!GetWorldTransform(context, &base_matrix)) {
+ NOTREACHED();
+ return false;
+ }
+ Emf::EnumerationContext playback_context;
+ playback_context.base_matrix = &base_matrix;
+ RECT rect = GetPageBounds(1).ToRECT();
+ return EnumEnhMetaFile(context,
+ emf_,
+ &Emf::SafePlaybackProc,
+ reinterpret_cast<void*>(&playback_context),
+ &rect) != 0;
+}
+
+gfx::Rect Emf::GetPageBounds(unsigned int page_number) const {
+ DCHECK(emf_ && !hdc_);
+ DCHECK_EQ(1U, page_number);
+ ENHMETAHEADER header;
+ if (GetEnhMetaFileHeader(emf_, sizeof(header), &header) != sizeof(header)) {
+ NOTREACHED();
+ return gfx::Rect();
+ }
+ // Add 1 to right and bottom because it's inclusive rectangle.
+ // See ENHMETAHEADER.
+ return gfx::Rect(header.rclBounds.left,
+ header.rclBounds.top,
+ header.rclBounds.right - header.rclBounds.left + 1,
+ header.rclBounds.bottom - header.rclBounds.top + 1);
+}
+
+uint32 Emf::GetDataSize() const {
+ DCHECK(emf_ && !hdc_);
+ return GetEnhMetaFileBits(emf_, 0, NULL);
+}
+
+bool Emf::GetData(void* buffer, uint32 size) const {
+ DCHECK(emf_ && !hdc_);
+ DCHECK(buffer && size);
+ uint32 size2 =
+ GetEnhMetaFileBits(emf_, size, reinterpret_cast<BYTE*>(buffer));
+ DCHECK(size2 == size);
+ return size2 == size && size2 != 0;
+}
+
+bool Emf::GetDataAsVector(std::vector<uint8>* buffer) const {
+ uint32 size = GetDataSize();
+ if (!size)
+ return false;
+
+ buffer->resize(size);
+ if (!GetData(&buffer->front(), size))
+ return false;
+ return true;
+}
+
+bool Emf::SaveTo(const base::FilePath& file_path) const {
+ HANDLE file = CreateFile(file_path.value().c_str(), GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ CREATE_ALWAYS, 0, NULL);
+ if (file == INVALID_HANDLE_VALUE)
+ return false;
+
+ bool success = false;
+ std::vector<uint8> buffer;
+ if (GetDataAsVector(&buffer)) {
+ DWORD written = 0;
+ if (WriteFile(file, &*buffer.begin(), static_cast<DWORD>(buffer.size()),
+ &written, NULL) &&
+ written == buffer.size()) {
+ success = true;
+ }
+ }
+ CloseHandle(file);
+ return success;
+}
+
+int CALLBACK Emf::SafePlaybackProc(HDC hdc,
+ HANDLETABLE* handle_table,
+ const ENHMETARECORD* record,
+ int objects_count,
+ LPARAM param) {
+ Emf::EnumerationContext* context =
+ reinterpret_cast<Emf::EnumerationContext*>(param);
+ context->handle_table = handle_table;
+ context->objects_count = objects_count;
+ context->hdc = hdc;
+ Record record_instance(record);
+ bool success = record_instance.SafePlayback(context);
+ DCHECK(success);
+ return 1;
+}
+
+Emf::EnumerationContext::EnumerationContext() {
+ memset(this, 0, sizeof(*this));
+}
+
+Emf::Record::Record(const ENHMETARECORD* record)
+ : record_(record) {
+ DCHECK(record_);
+}
+
+bool Emf::Record::Play(Emf::EnumerationContext* context) const {
+ return 0 != PlayEnhMetaFileRecord(context->hdc,
+ context->handle_table,
+ record_,
+ context->objects_count);
+}
+
+bool Emf::Record::SafePlayback(Emf::EnumerationContext* context) const {
+ // For EMF field description, see [MS-EMF] Enhanced Metafile Format
+ // Specification.
+ //
+ // This is the second major EMF breakage I get; the first one being
+ // SetDCBrushColor/SetDCPenColor/DC_PEN/DC_BRUSH being silently ignored.
+ //
+ // This function is the guts of the fix for bug 1186598. Some printer drivers
+ // somehow choke on certain EMF records, but calling the corresponding
+ // function directly on the printer HDC is fine. Still, playing the EMF record
+ // fails. Go figure.
+ //
+ // The main issue is that SetLayout is totally unsupported on these printers
+ // (HP 4500/4700). I used to call SetLayout and I stopped. I found out this is
+ // not sufficient because GDI32!PlayEnhMetaFile internally calls SetLayout(!)
+ // Damn.
+ //
+ // So I resorted to manually parse the EMF records and play them one by one.
+ // The issue with this method compared to using PlayEnhMetaFile to play back
+ // an EMF buffer is that the later silently fixes the matrix to take in
+ // account the matrix currently loaded at the time of the call.
+ // The matrix magic is done transparently when using PlayEnhMetaFile but since
+ // I'm processing one field at a time, I need to do the fixup myself. Note
+ // that PlayEnhMetaFileRecord doesn't fix the matrix correctly even when
+ // called inside an EnumEnhMetaFile loop. Go figure (bis).
+ //
+ // So when I see a EMR_SETWORLDTRANSFORM and EMR_MODIFYWORLDTRANSFORM, I need
+ // to fix the matrix according to the matrix previously loaded before playing
+ // back the buffer. Otherwise, the previously loaded matrix would be ignored
+ // and the EMF buffer would always be played back at its native resolution.
+ // Duh.
+ //
+ // I also use this opportunity to skip over eventual EMR_SETLAYOUT record that
+ // could remain.
+ //
+ // Another tweak we make is for JPEGs/PNGs in calls to StretchDIBits.
+ // (Our Pepper plugin code uses a JPEG). If the printer does not support
+ // JPEGs/PNGs natively we decompress the JPEG/PNG and then set it to the
+ // device.
+ // TODO(sanjeevr): We should also add JPEG/PNG support for SetSIBitsToDevice
+ //
+ // We also process any custom EMR_GDICOMMENT records which are our
+ // placeholders for StartPage and EndPage.
+ // Note: I should probably care about view ports and clipping, eventually.
+ bool res = false;
+ const XFORM* base_matrix = context->base_matrix;
+ switch (record()->iType) {
+ case EMR_STRETCHDIBITS: {
+ const EMRSTRETCHDIBITS * sdib_record =
+ reinterpret_cast<const EMRSTRETCHDIBITS*>(record());
+ const BYTE* record_start = reinterpret_cast<const BYTE *>(record());
+ const BITMAPINFOHEADER *bmih =
+ reinterpret_cast<const BITMAPINFOHEADER *>(record_start +
+ sdib_record->offBmiSrc);
+ const BYTE* bits = record_start + sdib_record->offBitsSrc;
+ bool play_normally = true;
+ res = false;
+ HDC hdc = context->hdc;
+ scoped_ptr<SkBitmap> bitmap;
+ if (bmih->biCompression == BI_JPEG) {
+ if (!DIBFormatNativelySupported(hdc, CHECKJPEGFORMAT, bits,
+ bmih->biSizeImage)) {
+ play_normally = false;
+ bitmap.reset(gfx::JPEGCodec::Decode(bits, bmih->biSizeImage));
+ }
+ } else if (bmih->biCompression == BI_PNG) {
+ if (!DIBFormatNativelySupported(hdc, CHECKPNGFORMAT, bits,
+ bmih->biSizeImage)) {
+ play_normally = false;
+ bitmap.reset(new SkBitmap());
+ gfx::PNGCodec::Decode(bits, bmih->biSizeImage, bitmap.get());
+ }
+ }
+ if (!play_normally) {
+ DCHECK(bitmap.get());
+ if (bitmap.get()) {
+ SkAutoLockPixels lock(*bitmap.get());
+ DCHECK_EQ(bitmap->config(), SkBitmap::kARGB_8888_Config);
+ const uint32_t* pixels =
+ static_cast<const uint32_t*>(bitmap->getPixels());
+ if (pixels == NULL) {
+ NOTREACHED();
+ return false;
+ }
+ BITMAPINFOHEADER bmi = {0};
+ gfx::CreateBitmapHeader(bitmap->width(), bitmap->height(), &bmi);
+ res = (0 != StretchDIBits(hdc, sdib_record->xDest, sdib_record->yDest,
+ sdib_record->cxDest,
+ sdib_record->cyDest, sdib_record->xSrc,
+ sdib_record->ySrc,
+ sdib_record->cxSrc, sdib_record->cySrc,
+ pixels,
+ reinterpret_cast<const BITMAPINFO *>(&bmi),
+ sdib_record->iUsageSrc,
+ sdib_record->dwRop));
+ }
+ } else {
+ res = Play(context);
+ }
+ break;
+ }
+ case EMR_SETWORLDTRANSFORM: {
+ DCHECK_EQ(record()->nSize, sizeof(DWORD) * 2 + sizeof(XFORM));
+ const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm);
+ HDC hdc = context->hdc;
+ if (base_matrix) {
+ res = 0 != SetWorldTransform(hdc, base_matrix) &&
+ ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
+ } else {
+ res = 0 != SetWorldTransform(hdc, xform);
+ }
+ break;
+ }
+ case EMR_MODIFYWORLDTRANSFORM: {
+ DCHECK_EQ(record()->nSize,
+ sizeof(DWORD) * 2 + sizeof(XFORM) + sizeof(DWORD));
+ const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm);
+ const DWORD* option = reinterpret_cast<const DWORD*>(xform + 1);
+ HDC hdc = context->hdc;
+ switch (*option) {
+ case MWT_IDENTITY:
+ if (base_matrix) {
+ res = 0 != SetWorldTransform(hdc, base_matrix);
+ } else {
+ res = 0 != ModifyWorldTransform(hdc, xform, MWT_IDENTITY);
+ }
+ break;
+ case MWT_LEFTMULTIPLY:
+ case MWT_RIGHTMULTIPLY:
+ res = 0 != ModifyWorldTransform(hdc, xform, *option);
+ break;
+ case 4: // MWT_SET
+ if (base_matrix) {
+ res = 0 != SetWorldTransform(hdc, base_matrix) &&
+ ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
+ } else {
+ res = 0 != SetWorldTransform(hdc, xform);
+ }
+ break;
+ default:
+ res = false;
+ break;
+ }
+ break;
+ }
+ case EMR_SETLAYOUT:
+ // Ignore it.
+ res = true;
+ break;
+ case EMR_GDICOMMENT: {
+ const EMRGDICOMMENT* comment_record =
+ reinterpret_cast<const EMRGDICOMMENT*>(record());
+ if (comment_record->cbData == sizeof(PageBreakRecord)) {
+ const PageBreakRecord* page_break_record =
+ reinterpret_cast<const PageBreakRecord*>(comment_record->Data);
+ if (page_break_record && page_break_record->IsValid()) {
+ if (page_break_record->type == PageBreakRecord::START_PAGE) {
+ res = !!::StartPage(context->hdc);
+ DCHECK_EQ(0, context->dc_on_page_start);
+ context->dc_on_page_start = ::SaveDC(context->hdc);
+ } else if (page_break_record->type == PageBreakRecord::END_PAGE) {
+ DCHECK_NE(0, context->dc_on_page_start);
+ ::RestoreDC(context->hdc, context->dc_on_page_start);
+ context->dc_on_page_start = 0;
+ res = !!::EndPage(context->hdc);
+ } else {
+ res = false;
+ NOTREACHED();
+ }
+ } else {
+ res = Play(context);
+ }
+ } else {
+ res = true;
+ }
+ break;
+ }
+ default: {
+ res = Play(context);
+ break;
+ }
+ }
+ return res;
+}
+
+SkDevice* Emf::StartPageForVectorCanvas(
+ const gfx::Size& page_size, const gfx::Rect& content_area,
+ const float& scale_factor) {
+ if (!StartPage(page_size, content_area, scale_factor))
+ return NULL;
+
+ return skia::VectorPlatformDeviceEmf::CreateDevice(page_size.width(),
+ page_size.height(),
+ true, hdc_);
+}
+
+bool Emf::StartPage(const gfx::Size& /*page_size*/,
+ const gfx::Rect& /*content_area*/,
+ const float& /*scale_factor*/) {
+ DCHECK(hdc_);
+ if (!hdc_)
+ return false;
+ page_count_++;
+ PageBreakRecord record(PageBreakRecord::START_PAGE);
+ return !!GdiComment(hdc_, sizeof(record),
+ reinterpret_cast<const BYTE *>(&record));
+}
+
+bool Emf::FinishPage() {
+ DCHECK(hdc_);
+ if (!hdc_)
+ return false;
+ PageBreakRecord record(PageBreakRecord::END_PAGE);
+ return !!GdiComment(hdc_, sizeof(record),
+ reinterpret_cast<const BYTE *>(&record));
+}
+
+Emf::Enumerator::Enumerator(const Emf& emf, HDC context, const RECT* rect) {
+ items_.clear();
+ if (!EnumEnhMetaFile(context,
+ emf.emf(),
+ &Emf::Enumerator::EnhMetaFileProc,
+ reinterpret_cast<void*>(this),
+ rect)) {
+ NOTREACHED();
+ items_.clear();
+ }
+ DCHECK_EQ(context_.hdc, context);
+}
+
+Emf::Enumerator::const_iterator Emf::Enumerator::begin() const {
+ return items_.begin();
+}
+
+Emf::Enumerator::const_iterator Emf::Enumerator::end() const {
+ return items_.end();
+}
+
+int CALLBACK Emf::Enumerator::EnhMetaFileProc(HDC hdc,
+ HANDLETABLE* handle_table,
+ const ENHMETARECORD* record,
+ int objects_count,
+ LPARAM param) {
+ Enumerator& emf = *reinterpret_cast<Enumerator*>(param);
+ if (!emf.context_.handle_table) {
+ DCHECK(!emf.context_.handle_table);
+ DCHECK(!emf.context_.objects_count);
+ emf.context_.handle_table = handle_table;
+ emf.context_.objects_count = objects_count;
+ emf.context_.hdc = hdc;
+ } else {
+ DCHECK_EQ(emf.context_.handle_table, handle_table);
+ DCHECK_EQ(emf.context_.objects_count, objects_count);
+ DCHECK_EQ(emf.context_.hdc, hdc);
+ }
+ emf.items_.push_back(Record(record));
+ return 1;
+}
+
+bool Emf::IsAlphaBlendUsed() const {
+ bool result = false;
+ ::EnumEnhMetaFile(NULL,
+ emf(),
+ &IsAlphaBlendUsedEnumProc,
+ &result,
+ NULL);
+ return result;
+}
+
+Emf* Emf::RasterizeMetafile(int raster_area_in_pixels) const {
+ gfx::Rect page_bounds = GetPageBounds(1);
+ gfx::Size page_size(page_bounds.size());
+ if (page_size.GetArea() <= 0) {
+ NOTREACHED() << "Metafile is empty";
+ page_bounds = gfx::Rect(1, 1);
+ }
+
+ float scale = sqrt(float(raster_area_in_pixels) / page_size.GetArea());
+ page_size.set_width(std::max<int>(1, page_size.width() * scale));
+ page_size.set_height(std::max<int>(1, page_size.height() * scale));
+
+
+ RasterBitmap bitmap(page_size);
+
+ gfx::Rect bitmap_rect(page_size);
+ RECT rect = bitmap_rect.ToRECT();
+ Playback(bitmap.context(), &rect);
+
+ scoped_ptr<Emf> result(new Emf);
+ result->Init();
+ HDC hdc = result->context();
+ DCHECK(hdc);
+ skia::InitializeDC(hdc);
+
+ // Params are ignored.
+ result->StartPage(page_bounds.size(), page_bounds, 1);
+
+ ::ModifyWorldTransform(hdc, NULL, MWT_IDENTITY);
+ XFORM xform = {
+ float(page_bounds.width()) / bitmap_rect.width(), 0,
+ 0, float(page_bounds.height()) / bitmap_rect.height(),
+ page_bounds.x(),
+ page_bounds.y(),
+ };
+ ::SetWorldTransform(hdc, &xform);
+ ::BitBlt(hdc, 0, 0, bitmap_rect.width(), bitmap_rect.height(),
+ bitmap.context(), bitmap_rect.x(), bitmap_rect.y(), SRCCOPY);
+
+ result->FinishPage();
+ result->FinishDocument();
+
+ return result.release();
+}
+
+Emf* Emf::RasterizeAlphaBlend() const {
+ gfx::Rect page_bounds = GetPageBounds(1);
+ if (page_bounds.size().GetArea() <= 0) {
+ NOTREACHED() << "Metafile is empty";
+ page_bounds = gfx::Rect(1, 1);
+ }
+
+ RasterBitmap bitmap(page_bounds.size());
+
+ // Map metafile page_bounds.x(), page_bounds.y() to bitmap 0, 0.
+ XFORM xform = { 1, 0, 0, 1, -page_bounds.x(), -page_bounds.y()};
+ ::SetWorldTransform(bitmap.context(), &xform);
+
+ scoped_ptr<Emf> result(new Emf);
+ result->Init();
+ HDC hdc = result->context();
+ DCHECK(hdc);
+ skia::InitializeDC(hdc);
+
+ HDC bitmap_dc = bitmap.context();
+ RECT rect = page_bounds.ToRECT();
+ ::EnumEnhMetaFile(hdc, emf(), &RasterizeAlphaBlendProc, &bitmap_dc, &rect);
+
+ result->FinishDocument();
+
+ return result.release();
+}
+
+
+} // namespace printing
diff --git a/chromium/printing/emf_win.h b/chromium/printing/emf_win.h
new file mode 100644
index 00000000000..3516f4efb7d
--- /dev/null
+++ b/chromium/printing/emf_win.h
@@ -0,0 +1,207 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_EMF_WIN_H_
+#define PRINTING_EMF_WIN_H_
+
+#include <windows.h>
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "printing/metafile.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace gfx {
+class Rect;
+class Size;
+}
+
+namespace printing {
+
+// http://msdn2.microsoft.com/en-us/library/ms535522.aspx
+// Windows 2000/XP: When a page in a spooled file exceeds approximately 350
+// MB, it can fail to print and not send an error message.
+const size_t kMetafileMaxSize = 350*1024*1024;
+
+// Simple wrapper class that manage an EMF data stream and its virtual HDC.
+class PRINTING_EXPORT Emf : public Metafile {
+ public:
+ class Record;
+ class Enumerator;
+ struct EnumerationContext;
+
+ // Generates a virtual HDC that will record every GDI commands and compile
+ // it in a EMF data stream.
+ Emf();
+ virtual ~Emf();
+
+ // Generates a new metafile that will record every GDI command, and will
+ // be saved to |metafile_path|.
+ virtual bool InitToFile(const base::FilePath& metafile_path);
+
+ // Initializes the Emf with the data in |metafile_path|.
+ virtual bool InitFromFile(const base::FilePath& metafile_path);
+
+ // Metafile methods.
+ virtual bool Init() OVERRIDE;
+ virtual bool InitFromData(const void* src_buffer,
+ uint32 src_buffer_size) OVERRIDE;
+
+ virtual SkDevice* StartPageForVectorCanvas(
+ const gfx::Size& page_size, const gfx::Rect& content_area,
+ const float& scale_factor) OVERRIDE;
+ // Inserts a custom GDICOMMENT records indicating StartPage/EndPage calls
+ // (since StartPage and EndPage do not work in a metafile DC). Only valid
+ // when hdc_ is non-NULL. |page_size|, |content_area|, and |scale_factor| are
+ // ignored.
+ virtual bool StartPage(const gfx::Size& page_size,
+ const gfx::Rect& content_area,
+ const float& scale_factor) OVERRIDE;
+ virtual bool FinishPage() OVERRIDE;
+ virtual bool FinishDocument() OVERRIDE;
+
+ virtual uint32 GetDataSize() const OVERRIDE;
+ virtual bool GetData(void* buffer, uint32 size) const OVERRIDE;
+
+ // Saves the EMF data to a file as-is. It is recommended to use the .emf file
+ // extension but it is not enforced. This function synchronously writes to the
+ // file. For testing only.
+ virtual bool SaveTo(const base::FilePath& file_path) const OVERRIDE;
+
+ // Should be passed to Playback to keep the exact same size.
+ virtual gfx::Rect GetPageBounds(unsigned int page_number) const OVERRIDE;
+
+ virtual unsigned int GetPageCount() const OVERRIDE {
+ return page_count_;
+ }
+
+ virtual HDC context() const OVERRIDE {
+ return hdc_;
+ }
+
+ virtual bool Playback(HDC hdc, const RECT* rect) const OVERRIDE;
+ virtual bool SafePlayback(HDC hdc) const OVERRIDE;
+
+ virtual HENHMETAFILE emf() const OVERRIDE {
+ return emf_;
+ }
+
+ // Returns true if metafile contains alpha blend.
+ bool IsAlphaBlendUsed() const;
+
+ // Returns new metafile with only bitmap created by playback of the current
+ // metafile. Returns NULL if fails.
+ Emf* RasterizeMetafile(int raster_area_in_pixels) const;
+
+ // Returns new metafile where AlphaBlend replaced by bitmaps. Returns NULL
+ // if fails.
+ Emf* RasterizeAlphaBlend() const;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(EmfTest, DC);
+ FRIEND_TEST_ALL_PREFIXES(EmfPrintingTest, PageBreak);
+ FRIEND_TEST_ALL_PREFIXES(EmfTest, FileBackedEmf);
+
+ // Retrieves the underlying data stream. It is a helper function.
+ bool GetDataAsVector(std::vector<uint8>* buffer) const;
+
+ // Playbacks safely one EMF record.
+ static int CALLBACK SafePlaybackProc(HDC hdc,
+ HANDLETABLE* handle_table,
+ const ENHMETARECORD* record,
+ int objects_count,
+ LPARAM param);
+
+ // Compiled EMF data handle.
+ HENHMETAFILE emf_;
+
+ // Valid when generating EMF data through a virtual HDC.
+ HDC hdc_;
+
+ int page_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(Emf);
+};
+
+struct Emf::EnumerationContext {
+ EnumerationContext();
+
+ HANDLETABLE* handle_table;
+ int objects_count;
+ HDC hdc;
+ const XFORM* base_matrix;
+ int dc_on_page_start;
+};
+
+// One EMF record. It keeps pointers to the EMF buffer held by Emf::emf_.
+// The entries become invalid once Emf::CloseEmf() is called.
+class PRINTING_EXPORT Emf::Record {
+ public:
+ // Plays the record.
+ bool Play(EnumerationContext* context) const;
+
+ // Plays the record working around quirks with SetLayout,
+ // SetWorldTransform and ModifyWorldTransform. See implementation for details.
+ bool SafePlayback(EnumerationContext* context) const;
+
+ // Access the underlying EMF record.
+ const ENHMETARECORD* record() const { return record_; }
+
+ protected:
+ explicit Record(const ENHMETARECORD* record);
+
+ private:
+ friend class Emf;
+ friend class Enumerator;
+ const ENHMETARECORD* record_;
+};
+
+// Retrieves individual records out of a Emf buffer. The main use is to skip
+// over records that are unsupported on a specific printer or to play back
+// only a part of an EMF buffer.
+class PRINTING_EXPORT Emf::Enumerator {
+ public:
+ // Iterator type used for iterating the records.
+ typedef std::vector<Record>::const_iterator const_iterator;
+
+ // Enumerates the records at construction time. |hdc| and |rect| are
+ // both optional at the same time or must both be valid.
+ // Warning: |emf| must be kept valid for the time this object is alive.
+ Enumerator(const Emf& emf, HDC hdc, const RECT* rect);
+
+ // Retrieves the first Record.
+ const_iterator begin() const;
+
+ // Retrieves the end of the array.
+ const_iterator end() const;
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(EmfPrintingTest, Enumerate);
+
+ // Processes one EMF record and saves it in the items_ array.
+ static int CALLBACK EnhMetaFileProc(HDC hdc,
+ HANDLETABLE* handle_table,
+ const ENHMETARECORD* record,
+ int objects_count,
+ LPARAM param);
+
+ // The collection of every EMF records in the currently loaded EMF buffer.
+ // Initialized by Enumerate(). It keeps pointers to the EMF buffer held by
+ // Emf::emf_. The entries become invalid once Emf::CloseEmf() is called.
+ std::vector<Record> items_;
+
+ EnumerationContext context_;
+
+ DISALLOW_COPY_AND_ASSIGN(Enumerator);
+};
+
+} // namespace printing
+
+#endif // PRINTING_EMF_WIN_H_
diff --git a/chromium/printing/emf_win_unittest.cc b/chromium/printing/emf_win_unittest.cc
new file mode 100644
index 00000000000..0f5e6ca8cf9
--- /dev/null
+++ b/chromium/printing/emf_win_unittest.cc
@@ -0,0 +1,230 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/emf_win.h"
+
+// For quick access.
+#include <wingdi.h>
+#include <winspool.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/path_service.h"
+#include "base/win/scoped_hdc.h"
+#include "printing/printing_context.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/point.h"
+#include "ui/gfx/size.h"
+
+namespace {
+
+// This test is automatically disabled if no printer named "UnitTest Printer" is
+// available.
+class EmfPrintingTest : public testing::Test {
+ public:
+ typedef testing::Test Parent;
+ static bool IsTestCaseDisabled() {
+ // It is assumed this printer is a HP Color LaserJet 4550 PCL or 4700.
+ HDC hdc = CreateDC(L"WINSPOOL", L"UnitTest Printer", NULL, NULL);
+ if (!hdc)
+ return true;
+ DeleteDC(hdc);
+ return false;
+ }
+};
+
+const uint32 EMF_HEADER_SIZE = 128;
+
+} // namespace
+
+namespace printing {
+
+TEST(EmfTest, DC) {
+ // Simplest use case.
+ uint32 size;
+ std::vector<BYTE> data;
+ {
+ Emf emf;
+ EXPECT_TRUE(emf.Init());
+ EXPECT_TRUE(emf.context() != NULL);
+ // An empty EMF is invalid, so we put at least a rectangle in it.
+ ::Rectangle(emf.context(), 10, 10, 190, 190);
+ EXPECT_TRUE(emf.FinishDocument());
+ size = emf.GetDataSize();
+ EXPECT_GT(size, EMF_HEADER_SIZE);
+ EXPECT_TRUE(emf.GetDataAsVector(&data));
+ EXPECT_EQ(data.size(), size);
+ }
+
+ // Playback the data.
+ Emf emf;
+ EXPECT_TRUE(emf.InitFromData(&data.front(), size));
+ HDC hdc = CreateCompatibleDC(NULL);
+ EXPECT_TRUE(hdc);
+ RECT output_rect = {0, 0, 10, 10};
+ EXPECT_TRUE(emf.Playback(hdc, &output_rect));
+ EXPECT_TRUE(DeleteDC(hdc));
+}
+
+// Disabled if no "UnitTest printer" exist. Useful to reproduce bug 1186598.
+TEST_F(EmfPrintingTest, Enumerate) {
+ if (IsTestCaseDisabled())
+ return;
+
+ PrintSettings settings;
+
+ // My test case is a HP Color LaserJet 4550 PCL.
+ settings.set_device_name(L"UnitTest Printer");
+
+ // Initialize it.
+ scoped_ptr<PrintingContext> context(PrintingContext::Create(std::string()));
+ EXPECT_EQ(context->InitWithSettings(settings), PrintingContext::OK);
+
+ base::FilePath emf_file;
+ EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &emf_file));
+ emf_file = emf_file.Append(FILE_PATH_LITERAL("printing"))
+ .Append(FILE_PATH_LITERAL("test"))
+ .Append(FILE_PATH_LITERAL("data"))
+ .Append(FILE_PATH_LITERAL("test4.emf"));
+ // Load any EMF with an image.
+ Emf emf;
+ std::string emf_data;
+ file_util::ReadFileToString(emf_file, &emf_data);
+ ASSERT_TRUE(emf_data.size());
+ EXPECT_TRUE(emf.InitFromData(&emf_data[0], emf_data.size()));
+
+ // This will print to file. The reason is that when running inside a
+ // unit_test, PrintingContext automatically dumps its files to the
+ // current directory.
+ // TODO(maruel): Clean the .PRN file generated in current directory.
+ context->NewDocument(L"EmfTest.Enumerate");
+ context->NewPage();
+ // Process one at a time.
+ Emf::Enumerator emf_enum(emf, context->context(),
+ &emf.GetPageBounds(1).ToRECT());
+ for (Emf::Enumerator::const_iterator itr = emf_enum.begin();
+ itr != emf_enum.end();
+ ++itr) {
+ // To help debugging.
+ ptrdiff_t index = itr - emf_enum.begin();
+ // If you get this assert, you need to lookup iType in wingdi.h. It starts
+ // with EMR_HEADER.
+ EMR_HEADER;
+ EXPECT_TRUE(itr->SafePlayback(&emf_enum.context_)) <<
+ " index: " << index << " type: " << itr->record()->iType;
+ }
+ context->PageDone();
+ context->DocumentDone();
+}
+
+// Disabled if no "UnitTest printer" exists.
+TEST_F(EmfPrintingTest, PageBreak) {
+ base::win::ScopedCreateDC dc(
+ CreateDC(L"WINSPOOL", L"UnitTest Printer", NULL, NULL));
+ if (!dc.Get())
+ return;
+ uint32 size;
+ std::vector<BYTE> data;
+ {
+ Emf emf;
+ EXPECT_TRUE(emf.Init());
+ EXPECT_TRUE(emf.context() != NULL);
+ int pages = 3;
+ while (pages) {
+ EXPECT_TRUE(emf.StartPage(gfx::Size(), gfx::Rect(), 1));
+ ::Rectangle(emf.context(), 10, 10, 190, 190);
+ EXPECT_TRUE(emf.FinishPage());
+ --pages;
+ }
+ EXPECT_EQ(3U, emf.GetPageCount());
+ EXPECT_TRUE(emf.FinishDocument());
+ size = emf.GetDataSize();
+ EXPECT_TRUE(emf.GetDataAsVector(&data));
+ EXPECT_EQ(data.size(), size);
+ }
+
+ // Playback the data.
+ DOCINFO di = {0};
+ di.cbSize = sizeof(DOCINFO);
+ di.lpszDocName = L"Test Job";
+ int job_id = ::StartDoc(dc.Get(), &di);
+ Emf emf;
+ EXPECT_TRUE(emf.InitFromData(&data.front(), size));
+ EXPECT_TRUE(emf.SafePlayback(dc.Get()));
+ ::EndDoc(dc.Get());
+ // Since presumably the printer is not real, let us just delete the job from
+ // the queue.
+ HANDLE printer = NULL;
+ if (::OpenPrinter(L"UnitTest Printer", &printer, NULL)) {
+ ::SetJob(printer, job_id, 0, NULL, JOB_CONTROL_DELETE);
+ ClosePrinter(printer);
+ }
+}
+
+TEST(EmfTest, FileBackedEmf) {
+ // Simplest use case.
+ base::ScopedTempDir scratch_metafile_dir;
+ ASSERT_TRUE(scratch_metafile_dir.CreateUniqueTempDir());
+ base::FilePath metafile_path;
+ EXPECT_TRUE(file_util::CreateTemporaryFileInDir(scratch_metafile_dir.path(),
+ &metafile_path));
+ uint32 size;
+ std::vector<BYTE> data;
+ {
+ Emf emf;
+ EXPECT_TRUE(emf.InitToFile(metafile_path));
+ EXPECT_TRUE(emf.context() != NULL);
+ // An empty EMF is invalid, so we put at least a rectangle in it.
+ ::Rectangle(emf.context(), 10, 10, 190, 190);
+ EXPECT_TRUE(emf.FinishDocument());
+ size = emf.GetDataSize();
+ EXPECT_GT(size, EMF_HEADER_SIZE);
+ EXPECT_TRUE(emf.GetDataAsVector(&data));
+ EXPECT_EQ(data.size(), size);
+ }
+ int64 file_size = 0;
+ file_util::GetFileSize(metafile_path, &file_size);
+ EXPECT_EQ(size, file_size);
+
+ // Playback the data.
+ HDC hdc = CreateCompatibleDC(NULL);
+ EXPECT_TRUE(hdc);
+ Emf emf;
+ EXPECT_TRUE(emf.InitFromFile(metafile_path));
+ RECT output_rect = {0, 0, 10, 10};
+ EXPECT_TRUE(emf.Playback(hdc, &output_rect));
+ EXPECT_TRUE(DeleteDC(hdc));
+}
+
+TEST(EmfTest, RasterizeMetafile) {
+ Emf emf;
+ EXPECT_TRUE(emf.Init());
+ EXPECT_TRUE(emf.context() != NULL);
+ HBRUSH brush = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
+ for (int i = 0; i < 4; ++i) {
+ RECT rect = { 5 + i, 5 + i, 5 + i + 1, 5 + i + 2};
+ FillRect(emf.context(), &rect, brush);
+ }
+ EXPECT_TRUE(emf.FinishDocument());
+
+ scoped_ptr<Emf> raster(emf.RasterizeMetafile(1));
+ // Just 1px bitmap but should be stretched to the same bounds.
+ EXPECT_EQ(emf.GetPageBounds(1), raster->GetPageBounds(1));
+
+ raster.reset(emf.RasterizeMetafile(20));
+ EXPECT_EQ(emf.GetPageBounds(1), raster->GetPageBounds(1));
+
+ raster.reset(emf.RasterizeMetafile(16*1024*1024));
+ // Expected size about 64MB.
+ EXPECT_LE(abs(int(raster->GetDataSize()) - 64*1024*1024), 1024*1024);
+ // Bounds should still be the same.
+ EXPECT_EQ(emf.GetPageBounds(1), raster->GetPageBounds(1));
+}
+
+} // namespace printing
diff --git a/chromium/printing/image.cc b/chromium/printing/image.cc
new file mode 100644
index 00000000000..47ce089d302
--- /dev/null
+++ b/chromium/printing/image.cc
@@ -0,0 +1,160 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/image.h"
+
+#include <algorithm>
+
+#include "base/file_util.h"
+#include "base/md5.h"
+#include "base/safe_numerics.h"
+#include "base/strings/string_number_conversions.h"
+#include "printing/metafile.h"
+#include "printing/metafile_impl.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "ui/gfx/codec/png_codec.h"
+
+namespace printing {
+
+Image::Image(const base::FilePath& path)
+ : row_length_(0),
+ ignore_alpha_(true) {
+ std::string data;
+ file_util::ReadFileToString(path, &data);
+ bool success = false;
+ if (path.MatchesExtension(FILE_PATH_LITERAL(".png"))) {
+ success = LoadPng(data);
+ } else if (path.MatchesExtension(FILE_PATH_LITERAL(".emf"))) {
+ success = LoadMetafile(data);
+ } else {
+ DCHECK(false);
+ }
+ if (!success) {
+ size_.SetSize(0, 0);
+ row_length_ = 0;
+ data_.clear();
+ }
+}
+
+Image::Image(const Metafile& metafile)
+ : row_length_(0),
+ ignore_alpha_(true) {
+ LoadMetafile(metafile);
+}
+
+Image::Image(const Image& image)
+ : size_(image.size_),
+ row_length_(image.row_length_),
+ data_(image.data_),
+ ignore_alpha_(image.ignore_alpha_) {
+}
+
+Image::~Image() {}
+
+std::string Image::checksum() const {
+ base::MD5Digest digest;
+ base::MD5Sum(&data_[0], data_.size(), &digest);
+ return base::HexEncode(&digest, sizeof(digest));
+}
+
+bool Image::SaveToPng(const base::FilePath& filepath) const {
+ DCHECK(!data_.empty());
+ std::vector<unsigned char> compressed;
+ bool success = gfx::PNGCodec::Encode(&*data_.begin(),
+ gfx::PNGCodec::FORMAT_BGRA,
+ size_,
+ row_length_,
+ true,
+ std::vector<gfx::PNGCodec::Comment>(),
+ &compressed);
+ DCHECK(success && compressed.size());
+ if (success) {
+ int write_bytes = file_util::WriteFile(
+ filepath,
+ reinterpret_cast<char*>(&*compressed.begin()),
+ base::checked_numeric_cast<int>(compressed.size()));
+ success = (write_bytes == static_cast<int>(compressed.size()));
+ DCHECK(success);
+ }
+ return success;
+}
+
+double Image::PercentageDifferent(const Image& rhs) const {
+ if (size_.width() == 0 || size_.height() == 0 ||
+ rhs.size_.width() == 0 || rhs.size_.height() == 0)
+ return 100.;
+
+ int width = std::min(size_.width(), rhs.size_.width());
+ int height = std::min(size_.height(), rhs.size_.height());
+ // Compute pixels different in the overlap
+ int pixels_different = 0;
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ uint32 lhs_pixel = pixel_at(x, y);
+ uint32 rhs_pixel = rhs.pixel_at(x, y);
+ if (lhs_pixel != rhs_pixel)
+ ++pixels_different;
+ }
+
+ // Look for extra right lhs pixels. They should be white.
+ for (int x = width; x < size_.width(); ++x) {
+ uint32 lhs_pixel = pixel_at(x, y);
+ if (lhs_pixel != Color(SK_ColorWHITE))
+ ++pixels_different;
+ }
+
+ // Look for extra right rhs pixels. They should be white.
+ for (int x = width; x < rhs.size_.width(); ++x) {
+ uint32 rhs_pixel = rhs.pixel_at(x, y);
+ if (rhs_pixel != Color(SK_ColorWHITE))
+ ++pixels_different;
+ }
+ }
+
+ // Look for extra bottom lhs pixels. They should be white.
+ for (int y = height; y < size_.height(); ++y) {
+ for (int x = 0; x < size_.width(); ++x) {
+ uint32 lhs_pixel = pixel_at(x, y);
+ if (lhs_pixel != Color(SK_ColorWHITE))
+ ++pixels_different;
+ }
+ }
+
+ // Look for extra bottom rhs pixels. They should be white.
+ for (int y = height; y < rhs.size_.height(); ++y) {
+ for (int x = 0; x < rhs.size_.width(); ++x) {
+ uint32 rhs_pixel = rhs.pixel_at(x, y);
+ if (rhs_pixel != Color(SK_ColorWHITE))
+ ++pixels_different;
+ }
+ }
+
+ // Like the WebKit ImageDiff tool, we define percentage different in terms
+ // of the size of the 'actual' bitmap.
+ double total_pixels = static_cast<double>(size_.width()) *
+ static_cast<double>(height);
+ return static_cast<double>(pixels_different) / total_pixels * 100.;
+}
+
+bool Image::LoadPng(const std::string& compressed) {
+ int w;
+ int h;
+ bool success = gfx::PNGCodec::Decode(
+ reinterpret_cast<const unsigned char*>(compressed.c_str()),
+ compressed.size(), gfx::PNGCodec::FORMAT_BGRA, &data_, &w, &h);
+ size_.SetSize(w, h);
+ row_length_ = size_.width() * sizeof(uint32);
+ return success;
+}
+
+bool Image::LoadMetafile(const std::string& data) {
+ DCHECK(!data.empty());
+ NativeMetafile metafile;
+ if (!metafile.InitFromData(data.data(),
+ base::checked_numeric_cast<uint32>(data.size())))
+ return false;
+ return LoadMetafile(metafile);
+}
+
+} // namespace printing
diff --git a/chromium/printing/image.h b/chromium/printing/image.h
new file mode 100644
index 00000000000..b40657a3782
--- /dev/null
+++ b/chromium/printing/image.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_IMAGE_H_
+#define PRINTING_IMAGE_H_
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "printing/printing_export.h"
+#include "ui/gfx/size.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace printing {
+
+class Metafile;
+
+// Lightweight raw-bitmap management. The image, once initialized, is immutable.
+// The main purpose is testing image contents.
+class PRINTING_EXPORT Image {
+ public:
+ // Creates the image from the given file on disk. Uses extension to
+ // defer file type. PNG and EMF (on Windows) currently supported.
+ // If image loading fails size().IsEmpty() will be true.
+ explicit Image(const base::FilePath& path);
+
+ // Creates the image from the metafile. Deduces bounds based on bounds in
+ // metafile. If loading fails size().IsEmpty() will be true.
+ explicit Image(const Metafile& metafile);
+
+ // Copy constructor.
+ explicit Image(const Image& image);
+
+ ~Image();
+
+ const gfx::Size& size() const {
+ return size_;
+ }
+
+ // Return a checksum of the image (MD5 over the internal data structure).
+ std::string checksum() const;
+
+ // Save image as PNG.
+ bool SaveToPng(const base::FilePath& filepath) const;
+
+ // Returns % of pixels different
+ double PercentageDifferent(const Image& rhs) const;
+
+ // Returns the 0x0RGB or 0xARGB value of the pixel at the given location.
+ uint32 Color(uint32 color) const {
+ if (ignore_alpha_)
+ return color & 0xFFFFFF; // Strip out A.
+ else
+ return color;
+ }
+
+ uint32 pixel_at(int x, int y) const {
+ DCHECK(x >= 0 && x < size_.width());
+ DCHECK(y >= 0 && y < size_.height());
+ const uint32* data = reinterpret_cast<const uint32*>(&*data_.begin());
+ const uint32* data_row = data + y * row_length_ / sizeof(uint32);
+ return Color(data_row[x]);
+ }
+
+ private:
+ // Construct from metafile. This is kept internal since it's ambiguous what
+ // kind of data is used (png, bmp, metafile etc).
+ Image(const void* data, size_t size);
+
+ bool LoadPng(const std::string& compressed);
+
+ bool LoadMetafile(const std::string& data);
+
+ bool LoadMetafile(const Metafile& metafile);
+
+ // Pixel dimensions of the image.
+ gfx::Size size_;
+
+ // Length of a line in bytes.
+ int row_length_;
+
+ // Actual bitmap data in arrays of RGBAs (so when loaded as uint32, it's
+ // 0xABGR).
+ std::vector<unsigned char> data_;
+
+ // Flag to signal if the comparison functions should ignore the alpha channel.
+ const bool ignore_alpha_; // Currently always true.
+
+ // Prevent operator= (this function has no implementation)
+ Image& operator=(const Image& image);
+};
+
+} // namespace printing
+
+#endif // PRINTING_IMAGE_H_
diff --git a/chromium/printing/image_linux.cc b/chromium/printing/image_linux.cc
new file mode 100644
index 00000000000..3968cfd993f
--- /dev/null
+++ b/chromium/printing/image_linux.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/image.h"
+
+#include "base/logging.h"
+
+namespace printing {
+
+bool Image::LoadMetafile(const Metafile& metafile) {
+ return false;
+}
+
+} // namespace printing
diff --git a/chromium/printing/image_mac.cc b/chromium/printing/image_mac.cc
new file mode 100644
index 00000000000..2ec9ef60048
--- /dev/null
+++ b/chromium/printing/image_mac.cc
@@ -0,0 +1,48 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/image.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+
+#include "base/mac/scoped_cftyperef.h"
+#include "printing/metafile.h"
+#include "ui/gfx/rect.h"
+
+namespace printing {
+
+bool Image::LoadMetafile(const Metafile& metafile) {
+ // The printing system uses single-page metafiles (page indexes are 1-based).
+ const unsigned int page_number = 1;
+ gfx::Rect rect(metafile.GetPageBounds(page_number));
+ if (rect.width() < 1 || rect.height() < 1)
+ return false;
+
+ size_ = rect.size();
+ row_length_ = size_.width() * sizeof(uint32);
+ size_t bytes = row_length_ * size_.height();
+ DCHECK(bytes);
+
+ data_.resize(bytes);
+ base::ScopedCFTypeRef<CGColorSpaceRef> color_space(
+ CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));
+ base::ScopedCFTypeRef<CGContextRef> bitmap_context(
+ CGBitmapContextCreate(&*data_.begin(),
+ size_.width(),
+ size_.height(),
+ 8,
+ row_length_,
+ color_space,
+ kCGImageAlphaPremultipliedLast));
+ DCHECK(bitmap_context.get());
+
+ struct Metafile::MacRenderPageParams params;
+ params.shrink_to_fit = true;
+ metafile.RenderPage(page_number, bitmap_context,
+ CGRectMake(0, 0, size_.width(), size_.height()), params);
+
+ return true;
+}
+
+} // namespace printing
diff --git a/chromium/printing/image_win.cc b/chromium/printing/image_win.cc
new file mode 100644
index 00000000000..fdf99c8bf6b
--- /dev/null
+++ b/chromium/printing/image_win.cc
@@ -0,0 +1,89 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/image.h"
+
+#include "base/win/scoped_gdi_object.h"
+#include "base/win/scoped_hdc.h"
+#include "base/win/scoped_select_object.h"
+#include "printing/metafile.h"
+#include "skia/ext/platform_device.h"
+#include "ui/gfx/gdi_util.h" // EMF support
+#include "ui/gfx/rect.h"
+
+
+namespace {
+
+// A simple class which temporarily overrides system settings.
+// The bitmap image rendered via the PlayEnhMetaFile() function depends on
+// some system settings.
+// As a workaround for such dependency, this class saves the system settings
+// and changes them. This class also restore the saved settings in its
+// destructor.
+class DisableFontSmoothing {
+ public:
+ explicit DisableFontSmoothing() : enable_again_(false) {
+ BOOL enabled;
+ if (SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &enabled, 0) &&
+ enabled) {
+ if (SystemParametersInfo(SPI_SETFONTSMOOTHING, FALSE, NULL, 0))
+ enable_again_ = true;
+ }
+ }
+
+ ~DisableFontSmoothing() {
+ if (enable_again_) {
+ BOOL result = SystemParametersInfo(SPI_SETFONTSMOOTHING, TRUE, NULL, 0);
+ DCHECK(result);
+ }
+ }
+
+ private:
+ bool enable_again_;
+
+ DISALLOW_COPY_AND_ASSIGN(DisableFontSmoothing);
+};
+
+} // namespace
+
+namespace printing {
+
+bool Image::LoadMetafile(const Metafile& metafile) {
+ gfx::Rect rect(metafile.GetPageBounds(1));
+ DisableFontSmoothing disable_in_this_scope;
+
+ // Create a temporary HDC and bitmap to retrieve the rendered data.
+ base::win::ScopedCreateDC hdc(::CreateCompatibleDC(NULL));
+ BITMAPV4HEADER hdr;
+ DCHECK_EQ(rect.x(), 0);
+ DCHECK_EQ(rect.y(), 0);
+ DCHECK_GE(rect.width(), 0); // Metafile could be empty.
+ DCHECK_GE(rect.height(), 0);
+
+ if (rect.width() < 1 || rect.height() < 1)
+ return false;
+
+ size_ = rect.size();
+ gfx::CreateBitmapV4Header(rect.width(), rect.height(), &hdr);
+ unsigned char* bits = NULL;
+ base::win::ScopedBitmap bitmap(
+ ::CreateDIBSection(hdc, reinterpret_cast<BITMAPINFO*>(&hdr), 0,
+ reinterpret_cast<void**>(&bits), NULL, 0));
+ DCHECK(bitmap);
+ base::win::ScopedSelectObject select_object(hdc, bitmap);
+
+ skia::InitializeDC(hdc);
+
+ bool success = metafile.Playback(hdc, NULL);
+
+ row_length_ = size_.width() * sizeof(uint32);
+ size_t bytes = row_length_ * size_.height();
+ DCHECK(bytes);
+
+ data_.assign(bits, bits + bytes);
+
+ return success;
+}
+
+} // namespace printing
diff --git a/chromium/printing/metafile.h b/chromium/printing/metafile.h
new file mode 100644
index 00000000000..ca0901b2b65
--- /dev/null
+++ b/chromium/printing/metafile.h
@@ -0,0 +1,168 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_METAFILE_H_
+#define PRINTING_METAFILE_H_
+
+#include "base/basictypes.h"
+#include "build/build_config.h"
+#include "printing/printing_export.h"
+#include "ui/gfx/native_widget_types.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_MACOSX)
+#include <ApplicationServices/ApplicationServices.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include "base/mac/scoped_cftyperef.h"
+#endif
+
+namespace base {
+class FilePath;
+}
+
+namespace gfx {
+class Rect;
+class Size;
+}
+
+class SkDevice;
+
+#if defined(OS_CHROMEOS)
+namespace base {
+struct FileDescriptor;
+}
+#endif
+
+namespace printing {
+
+// This class creates a graphics context that renders into a data stream
+// (usually PDF or EMF).
+class PRINTING_EXPORT Metafile {
+ public:
+#if defined(OS_MACOSX)
+ // |shrink_to_fit| specifies whether the output should be shrunk to fit a
+ // destination page if the source PDF is bigger than the destination page in
+ // any dimension. If this is false, parts of the source PDF page that lie
+ // outside the bounds will be clipped.
+ // |stretch_to_fit| specifies whether the output should be stretched to fit
+ // the destination page if the source page size is smaller in all dimensions.
+ // |center_horizontally| specifies whether the output (after any scaling is
+ // done) should be centered horizontally within the destination page.
+ // |center_vertically| specifies whether the output (after any scaling is
+ // done) should be centered vertically within the destination page.
+ // Note that all scaling preserves the original aspect ratio of the page.
+ // |autorotate| specifies whether the source PDF should be autorotated to fit
+ // on the destination page.
+ struct MacRenderPageParams {
+ MacRenderPageParams()
+ : shrink_to_fit(false),
+ stretch_to_fit(false),
+ center_horizontally(false),
+ center_vertically(false),
+ autorotate(false) {
+ }
+
+ bool shrink_to_fit;
+ bool stretch_to_fit;
+ bool center_horizontally;
+ bool center_vertically;
+ bool autorotate;
+ };
+#endif // defined(OS_MACOSX)
+
+ virtual ~Metafile() {}
+
+ // Initializes a fresh new metafile for rendering. Returns false on failure.
+ // Note: It should only be called from within the renderer process to allocate
+ // rendering resources.
+ virtual bool Init() = 0;
+
+ // Initializes the metafile with the data in |src_buffer|. Returns true
+ // on success.
+ // Note: It should only be called from within the browser process.
+ virtual bool InitFromData(const void* src_buffer, uint32 src_buffer_size) = 0;
+
+ // This method calls StartPage and then returns an appropriate
+ // VectorPlatformDevice implementation bound to the context created by
+ // StartPage or NULL on error.
+ virtual SkDevice* StartPageForVectorCanvas(
+ const gfx::Size& page_size,
+ const gfx::Rect& content_area,
+ const float& scale_factor) = 0;
+
+ // Prepares a context for rendering a new page with the given |page_size|,
+ // |content_area| and a |scale_factor| to use for the drawing. The units are
+ // in points (=1/72 in). Returns true on success.
+ virtual bool StartPage(const gfx::Size& page_size,
+ const gfx::Rect& content_area,
+ const float& scale_factor) = 0;
+
+ // Closes the current page and destroys the context used in rendering that
+ // page. The results of current page will be appended into the underlying
+ // data stream. Returns true on success.
+ virtual bool FinishPage() = 0;
+
+ // Closes the metafile. No further rendering is allowed (the current page
+ // is implicitly closed).
+ virtual bool FinishDocument() = 0;
+
+ // Returns the size of the underlying data stream. Only valid after Close()
+ // has been called.
+ virtual uint32 GetDataSize() const = 0;
+
+ // Copies the first |dst_buffer_size| bytes of the underlying data stream into
+ // |dst_buffer|. This function should ONLY be called after Close() is invoked.
+ // Returns true if the copy succeeds.
+ virtual bool GetData(void* dst_buffer, uint32 dst_buffer_size) const = 0;
+
+ // Saves the underlying data to the given file. This function should ONLY be
+ // called after the metafile is closed. Returns true if writing succeeded.
+ virtual bool SaveTo(const base::FilePath& file_path) const = 0;
+
+ // Returns the bounds of the given page. Pages use a 1-based index.
+ virtual gfx::Rect GetPageBounds(unsigned int page_number) const = 0;
+ virtual unsigned int GetPageCount() const = 0;
+
+ // Get the context for rendering to the PDF.
+ virtual gfx::NativeDrawingContext context() const = 0;
+
+#if defined(OS_WIN)
+ // "Plays" the EMF buffer in a HDC. It is the same effect as calling the
+ // original GDI function that were called when recording the EMF. |rect| is in
+ // "logical units" and is optional. If |rect| is NULL, the natural EMF bounds
+ // are used.
+ // Note: Windows has been known to have stack buffer overflow in its GDI
+ // functions, whether used directly or indirectly through precompiled EMF
+ // data. We have to accept the risk here. Since it is used only for printing,
+ // it requires user intervention.
+ virtual bool Playback(gfx::NativeDrawingContext hdc,
+ const RECT* rect) const = 0;
+
+ // The slow version of Playback(). It enumerates all the records and play them
+ // back in the HDC. The trick is that it skip over the records known to have
+ // issue with some printers. See Emf::Record::SafePlayback implementation for
+ // details.
+ virtual bool SafePlayback(gfx::NativeDrawingContext hdc) const = 0;
+
+ virtual HENHMETAFILE emf() const = 0;
+#elif defined(OS_MACOSX)
+ // Renders the given page into |rect| in the given context.
+ // Pages use a 1-based index. The rendering uses the arguments in
+ // |params| to determine scaling, translation, and rotation.
+ virtual bool RenderPage(unsigned int page_number,
+ gfx::NativeDrawingContext context,
+ const CGRect rect,
+ const MacRenderPageParams& params) const = 0;
+#elif defined(OS_CHROMEOS)
+ // Saves the underlying data to the file associated with fd. This function
+ // should ONLY be called after the metafile is closed.
+ // Returns true if writing succeeded.
+ virtual bool SaveToFD(const base::FileDescriptor& fd) const = 0;
+#endif // if defined(OS_CHROMEOS)
+};
+
+} // namespace printing
+
+#endif // PRINTING_METAFILE_H_
diff --git a/chromium/printing/metafile_impl.h b/chromium/printing/metafile_impl.h
new file mode 100644
index 00000000000..840a1cede68
--- /dev/null
+++ b/chromium/printing/metafile_impl.h
@@ -0,0 +1,26 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_METAFILE_IMPL_H_
+#define PRINTING_METAFILE_IMPL_H_
+
+#include "printing/pdf_metafile_skia.h"
+
+#if defined(OS_WIN)
+#include "printing/emf_win.h"
+#endif
+
+namespace printing {
+
+#if defined(OS_WIN)
+typedef Emf NativeMetafile;
+typedef PdfMetafileSkia PreviewMetafile;
+#elif defined(OS_POSIX)
+typedef PdfMetafileSkia NativeMetafile;
+typedef PdfMetafileSkia PreviewMetafile;
+#endif
+
+} // namespace printing
+
+#endif // PRINTING_METAFILE_IMPL_H_
diff --git a/chromium/printing/metafile_skia_wrapper.cc b/chromium/printing/metafile_skia_wrapper.cc
new file mode 100644
index 00000000000..2fddc004e12
--- /dev/null
+++ b/chromium/printing/metafile_skia_wrapper.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/metafile_skia_wrapper.h"
+#include "skia/ext/platform_device.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkMetaData.h"
+
+namespace printing {
+
+namespace {
+
+const char* kMetafileKey = "CrMetafile";
+const char* kCustomScaleKey = "CrCustomScale";
+
+} // namespace
+
+// static
+void MetafileSkiaWrapper::SetMetafileOnCanvas(const SkCanvas& canvas,
+ Metafile* metafile) {
+ skia::RefPtr<MetafileSkiaWrapper> wrapper;
+ if (metafile)
+ wrapper = skia::AdoptRef(new MetafileSkiaWrapper(metafile));
+
+ SkMetaData& meta = skia::getMetaData(canvas);
+ meta.setRefCnt(kMetafileKey, wrapper.get());
+}
+
+// static
+Metafile* MetafileSkiaWrapper::GetMetafileFromCanvas(const SkCanvas& canvas) {
+ SkMetaData& meta = skia::getMetaData(canvas);
+ SkRefCnt* value;
+ if (!meta.findRefCnt(kMetafileKey, &value) || !value)
+ return NULL;
+
+ return static_cast<MetafileSkiaWrapper*>(value)->metafile_;
+}
+
+// static
+void MetafileSkiaWrapper::SetCustomScaleOnCanvas(const SkCanvas& canvas,
+ double scale) {
+ SkMetaData& meta = skia::getMetaData(canvas);
+ meta.setScalar(kCustomScaleKey, SkFloatToScalar(scale));
+}
+
+// static
+bool MetafileSkiaWrapper::GetCustomScaleOnCanvas(const SkCanvas& canvas,
+ double* scale) {
+ SkMetaData& meta = skia::getMetaData(canvas);
+ SkScalar value;
+ if (!meta.findScalar(kCustomScaleKey, &value))
+ return false;
+
+ *scale = SkScalarToFloat(value);
+ return true;
+}
+
+MetafileSkiaWrapper::MetafileSkiaWrapper(Metafile* metafile)
+ : metafile_(metafile) {
+}
+
+} // namespace printing
diff --git a/chromium/printing/metafile_skia_wrapper.h b/chromium/printing/metafile_skia_wrapper.h
new file mode 100644
index 00000000000..42bbc5f9f69
--- /dev/null
+++ b/chromium/printing/metafile_skia_wrapper.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_METAFILE_SKIA_WRAPPER_H_
+#define PRINTING_METAFILE_SKIA_WRAPPER_H_
+
+#include "printing/printing_export.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+class SkCanvas;
+
+namespace printing {
+
+class Metafile;
+
+// A wrapper class with static methods to set and retrieve a Metafile
+// on an SkCanvas. The ownership of the metafile is not affected and it
+// is the caller's responsibility to ensure that the metafile remains valid
+// as long as the canvas.
+class PRINTING_EXPORT MetafileSkiaWrapper : public SkRefCnt {
+ public:
+ static void SetMetafileOnCanvas(const SkCanvas& canvas, Metafile* metafile);
+
+ static Metafile* GetMetafileFromCanvas(const SkCanvas& canvas);
+
+ // Methods to set and retrieve custom scale factor for metafile from canvas.
+ static void SetCustomScaleOnCanvas(const SkCanvas& canvas, double scale);
+ static bool GetCustomScaleOnCanvas(const SkCanvas& canvas, double* scale);
+
+ private:
+ explicit MetafileSkiaWrapper(Metafile* metafile);
+
+ Metafile* metafile_;
+};
+
+} // namespace printing
+
+#endif // PRINTING_METAFILE_SKIA_WRAPPER_H_
diff --git a/chromium/printing/page_number.cc b/chromium/printing/page_number.cc
new file mode 100644
index 00000000000..8e9287027e7
--- /dev/null
+++ b/chromium/printing/page_number.cc
@@ -0,0 +1,83 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/page_number.h"
+
+#include <limits>
+
+#include "base/logging.h"
+#include "printing/print_settings.h"
+
+namespace printing {
+
+PageNumber::PageNumber(const PrintSettings& settings, int document_page_count) {
+ Init(settings, document_page_count);
+}
+
+PageNumber::PageNumber()
+ : ranges_(NULL),
+ page_number_(-1),
+ page_range_index_(-1),
+ document_page_count_(0) {
+}
+
+void PageNumber::operator=(const PageNumber& other) {
+ ranges_ = other.ranges_;
+ page_number_ = other.page_number_;
+ page_range_index_ = other.page_range_index_;
+ document_page_count_ = other.document_page_count_;
+}
+
+void PageNumber::Init(const PrintSettings& settings, int document_page_count) {
+ DCHECK(document_page_count);
+ ranges_ = settings.ranges.empty() ? NULL : &settings.ranges;
+ document_page_count_ = document_page_count;
+ if (ranges_) {
+ page_range_index_ = 0;
+ page_number_ = (*ranges_)[0].from;
+ } else {
+ if (document_page_count) {
+ page_number_ = 0;
+ } else {
+ page_number_ = -1;
+ }
+ page_range_index_ = -1;
+ }
+}
+
+int PageNumber::operator++() {
+ if (!ranges_) {
+ // Switch to next page.
+ if (++page_number_ == document_page_count_) {
+ // Finished.
+ *this = npos();
+ }
+ } else {
+ // Switch to next page.
+ ++page_number_;
+ // Page ranges are inclusive.
+ if (page_number_ > (*ranges_)[page_range_index_].to) {
+ DCHECK(ranges_->size() <= static_cast<size_t>(
+ std::numeric_limits<int>::max()));
+ if (++page_range_index_ == static_cast<int>(ranges_->size())) {
+ // Finished.
+ *this = npos();
+ } else {
+ page_number_ = (*ranges_)[page_range_index_].from;
+ }
+ }
+ }
+ return ToInt();
+}
+
+bool PageNumber::operator==(const PageNumber& other) const {
+ return page_number_ == other.page_number_ &&
+ page_range_index_ == other.page_range_index_;
+}
+bool PageNumber::operator!=(const PageNumber& other) const {
+ return page_number_ != other.page_number_ ||
+ page_range_index_ != other.page_range_index_;
+}
+
+} // namespace printing
diff --git a/chromium/printing/page_number.h b/chromium/printing/page_number.h
new file mode 100644
index 00000000000..a51ad1e98ad
--- /dev/null
+++ b/chromium/printing/page_number.h
@@ -0,0 +1,73 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PAGE_NUMBER_H_
+#define PRINTING_PAGE_NUMBER_H_
+
+#include <ostream>
+
+#include "printing/page_range.h"
+
+namespace printing {
+
+class PrintSettings;
+
+// Represents a page series following the array of page ranges defined in a
+// PrintSettings.
+class PRINTING_EXPORT PageNumber {
+ public:
+ // Initializes the page to the first page in the settings's range or 0.
+ PageNumber(const PrintSettings& settings, int document_page_count);
+
+ PageNumber();
+
+ void operator=(const PageNumber& other);
+
+ // Initializes the page to the first page in the setting's range or 0. It
+ // initialize to npos if the range is empty and document_page_count is 0.
+ void Init(const PrintSettings& settings, int document_page_count);
+
+ // Converts to a page numbers.
+ int ToInt() const {
+ return page_number_;
+ }
+
+ // Calculates the next page in the serie.
+ int operator++();
+
+ // Returns an instance that represents the end of a serie.
+ static const PageNumber npos() {
+ return PageNumber();
+ }
+
+ // Equality operator. Only the current page number is verified so that
+ // "page != PageNumber::npos()" works.
+ bool operator==(const PageNumber& other) const;
+ bool operator!=(const PageNumber& other) const;
+
+ private:
+ // The page range to follow.
+ const PageRanges* ranges_;
+
+ // The next page to be printed. -1 when not printing.
+ int page_number_;
+
+ // The next page to be printed. -1 when not used. Valid only if
+ // document()->settings().range.empty() is false.
+ int page_range_index_;
+
+ // Number of expected pages in the document. Used when ranges_ is NULL.
+ int document_page_count_;
+};
+
+// Debug output support.
+template<class E, class T>
+inline typename std::basic_ostream<E,T>& operator<<(
+ typename std::basic_ostream<E,T>& ss, const PageNumber& page) {
+ return ss << page.ToInt();
+}
+
+} // namespace printing
+
+#endif // PRINTING_PAGE_NUMBER_H_
diff --git a/chromium/printing/page_number_unittest.cc b/chromium/printing/page_number_unittest.cc
new file mode 100644
index 00000000000..ece1e0f6f0a
--- /dev/null
+++ b/chromium/printing/page_number_unittest.cc
@@ -0,0 +1,30 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/page_number.h"
+#include "printing/print_settings.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(PageNumberTest, Count) {
+ printing::PrintSettings settings;
+ printing::PageNumber page;
+ EXPECT_EQ(printing::PageNumber::npos(), page);
+ page.Init(settings, 3);
+ EXPECT_EQ(0, page.ToInt());
+ EXPECT_NE(printing::PageNumber::npos(), page);
+ ++page;
+ EXPECT_EQ(1, page.ToInt());
+ EXPECT_NE(printing::PageNumber::npos(), page);
+
+ printing::PageNumber page_copy(page);
+ EXPECT_EQ(1, page_copy.ToInt());
+ EXPECT_EQ(1, page.ToInt());
+ ++page;
+ EXPECT_EQ(1, page_copy.ToInt());
+ EXPECT_EQ(2, page.ToInt());
+ ++page;
+ EXPECT_EQ(printing::PageNumber::npos(), page);
+ ++page;
+ EXPECT_EQ(printing::PageNumber::npos(), page);
+}
diff --git a/chromium/printing/page_range.cc b/chromium/printing/page_range.cc
new file mode 100644
index 00000000000..7a8cb84c84c
--- /dev/null
+++ b/chromium/printing/page_range.cc
@@ -0,0 +1,25 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/page_range.h"
+
+#include <set>
+#include <vector>
+
+namespace printing {
+
+/* static */
+std::vector<int> PageRange::GetPages(const PageRanges& ranges) {
+ std::set<int> pages;
+ for (unsigned i = 0; i < ranges.size(); ++i) {
+ const PageRange& range = ranges[i];
+ // Ranges are inclusive.
+ for (int i = range.from; i <= range.to; ++i) {
+ pages.insert(i);
+ }
+ }
+ return std::vector<int>(pages.begin(), pages.end());
+}
+
+} // namespace printing
diff --git a/chromium/printing/page_range.h b/chromium/printing/page_range.h
new file mode 100644
index 00000000000..989c33c22f7
--- /dev/null
+++ b/chromium/printing/page_range.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PAGE_RANGE_H_
+#define PRINTING_PAGE_RANGE_H_
+
+#include <vector>
+
+#include "printing_export.h"
+
+namespace printing {
+
+struct PageRange;
+
+typedef std::vector<PageRange> PageRanges;
+
+// Print range is inclusive. To select one page, set from == to.
+struct PRINTING_EXPORT PageRange {
+ int from;
+ int to;
+
+ bool operator==(const PageRange& rhs) const {
+ return from == rhs.from && to == rhs.to;
+ }
+
+ // Retrieves the sorted list of unique pages in the page ranges.
+ static std::vector<int> GetPages(const PageRanges& ranges);
+};
+
+} // namespace printing
+
+#endif // PRINTING_PAGE_RANGE_H_
diff --git a/chromium/printing/page_range_unittest.cc b/chromium/printing/page_range_unittest.cc
new file mode 100644
index 00000000000..7b0f5779fc2
--- /dev/null
+++ b/chromium/printing/page_range_unittest.cc
@@ -0,0 +1,36 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/page_range.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(PageRangeTest, RangeMerge) {
+ printing::PageRanges ranges;
+ printing::PageRange range;
+ range.from = 1;
+ range.to = 3;
+ ranges.push_back(range);
+ range.from = 10;
+ range.to = 12;
+ ranges.push_back(range);
+ range.from = 2;
+ range.to = 5;
+ ranges.push_back(range);
+ std::vector<int> pages(printing::PageRange::GetPages(ranges));
+ ASSERT_EQ(8U, pages.size());
+ EXPECT_EQ(1, pages[0]);
+ EXPECT_EQ(2, pages[1]);
+ EXPECT_EQ(3, pages[2]);
+ EXPECT_EQ(4, pages[3]);
+ EXPECT_EQ(5, pages[4]);
+ EXPECT_EQ(10, pages[5]);
+ EXPECT_EQ(11, pages[6]);
+ EXPECT_EQ(12, pages[7]);
+}
+
+TEST(PageRangeTest, Empty) {
+ printing::PageRanges ranges;
+ std::vector<int> pages(printing::PageRange::GetPages(ranges));
+ EXPECT_EQ(0U, pages.size());
+}
diff --git a/chromium/printing/page_setup.cc b/chromium/printing/page_setup.cc
new file mode 100644
index 00000000000..f0ea223adb0
--- /dev/null
+++ b/chromium/printing/page_setup.cc
@@ -0,0 +1,165 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/page_setup.h"
+
+#include <algorithm>
+
+#include "base/logging.h"
+
+namespace printing {
+
+PageMargins::PageMargins()
+ : header(0),
+ footer(0),
+ left(0),
+ right(0),
+ top(0),
+ bottom(0) {
+}
+
+void PageMargins::Clear() {
+ header = 0;
+ footer = 0;
+ left = 0;
+ right = 0;
+ top = 0;
+ bottom = 0;
+}
+
+bool PageMargins::Equals(const PageMargins& rhs) const {
+ return header == rhs.header &&
+ footer == rhs.footer &&
+ left == rhs.left &&
+ top == rhs.top &&
+ right == rhs.right &&
+ bottom == rhs.bottom;
+}
+
+PageSetup::PageSetup() {
+ Clear();
+}
+
+PageSetup::~PageSetup() {}
+
+void PageSetup::Clear() {
+ physical_size_.SetSize(0, 0);
+ printable_area_.SetRect(0, 0, 0, 0);
+ overlay_area_.SetRect(0, 0, 0, 0);
+ content_area_.SetRect(0, 0, 0, 0);
+ effective_margins_.Clear();
+ text_height_ = 0;
+ forced_margins_ = false;
+}
+
+bool PageSetup::Equals(const PageSetup& rhs) const {
+ return physical_size_ == rhs.physical_size_ &&
+ printable_area_ == rhs.printable_area_ &&
+ overlay_area_ == rhs.overlay_area_ &&
+ content_area_ == rhs.content_area_ &&
+ effective_margins_.Equals(rhs.effective_margins_) &&
+ requested_margins_.Equals(rhs.requested_margins_) &&
+ text_height_ == rhs.text_height_;
+}
+
+void PageSetup::Init(const gfx::Size& physical_size,
+ const gfx::Rect& printable_area,
+ int text_height) {
+ DCHECK_LE(printable_area.right(), physical_size.width());
+ // I've seen this assert triggers on Canon GP160PF PCL 5e and HP LaserJet 5.
+ // Since we don't know the dpi here, just disable the check.
+ // DCHECK_LE(printable_area.bottom(), physical_size.height());
+ DCHECK_GE(printable_area.x(), 0);
+ DCHECK_GE(printable_area.y(), 0);
+ DCHECK_GE(text_height, 0);
+ physical_size_ = physical_size;
+ printable_area_ = printable_area;
+ text_height_ = text_height;
+
+ SetRequestedMarginsAndCalculateSizes(requested_margins_);
+}
+
+void PageSetup::SetRequestedMargins(const PageMargins& requested_margins) {
+ forced_margins_ = false;
+ SetRequestedMarginsAndCalculateSizes(requested_margins);
+}
+
+void PageSetup::ForceRequestedMargins(const PageMargins& requested_margins) {
+ forced_margins_ = true;
+ SetRequestedMarginsAndCalculateSizes(requested_margins);
+}
+
+void PageSetup::FlipOrientation() {
+ if (physical_size_.width() && physical_size_.height()) {
+ gfx::Size new_size(physical_size_.height(), physical_size_.width());
+ int new_y = physical_size_.width() -
+ (printable_area_.width() + printable_area_.x());
+ gfx::Rect new_printable_area(printable_area_.y(),
+ new_y,
+ printable_area_.height(),
+ printable_area_.width());
+ Init(new_size, new_printable_area, text_height_);
+ }
+}
+
+void PageSetup::SetRequestedMarginsAndCalculateSizes(
+ const PageMargins& requested_margins) {
+ requested_margins_ = requested_margins;
+ if (physical_size_.width() && physical_size_.height()) {
+ if (forced_margins_)
+ CalculateSizesWithinRect(gfx::Rect(physical_size_), 0);
+ else
+ CalculateSizesWithinRect(printable_area_, text_height_);
+ }
+}
+
+void PageSetup::CalculateSizesWithinRect(const gfx::Rect& bounds,
+ int text_height) {
+ // Calculate the effective margins. The tricky part.
+ effective_margins_.header = std::max(requested_margins_.header,
+ bounds.y());
+ effective_margins_.footer = std::max(requested_margins_.footer,
+ physical_size_.height() -
+ bounds.bottom());
+ effective_margins_.left = std::max(requested_margins_.left,
+ bounds.x());
+ effective_margins_.top = std::max(std::max(requested_margins_.top,
+ bounds.y()),
+ effective_margins_.header + text_height);
+ effective_margins_.right = std::max(requested_margins_.right,
+ physical_size_.width() -
+ bounds.right());
+ effective_margins_.bottom =
+ std::max(std::max(requested_margins_.bottom,
+ physical_size_.height() - bounds.bottom()),
+ effective_margins_.footer + text_height);
+
+ // Calculate the overlay area. If the margins are excessive, the overlay_area
+ // size will be (0, 0).
+ overlay_area_.set_x(effective_margins_.left);
+ overlay_area_.set_y(effective_margins_.header);
+ overlay_area_.set_width(std::max(0,
+ physical_size_.width() -
+ effective_margins_.right -
+ overlay_area_.x()));
+ overlay_area_.set_height(std::max(0,
+ physical_size_.height() -
+ effective_margins_.footer -
+ overlay_area_.y()));
+
+ // Calculate the content area. If the margins are excessive, the content_area
+ // size will be (0, 0).
+ content_area_.set_x(effective_margins_.left);
+ content_area_.set_y(effective_margins_.top);
+ content_area_.set_width(std::max(0,
+ physical_size_.width() -
+ effective_margins_.right -
+ content_area_.x()));
+ content_area_.set_height(std::max(0,
+ physical_size_.height() -
+ effective_margins_.bottom -
+ content_area_.y()));
+}
+
+} // namespace printing
diff --git a/chromium/printing/page_setup.h b/chromium/printing/page_setup.h
new file mode 100644
index 00000000000..585d48ab8bd
--- /dev/null
+++ b/chromium/printing/page_setup.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PAGE_SETUP_H_
+#define PRINTING_PAGE_SETUP_H_
+
+#include "printing/printing_export.h"
+#include "ui/gfx/rect.h"
+
+namespace printing {
+
+// Margins for a page setup.
+class PRINTING_EXPORT PageMargins {
+ public:
+ PageMargins();
+
+ void Clear();
+
+ // Equality operator.
+ bool Equals(const PageMargins& rhs) const;
+
+ // Vertical space for the overlay from the top of the sheet.
+ int header;
+ // Vertical space for the overlay from the bottom of the sheet.
+ int footer;
+ // Margin on each side of the sheet.
+ int left;
+ int right;
+ int top;
+ int bottom;
+};
+
+// Settings that define the size and printable areas of a page. Unit is
+// unspecified.
+class PRINTING_EXPORT PageSetup {
+ public:
+ PageSetup();
+ ~PageSetup();
+
+ void Clear();
+
+ // Equality operator.
+ bool Equals(const PageSetup& rhs) const;
+
+ void Init(const gfx::Size& physical_size, const gfx::Rect& printable_area,
+ int text_height);
+
+ // Use |requested_margins| as long as they fall inside the printable area.
+ void SetRequestedMargins(const PageMargins& requested_margins);
+
+ // Ignore the printable area, and set the margins to |requested_margins|.
+ void ForceRequestedMargins(const PageMargins& requested_margins);
+
+ // Flips the orientation of the page and recalculates all page areas.
+ void FlipOrientation();
+
+ const gfx::Size& physical_size() const { return physical_size_; }
+ const gfx::Rect& overlay_area() const { return overlay_area_; }
+ const gfx::Rect& content_area() const { return content_area_; }
+ const gfx::Rect& printable_area() const { return printable_area_; }
+ const PageMargins& effective_margins() const {
+ return effective_margins_;
+ }
+
+ private:
+ // Store |requested_margins_| and update page setup values.
+ void SetRequestedMarginsAndCalculateSizes(
+ const PageMargins& requested_margins);
+
+ // Calculate overlay_area_, effective_margins_, and content_area_, based on
+ // a constraint of |bounds| and |text_height|.
+ void CalculateSizesWithinRect(const gfx::Rect& bounds, int text_height);
+
+ // Physical size of the page, including non-printable margins.
+ gfx::Size physical_size_;
+
+ // The printable area as specified by the printer driver. We can't get
+ // larger than this.
+ gfx::Rect printable_area_;
+
+ // The printable area for headers and footers.
+ gfx::Rect overlay_area_;
+
+ // The printable area as selected by the user's margins.
+ gfx::Rect content_area_;
+
+ // Effective margins.
+ PageMargins effective_margins_;
+
+ // Requested margins.
+ PageMargins requested_margins_;
+
+ // True when |effective_margins_| respects |printable_area_| else false.
+ bool forced_margins_;
+
+ // Space that must be kept free for the overlays.
+ int text_height_;
+};
+
+} // namespace printing
+
+#endif // PRINTING_PAGE_SETUP_H_
diff --git a/chromium/printing/page_setup_unittest.cc b/chromium/printing/page_setup_unittest.cc
new file mode 100644
index 00000000000..4915a6c0ddb
--- /dev/null
+++ b/chromium/printing/page_setup_unittest.cc
@@ -0,0 +1,274 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/page_setup.h"
+
+#include <stdlib.h>
+#include <time.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(PageSetupTest, Random) {
+ time_t seed = time(NULL);
+ int kMax = 10;
+ srand(static_cast<unsigned>(seed));
+
+ // Margins.
+ printing::PageMargins margins;
+ margins.header = rand() % kMax;
+ margins.footer = rand() % kMax;
+ margins.left = rand() % kMax;
+ margins.top = rand() % kMax;
+ margins.right = rand() % kMax;
+ margins.bottom = rand() % kMax;
+ int kTextHeight = rand() % kMax;
+
+ // Page description.
+ gfx::Size page_size(100 + rand() % kMax, 200 + rand() % kMax);
+ gfx::Rect printable_area(rand() % kMax, rand() % kMax, 0, 0);
+ printable_area.set_width(page_size.width() - (rand() % kMax) -
+ printable_area.x());
+ printable_area.set_height(page_size.height() - (rand() % kMax) -
+ printable_area.y());
+
+ // Make the calculations.
+ printing::PageSetup setup;
+ setup.SetRequestedMargins(margins);
+ setup.Init(page_size, printable_area, kTextHeight);
+
+ // Calculate the effective margins.
+ printing::PageMargins effective_margins;
+ effective_margins.header = std::max(margins.header, printable_area.y());
+ effective_margins.left = std::max(margins.left, printable_area.x());
+ effective_margins.top = std::max(margins.top,
+ effective_margins.header + kTextHeight);
+ effective_margins.footer = std::max(margins.footer,
+ page_size.height() -
+ printable_area.bottom());
+ effective_margins.right = std::max(margins.right,
+ page_size.width() -
+ printable_area.right());
+ effective_margins.bottom = std::max(margins.bottom,
+ effective_margins.footer + kTextHeight);
+
+ // Calculate the overlay area.
+ gfx::Rect overlay_area(effective_margins.left, effective_margins.header,
+ page_size.width() - effective_margins.right -
+ effective_margins.left,
+ page_size.height() - effective_margins.footer -
+ effective_margins.header);
+
+ // Calculate the content area.
+ gfx::Rect content_area(overlay_area.x(),
+ effective_margins.top,
+ overlay_area.width(),
+ page_size.height() - effective_margins.bottom -
+ effective_margins.top);
+
+ // Test values.
+ EXPECT_EQ(page_size, setup.physical_size()) << seed << " " <<
+ page_size.ToString() << " " << printable_area.ToString() <<
+ " " << kTextHeight;
+ EXPECT_EQ(overlay_area, setup.overlay_area()) << seed << " " <<
+ page_size.ToString() << " " << printable_area.ToString() <<
+ " " << kTextHeight;
+ EXPECT_EQ(content_area, setup.content_area()) << seed << " " <<
+ page_size.ToString() << " " << printable_area.ToString() <<
+ " " << kTextHeight;
+
+ EXPECT_EQ(effective_margins.header, setup.effective_margins().header) <<
+ seed << " " << page_size.ToString() << " " << printable_area.ToString() <<
+ " " << kTextHeight;
+ EXPECT_EQ(effective_margins.footer, setup.effective_margins().footer) <<
+ seed << " " << page_size.ToString() << " " << printable_area.ToString() <<
+ " " << kTextHeight;
+ EXPECT_EQ(effective_margins.left, setup.effective_margins().left) << seed <<
+ " " << page_size.ToString() << " " << printable_area.ToString() <<
+ " " << kTextHeight;
+ EXPECT_EQ(effective_margins.top, setup.effective_margins().top) << seed <<
+ " " << page_size.ToString() << " " << printable_area.ToString() <<
+ " " << kTextHeight;
+ EXPECT_EQ(effective_margins.right, setup.effective_margins().right) << seed <<
+ " " << page_size.ToString() << " " << printable_area.ToString() <<
+ " " << kTextHeight;
+ EXPECT_EQ(effective_margins.bottom, setup.effective_margins().bottom) <<
+ seed << " " << page_size.ToString() << " " << printable_area.ToString() <<
+ " " << kTextHeight;
+}
+
+TEST(PageSetupTest, HardCoded) {
+ // Margins.
+ printing::PageMargins margins;
+ margins.header = 2;
+ margins.footer = 2;
+ margins.left = 4;
+ margins.top = 4;
+ margins.right = 4;
+ margins.bottom = 4;
+ int kTextHeight = 3;
+
+ // Page description.
+ gfx::Size page_size(100, 100);
+ gfx::Rect printable_area(3, 3, 94, 94);
+
+ // Make the calculations.
+ printing::PageSetup setup;
+ setup.SetRequestedMargins(margins);
+ setup.Init(page_size, printable_area, kTextHeight);
+
+ // Calculate the effective margins.
+ printing::PageMargins effective_margins;
+ effective_margins.header = 3;
+ effective_margins.left = 4;
+ effective_margins.top = 6;
+ effective_margins.footer = 3;
+ effective_margins.right = 4;
+ effective_margins.bottom = 6;
+
+ // Calculate the overlay area.
+ gfx::Rect overlay_area(4, 3, 92, 94);
+
+ // Calculate the content area.
+ gfx::Rect content_area(4, 6, 92, 88);
+
+ // Test values.
+ EXPECT_EQ(page_size, setup.physical_size()) << " " << page_size.ToString() <<
+ " " << printable_area.ToString() << " " << kTextHeight;
+ EXPECT_EQ(overlay_area, setup.overlay_area()) << " " <<
+ page_size.ToString() << " " << printable_area.ToString() <<
+ " " << kTextHeight;
+ EXPECT_EQ(content_area, setup.content_area()) << " " <<
+ page_size.ToString() << " " << printable_area.ToString() <<
+ " " << kTextHeight;
+
+ EXPECT_EQ(effective_margins.header, setup.effective_margins().header) <<
+ " " << page_size.ToString() << " " <<
+ printable_area.ToString() << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.footer, setup.effective_margins().footer) <<
+ " " << page_size.ToString() << " " << printable_area.ToString() <<
+ " " << kTextHeight;
+ EXPECT_EQ(effective_margins.left, setup.effective_margins().left) <<
+ " " << page_size.ToString() << " " << printable_area.ToString() <<
+ " " << kTextHeight;
+ EXPECT_EQ(effective_margins.top, setup.effective_margins().top) <<
+ " " << page_size.ToString() << " " << printable_area.ToString() <<
+ " " << kTextHeight;
+ EXPECT_EQ(effective_margins.right, setup.effective_margins().right) <<
+ " " << page_size.ToString() << " " << printable_area.ToString() <<
+ " " << kTextHeight;
+ EXPECT_EQ(effective_margins.bottom, setup.effective_margins().bottom) <<
+ " " << page_size.ToString() << " " << printable_area.ToString() <<
+ " " << kTextHeight;
+}
+
+TEST(PageSetupTest, OutOfRangeMargins) {
+ printing::PageMargins margins;
+ margins.header = 0;
+ margins.footer = 0;
+ margins.left = -10;
+ margins.top = -11;
+ margins.right = -12;
+ margins.bottom = -13;
+
+ gfx::Size page_size(100, 100);
+ gfx::Rect printable_area(1, 2, 96, 94);
+
+ // Make the calculations.
+ printing::PageSetup setup;
+ setup.SetRequestedMargins(margins);
+ setup.Init(page_size, printable_area, 0);
+
+ EXPECT_EQ(setup.effective_margins().left, 1);
+ EXPECT_EQ(setup.effective_margins().top, 2);
+ EXPECT_EQ(setup.effective_margins().right, 3);
+ EXPECT_EQ(setup.effective_margins().bottom, 4);
+
+ setup.ForceRequestedMargins(margins);
+ EXPECT_EQ(setup.effective_margins().left, 0);
+ EXPECT_EQ(setup.effective_margins().top, 0);
+ EXPECT_EQ(setup.effective_margins().right, 0);
+ EXPECT_EQ(setup.effective_margins().bottom, 0);
+}
+
+TEST(PageSetupTest, FlipOrientation) {
+ // Margins.
+ printing::PageMargins margins;
+ margins.header = 2;
+ margins.footer = 3;
+ margins.left = 4;
+ margins.top = 14;
+ margins.right = 6;
+ margins.bottom = 7;
+ int kTextHeight = 5;
+
+ // Page description.
+ gfx::Size page_size(100, 70);
+ gfx::Rect printable_area(8, 9, 92, 50);
+
+ // Make the calculations.
+ printing::PageSetup setup;
+ setup.SetRequestedMargins(margins);
+ setup.Init(page_size, printable_area, kTextHeight);
+
+ gfx::Rect overlay_area(8, 9, 86, 50);
+ gfx::Rect content_area(8, 14, 86, 40);
+
+ EXPECT_EQ(page_size, setup.physical_size());
+ EXPECT_EQ(overlay_area, setup.overlay_area());
+ EXPECT_EQ(content_area, setup.content_area());
+
+ EXPECT_EQ(setup.effective_margins().left, 8);
+ EXPECT_EQ(setup.effective_margins().top, 14);
+ EXPECT_EQ(setup.effective_margins().right, 6);
+ EXPECT_EQ(setup.effective_margins().bottom, 16);
+
+ // Flip the orientation
+ setup.FlipOrientation();
+
+ // Expected values.
+ gfx::Size flipped_page_size(70, 100);
+ gfx::Rect flipped_printable_area(9, 0, 50, 92);
+ gfx::Rect flipped_overlay_area(9, 2, 50, 90);
+ gfx::Rect flipped_content_area(9, 14, 50, 73);
+
+ // Test values.
+ EXPECT_EQ(flipped_page_size, setup.physical_size());
+ EXPECT_EQ(flipped_overlay_area, setup.overlay_area());
+ EXPECT_EQ(flipped_content_area, setup.content_area());
+ EXPECT_EQ(flipped_printable_area, setup.printable_area());
+
+ // Margin values are updated as per the flipped values.
+ EXPECT_EQ(setup.effective_margins().left, 9);
+ EXPECT_EQ(setup.effective_margins().top, 14);
+ EXPECT_EQ(setup.effective_margins().right, 11);
+ EXPECT_EQ(setup.effective_margins().bottom, 13);
+
+ // Force requested margins and flip the orientation.
+ setup.Init(page_size, printable_area, kTextHeight);
+ setup.ForceRequestedMargins(margins);
+ EXPECT_EQ(setup.effective_margins().left, 4);
+ EXPECT_EQ(setup.effective_margins().top, 14);
+ EXPECT_EQ(setup.effective_margins().right, 6);
+ EXPECT_EQ(setup.effective_margins().bottom, 7);
+
+ // Flip the orientation
+ setup.FlipOrientation();
+
+ // Expected values.
+ gfx::Rect new_printable_area(9, 0, 50, 92);
+ gfx::Rect new_overlay_area(4, 2, 60, 95);
+ gfx::Rect new_content_area(4, 14, 60, 79);
+
+ // Test values.
+ EXPECT_EQ(flipped_page_size, setup.physical_size());
+ EXPECT_EQ(new_overlay_area, setup.overlay_area());
+ EXPECT_EQ(new_content_area, setup.content_area());
+ EXPECT_EQ(new_printable_area, setup.printable_area());
+
+ // Margins values are changed respectively.
+ EXPECT_EQ(setup.effective_margins().left, 4);
+ EXPECT_EQ(setup.effective_margins().top, 14);
+ EXPECT_EQ(setup.effective_margins().right, 6);
+ EXPECT_EQ(setup.effective_margins().bottom, 7);
+}
diff --git a/chromium/printing/page_size_margins.cc b/chromium/printing/page_size_margins.cc
new file mode 100644
index 00000000000..a28c6ada3c8
--- /dev/null
+++ b/chromium/printing/page_size_margins.cc
@@ -0,0 +1,29 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/page_size_margins.h"
+
+#include "base/logging.h"
+#include "base/values.h"
+#include "printing/print_job_constants.h"
+
+namespace printing {
+
+void GetCustomMarginsFromJobSettings(const base::DictionaryValue& settings,
+ PageSizeMargins* page_size_margins) {
+ const base::DictionaryValue* custom_margins;
+ if (!settings.GetDictionary(kSettingMarginsCustom, &custom_margins) ||
+ !custom_margins->GetDouble(kSettingMarginTop,
+ &page_size_margins->margin_top) ||
+ !custom_margins->GetDouble(kSettingMarginBottom,
+ &page_size_margins->margin_bottom) ||
+ !custom_margins->GetDouble(kSettingMarginLeft,
+ &page_size_margins->margin_left) ||
+ !custom_margins->GetDouble(kSettingMarginRight,
+ &page_size_margins->margin_right)) {
+ NOTREACHED();
+ }
+}
+
+} // namespace printing
diff --git a/chromium/printing/page_size_margins.h b/chromium/printing/page_size_margins.h
new file mode 100644
index 00000000000..b6913724a90
--- /dev/null
+++ b/chromium/printing/page_size_margins.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PAGE_SIZE_MARGINS_H_
+#define PRINTING_PAGE_SIZE_MARGINS_H_
+
+#include "printing/printing_export.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace printing {
+
+// Struct that holds margin and content area sizes of a page. Units are
+// arbitrary and can be chosen by the programmer.
+struct PageSizeMargins {
+ double content_width;
+ double content_height;
+ double margin_top;
+ double margin_right;
+ double margin_bottom;
+ double margin_left;
+};
+
+PRINTING_EXPORT void GetCustomMarginsFromJobSettings(
+ const base::DictionaryValue& settings, PageSizeMargins* page_size_margins);
+
+} // namespace printing
+
+#endif // PRINTING_PAGE_SIZE_MARGINS_H_
diff --git a/chromium/printing/pdf_metafile_cg_mac.cc b/chromium/printing/pdf_metafile_cg_mac.cc
new file mode 100644
index 00000000000..cd7c5c7e27b
--- /dev/null
+++ b/chromium/printing/pdf_metafile_cg_mac.cc
@@ -0,0 +1,319 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/pdf_metafile_cg_mac.h"
+
+#include <algorithm>
+
+#include "base/files/file_path.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/threading/thread_local.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+
+using base::ScopedCFTypeRef;
+
+namespace {
+
+// What is up with this ugly hack? <http://crbug.com/64641>, that's what.
+// The bug: Printing certain PDFs crashes. The cause: When printing, the
+// renderer process assembles pages one at a time, in PDF format, to send to the
+// browser process. When printing a PDF, the PDF plugin returns output in PDF
+// format. There is a bug in 10.5 and 10.6 (<rdar://9018916>,
+// <http://www.openradar.me/9018916>) where reference counting is broken when
+// drawing certain PDFs into PDF contexts. So at the high-level, a PdfMetafileCg
+// is used to hold the destination context, and then about five layers down on
+// the callstack, a PdfMetafileCg is used to hold the source PDF. If the source
+// PDF is drawn into the destination PDF context and then released, accessing
+// the destination PDF context will crash. So the outermost instantiation of
+// PdfMetafileCg creates a pool for deeper instantiations to dump their used
+// PDFs into rather than releasing them. When the top-level PDF is closed, then
+// it's safe to clear the pool. A thread local is used to allow this to work in
+// single-process mode. TODO(avi): This Apple bug appears fixed in 10.7; when
+// 10.7 is the minimum required version for Chromium, remove this hack.
+
+base::LazyInstance<base::ThreadLocalPointer<struct __CFSet> >::Leaky
+ thread_pdf_docs = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+namespace printing {
+
+PdfMetafileCg::PdfMetafileCg()
+ : page_is_open_(false),
+ thread_pdf_docs_owned_(false) {
+ if (!thread_pdf_docs.Pointer()->Get() &&
+ base::mac::IsOSSnowLeopard()) {
+ thread_pdf_docs_owned_ = true;
+ thread_pdf_docs.Pointer()->Set(
+ CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks));
+ }
+}
+
+PdfMetafileCg::~PdfMetafileCg() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ if (pdf_doc_ && thread_pdf_docs.Pointer()->Get()) {
+ // Transfer ownership to the pool.
+ CFSetAddValue(thread_pdf_docs.Pointer()->Get(), pdf_doc_);
+ }
+
+ if (thread_pdf_docs_owned_) {
+ CFRelease(thread_pdf_docs.Pointer()->Get());
+ thread_pdf_docs.Pointer()->Set(NULL);
+ }
+}
+
+bool PdfMetafileCg::Init() {
+ // Ensure that Init hasn't already been called.
+ DCHECK(!context_.get());
+ DCHECK(!pdf_data_.get());
+
+ pdf_data_.reset(CFDataCreateMutable(kCFAllocatorDefault, 0));
+ if (!pdf_data_.get()) {
+ LOG(ERROR) << "Failed to create pdf data for metafile";
+ return false;
+ }
+ ScopedCFTypeRef<CGDataConsumerRef> pdf_consumer(
+ CGDataConsumerCreateWithCFData(pdf_data_));
+ if (!pdf_consumer.get()) {
+ LOG(ERROR) << "Failed to create data consumer for metafile";
+ pdf_data_.reset(NULL);
+ return false;
+ }
+ context_.reset(CGPDFContextCreate(pdf_consumer, NULL, NULL));
+ if (!context_.get()) {
+ LOG(ERROR) << "Failed to create pdf context for metafile";
+ pdf_data_.reset(NULL);
+ }
+
+ return true;
+}
+
+bool PdfMetafileCg::InitFromData(const void* src_buffer,
+ uint32 src_buffer_size) {
+ DCHECK(!context_.get());
+ DCHECK(!pdf_data_.get());
+
+ if (!src_buffer || src_buffer_size == 0) {
+ return false;
+ }
+
+ pdf_data_.reset(CFDataCreateMutable(kCFAllocatorDefault, src_buffer_size));
+ CFDataAppendBytes(pdf_data_, static_cast<const UInt8*>(src_buffer),
+ src_buffer_size);
+
+ return true;
+}
+
+SkDevice* PdfMetafileCg::StartPageForVectorCanvas(
+ const gfx::Size& page_size, const gfx::Rect& content_area,
+ const float& scale_factor) {
+ NOTIMPLEMENTED();
+ return NULL;
+}
+
+bool PdfMetafileCg::StartPage(const gfx::Size& page_size,
+ const gfx::Rect& content_area,
+ const float& scale_factor) {
+ DCHECK(context_.get());
+ DCHECK(!page_is_open_);
+
+ double height = page_size.height();
+ double width = page_size.width();
+
+ CGRect bounds = CGRectMake(0, 0, width, height);
+ CGContextBeginPage(context_, &bounds);
+ page_is_open_ = true;
+ CGContextSaveGState(context_);
+
+ // Move to the context origin.
+ CGContextTranslateCTM(context_, content_area.x(), -content_area.y());
+
+ // Flip the context.
+ CGContextTranslateCTM(context_, 0, height);
+ CGContextScaleCTM(context_, scale_factor, -scale_factor);
+
+ return context_.get() != NULL;
+}
+
+bool PdfMetafileCg::FinishPage() {
+ DCHECK(context_.get());
+ DCHECK(page_is_open_);
+
+ CGContextRestoreGState(context_);
+ CGContextEndPage(context_);
+ page_is_open_ = false;
+ return true;
+}
+
+bool PdfMetafileCg::FinishDocument() {
+ DCHECK(context_.get());
+ DCHECK(!page_is_open_);
+
+#ifndef NDEBUG
+ // Check that the context will be torn down properly; if it's not, pdf_data_
+ // will be incomplete and generate invalid PDF files/documents.
+ if (context_.get()) {
+ CFIndex extra_retain_count = CFGetRetainCount(context_.get()) - 1;
+ if (extra_retain_count > 0) {
+ LOG(ERROR) << "Metafile context has " << extra_retain_count
+ << " extra retain(s) on Close";
+ }
+ }
+#endif
+ CGPDFContextClose(context_.get());
+ context_.reset(NULL);
+ return true;
+}
+
+bool PdfMetafileCg::RenderPage(unsigned int page_number,
+ CGContextRef context,
+ const CGRect rect,
+ const MacRenderPageParams& params) const {
+ CGPDFDocumentRef pdf_doc = GetPDFDocument();
+ if (!pdf_doc) {
+ LOG(ERROR) << "Unable to create PDF document from data";
+ return false;
+ }
+ CGPDFPageRef pdf_page = CGPDFDocumentGetPage(pdf_doc, page_number);
+ CGRect source_rect = CGPDFPageGetBoxRect(pdf_page, kCGPDFCropBox);
+ float scaling_factor = 1.0;
+ const bool source_is_landscape =
+ (source_rect.size.width > source_rect.size.height);
+ const bool dest_is_landscape = (rect.size.width > rect.size.height);
+ const bool rotate =
+ params.autorotate ? (source_is_landscape != dest_is_landscape) : false;
+ const float source_width =
+ rotate ? source_rect.size.height : source_rect.size.width;
+ const float source_height =
+ rotate ? source_rect.size.width : source_rect.size.height;
+
+ // See if we need to scale the output.
+ const bool scaling_needed =
+ (params.shrink_to_fit && ((source_width > rect.size.width) ||
+ (source_height > rect.size.height))) ||
+ (params.stretch_to_fit && ((source_width < rect.size.width) &&
+ (source_height < rect.size.height)));
+ if (scaling_needed) {
+ float x_scaling_factor = rect.size.width / source_width;
+ float y_scaling_factor = rect.size.height / source_height;
+ scaling_factor = std::min(x_scaling_factor, y_scaling_factor);
+ }
+ // Some PDFs have a non-zero origin. Need to take that into account and align
+ // the PDF to the origin.
+ const float x_origin_offset = -1 * source_rect.origin.x;
+ const float y_origin_offset = -1 * source_rect.origin.y;
+
+ // If the PDF needs to be centered, calculate the offsets here.
+ float x_offset = params.center_horizontally ?
+ ((rect.size.width - (source_width * scaling_factor)) / 2) : 0;
+ if (rotate)
+ x_offset = -x_offset;
+
+ float y_offset = params.center_vertically ?
+ ((rect.size.height - (source_height * scaling_factor)) / 2) : 0;
+
+ CGContextSaveGState(context);
+
+ // The transform operations specified here gets applied in reverse order.
+ // i.e. the origin offset translation happens first.
+ // Origin is at bottom-left.
+ CGContextTranslateCTM(context, x_offset, y_offset);
+ if (rotate) {
+ // After rotating by 90 degrees with the axis at the origin, the page
+ // content is now "off screen". Shift it right to move it back on screen.
+ CGContextTranslateCTM(context, rect.size.width, 0);
+ // Rotates counter-clockwise by 90 degrees.
+ CGContextRotateCTM(context, M_PI_2);
+ }
+ CGContextScaleCTM(context, scaling_factor, scaling_factor);
+ CGContextTranslateCTM(context, x_origin_offset, y_origin_offset);
+
+ CGContextDrawPDFPage(context, pdf_page);
+ CGContextRestoreGState(context);
+
+ return true;
+}
+
+unsigned int PdfMetafileCg::GetPageCount() const {
+ CGPDFDocumentRef pdf_doc = GetPDFDocument();
+ return pdf_doc ? CGPDFDocumentGetNumberOfPages(pdf_doc) : 0;
+}
+
+gfx::Rect PdfMetafileCg::GetPageBounds(unsigned int page_number) const {
+ CGPDFDocumentRef pdf_doc = GetPDFDocument();
+ if (!pdf_doc) {
+ LOG(ERROR) << "Unable to create PDF document from data";
+ return gfx::Rect();
+ }
+ if (page_number > GetPageCount()) {
+ LOG(ERROR) << "Invalid page number: " << page_number;
+ return gfx::Rect();
+ }
+ CGPDFPageRef pdf_page = CGPDFDocumentGetPage(pdf_doc, page_number);
+ CGRect page_rect = CGPDFPageGetBoxRect(pdf_page, kCGPDFMediaBox);
+ return gfx::Rect(page_rect);
+}
+
+uint32 PdfMetafileCg::GetDataSize() const {
+ // PDF data is only valid/complete once the context is released.
+ DCHECK(!context_);
+
+ if (!pdf_data_)
+ return 0;
+ return static_cast<uint32>(CFDataGetLength(pdf_data_));
+}
+
+bool PdfMetafileCg::GetData(void* dst_buffer, uint32 dst_buffer_size) const {
+ // PDF data is only valid/complete once the context is released.
+ DCHECK(!context_);
+ DCHECK(pdf_data_);
+ DCHECK(dst_buffer);
+ DCHECK_GT(dst_buffer_size, 0U);
+
+ uint32 data_size = GetDataSize();
+ if (dst_buffer_size > data_size) {
+ return false;
+ }
+
+ CFDataGetBytes(pdf_data_, CFRangeMake(0, dst_buffer_size),
+ static_cast<UInt8*>(dst_buffer));
+ return true;
+}
+
+bool PdfMetafileCg::SaveTo(const base::FilePath& file_path) const {
+ DCHECK(pdf_data_.get());
+ DCHECK(!context_.get());
+
+ std::string path_string = file_path.value();
+ ScopedCFTypeRef<CFURLRef> path_url(CFURLCreateFromFileSystemRepresentation(
+ kCFAllocatorDefault, reinterpret_cast<const UInt8*>(path_string.c_str()),
+ path_string.length(), false));
+ SInt32 error_code;
+ CFURLWriteDataAndPropertiesToResource(path_url, pdf_data_, NULL, &error_code);
+ return error_code == 0;
+}
+
+CGContextRef PdfMetafileCg::context() const {
+ return context_.get();
+}
+
+CGPDFDocumentRef PdfMetafileCg::GetPDFDocument() const {
+ // Make sure that we have data, and that it's not being modified any more.
+ DCHECK(pdf_data_.get());
+ DCHECK(!context_.get());
+
+ if (!pdf_doc_.get()) {
+ ScopedCFTypeRef<CGDataProviderRef> pdf_data_provider(
+ CGDataProviderCreateWithCFData(pdf_data_));
+ pdf_doc_.reset(CGPDFDocumentCreateWithProvider(pdf_data_provider));
+ }
+ return pdf_doc_.get();
+}
+
+} // namespace printing
diff --git a/chromium/printing/pdf_metafile_cg_mac.h b/chromium/printing/pdf_metafile_cg_mac.h
new file mode 100644
index 00000000000..77f703150c6
--- /dev/null
+++ b/chromium/printing/pdf_metafile_cg_mac.h
@@ -0,0 +1,93 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PDF_METAFILE_CG_MAC_H_
+#define PRINTING_PDF_METAFILE_CG_MAC_H_
+
+#include <ApplicationServices/ApplicationServices.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "base/basictypes.h"
+#include "base/gtest_prod_util.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/threading/thread_checker.h"
+#include "printing/metafile.h"
+
+namespace base {
+class FilePath;
+}
+
+namespace gfx {
+class Rect;
+class Size;
+}
+
+namespace printing {
+
+// This class creates a graphics context that renders into a PDF data stream.
+class PRINTING_EXPORT PdfMetafileCg : public Metafile {
+ public:
+ PdfMetafileCg();
+ virtual ~PdfMetafileCg();
+
+ // Metafile methods.
+ virtual bool Init() OVERRIDE;
+ virtual bool InitFromData(const void* src_buffer,
+ uint32 src_buffer_size) OVERRIDE;
+
+ // Not implemented on mac.
+ virtual SkDevice* StartPageForVectorCanvas(
+ const gfx::Size& page_size, const gfx::Rect& content_area,
+ const float& scale_factor) OVERRIDE;
+ virtual bool StartPage(const gfx::Size& page_size,
+ const gfx::Rect& content_area,
+ const float& scale_factor) OVERRIDE;
+ virtual bool FinishPage() OVERRIDE;
+ virtual bool FinishDocument() OVERRIDE;
+
+ virtual uint32 GetDataSize() const OVERRIDE;
+ virtual bool GetData(void* dst_buffer, uint32 dst_buffer_size) const OVERRIDE;
+
+ // For testing purposes only.
+ virtual bool SaveTo(const base::FilePath& file_path) const OVERRIDE;
+
+ virtual gfx::Rect GetPageBounds(unsigned int page_number) const OVERRIDE;
+ virtual unsigned int GetPageCount() const OVERRIDE;
+
+ // Note: The returned context *must not be retained* past Close(). If it is,
+ // the data returned from GetData will not be valid PDF data.
+ virtual CGContextRef context() const OVERRIDE;
+
+ virtual bool RenderPage(unsigned int page_number,
+ gfx::NativeDrawingContext context,
+ const CGRect rect,
+ const MacRenderPageParams& params) const OVERRIDE;
+
+ private:
+ // Returns a CGPDFDocumentRef version of pdf_data_.
+ CGPDFDocumentRef GetPDFDocument() const;
+
+ base::ThreadChecker thread_checker_;
+
+ // Context for rendering to the pdf.
+ base::ScopedCFTypeRef<CGContextRef> context_;
+
+ // PDF backing store.
+ base::ScopedCFTypeRef<CFMutableDataRef> pdf_data_;
+
+ // Lazily-created CGPDFDocument representation of pdf_data_.
+ mutable base::ScopedCFTypeRef<CGPDFDocumentRef> pdf_doc_;
+
+ // Whether or not a page is currently open.
+ bool page_is_open_;
+
+ // Whether this instantiation of the PdfMetafileCg owns the thread_pdf_docs.
+ bool thread_pdf_docs_owned_;
+
+ DISALLOW_COPY_AND_ASSIGN(PdfMetafileCg);
+};
+
+} // namespace printing
+
+#endif // PRINTING_PDF_METAFILE_CG_MAC_H_
diff --git a/chromium/printing/pdf_metafile_cg_mac_unittest.cc b/chromium/printing/pdf_metafile_cg_mac_unittest.cc
new file mode 100644
index 00000000000..1a213aea83d
--- /dev/null
+++ b/chromium/printing/pdf_metafile_cg_mac_unittest.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/pdf_metafile_cg_mac.h"
+
+#import <ApplicationServices/ApplicationServices.h>
+
+#include <string>
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/rect.h"
+
+namespace printing {
+
+TEST(PdfMetafileCgTest, Pdf) {
+ // Test in-renderer constructor.
+ PdfMetafileCg pdf;
+ EXPECT_TRUE(pdf.Init());
+ EXPECT_TRUE(pdf.context() != NULL);
+
+ // Render page 1.
+ gfx::Rect rect_1(10, 10, 520, 700);
+ gfx::Size size_1(540, 720);
+ pdf.StartPage(size_1, rect_1, 1.25);
+ pdf.FinishPage();
+
+ // Render page 2.
+ gfx::Rect rect_2(10, 10, 520, 700);
+ gfx::Size size_2(720, 540);
+ pdf.StartPage(size_2, rect_2, 2.0);
+ pdf.FinishPage();
+
+ pdf.FinishDocument();
+
+ // Check data size.
+ uint32 size = pdf.GetDataSize();
+ EXPECT_GT(size, 0U);
+
+ // Get resulting data.
+ std::vector<char> buffer(size, 0);
+ pdf.GetData(&buffer.front(), size);
+
+ // Test browser-side constructor.
+ PdfMetafileCg pdf2;
+ EXPECT_TRUE(pdf2.InitFromData(&buffer.front(), size));
+
+ // Get the first 4 characters from pdf2.
+ std::vector<char> buffer2(4, 0);
+ pdf2.GetData(&buffer2.front(), 4);
+
+ // Test that the header begins with "%PDF".
+ std::string header(&buffer2.front(), 4);
+ EXPECT_EQ(0U, header.find("%PDF", 0));
+
+ // Test that the PDF is correctly reconstructed.
+ EXPECT_EQ(2U, pdf2.GetPageCount());
+ gfx::Size page_size = pdf2.GetPageBounds(1).size();
+ EXPECT_EQ(540, page_size.width());
+ EXPECT_EQ(720, page_size.height());
+ page_size = pdf2.GetPageBounds(2).size();
+ EXPECT_EQ(720, page_size.width());
+ EXPECT_EQ(540, page_size.height());
+}
+
+} // namespace printing
diff --git a/chromium/printing/pdf_metafile_skia.cc b/chromium/printing/pdf_metafile_skia.cc
new file mode 100644
index 00000000000..3326a1d6d86
--- /dev/null
+++ b/chromium/printing/pdf_metafile_skia.cc
@@ -0,0 +1,248 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/pdf_metafile_skia.h"
+
+#include "base/containers/hash_tables.h"
+#include "base/file_descriptor_posix.h"
+#include "base/file_util.h"
+#include "base/metrics/histogram.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/safe_numerics.h"
+#include "skia/ext/refptr.h"
+#include "skia/ext/vector_platform_device_skia.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkScalar.h"
+#include "third_party/skia/include/core/SkStream.h"
+#include "third_party/skia/include/core/SkTypeface.h"
+#include "third_party/skia/include/pdf/SkPDFDevice.h"
+#include "third_party/skia/include/pdf/SkPDFDocument.h"
+#include "ui/gfx/point.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+
+#if defined(OS_MACOSX)
+#include "printing/pdf_metafile_cg_mac.h"
+#endif
+
+namespace printing {
+
+struct PdfMetafileSkiaData {
+ skia::RefPtr<SkPDFDevice> current_page_;
+ SkPDFDocument pdf_doc_;
+ SkDynamicMemoryWStream pdf_stream_;
+#if defined(OS_MACOSX)
+ PdfMetafileCg pdf_cg_;
+#endif
+};
+
+PdfMetafileSkia::~PdfMetafileSkia() {}
+
+bool PdfMetafileSkia::Init() {
+ return true;
+}
+bool PdfMetafileSkia::InitFromData(const void* src_buffer,
+ uint32 src_buffer_size) {
+ return data_->pdf_stream_.write(src_buffer, src_buffer_size);
+}
+
+SkDevice* PdfMetafileSkia::StartPageForVectorCanvas(
+ const gfx::Size& page_size, const gfx::Rect& content_area,
+ const float& scale_factor) {
+ DCHECK(!page_outstanding_);
+ page_outstanding_ = true;
+
+ // Adjust for the margins and apply the scale factor.
+ SkMatrix transform;
+ transform.setTranslate(SkIntToScalar(content_area.x()),
+ SkIntToScalar(content_area.y()));
+ transform.preScale(SkFloatToScalar(scale_factor),
+ SkFloatToScalar(scale_factor));
+
+ SkISize pdf_page_size = SkISize::Make(page_size.width(), page_size.height());
+ SkISize pdf_content_size =
+ SkISize::Make(content_area.width(), content_area.height());
+ skia::RefPtr<SkPDFDevice> pdf_device =
+ skia::AdoptRef(new skia::VectorPlatformDeviceSkia(
+ pdf_page_size, pdf_content_size, transform));
+ data_->current_page_ = pdf_device;
+ return pdf_device.get();
+}
+
+bool PdfMetafileSkia::StartPage(const gfx::Size& page_size,
+ const gfx::Rect& content_area,
+ const float& scale_factor) {
+ NOTREACHED();
+ return false;
+}
+
+bool PdfMetafileSkia::FinishPage() {
+ DCHECK(data_->current_page_.get());
+
+ data_->pdf_doc_.appendPage(data_->current_page_.get());
+ page_outstanding_ = false;
+ return true;
+}
+
+bool PdfMetafileSkia::FinishDocument() {
+ // Don't do anything if we've already set the data in InitFromData.
+ if (data_->pdf_stream_.getOffset())
+ return true;
+
+ if (page_outstanding_)
+ FinishPage();
+
+ data_->current_page_.clear();
+
+ int font_counts[SkAdvancedTypefaceMetrics::kNotEmbeddable_Font + 1];
+ data_->pdf_doc_.getCountOfFontTypes(font_counts);
+ for (int type = 0;
+ type <= SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
+ type++) {
+ for (int count = 0; count < font_counts[type]; count++) {
+ UMA_HISTOGRAM_ENUMERATION(
+ "PrintPreview.FontType", type,
+ SkAdvancedTypefaceMetrics::kNotEmbeddable_Font + 1);
+ }
+ }
+
+ return data_->pdf_doc_.emitPDF(&data_->pdf_stream_);
+}
+
+uint32 PdfMetafileSkia::GetDataSize() const {
+ return base::checked_numeric_cast<uint32>(data_->pdf_stream_.getOffset());
+}
+
+bool PdfMetafileSkia::GetData(void* dst_buffer,
+ uint32 dst_buffer_size) const {
+ if (dst_buffer_size < GetDataSize())
+ return false;
+
+ SkAutoDataUnref data(data_->pdf_stream_.copyToData());
+ memcpy(dst_buffer, data->bytes(), dst_buffer_size);
+ return true;
+}
+
+bool PdfMetafileSkia::SaveTo(const base::FilePath& file_path) const {
+ DCHECK_GT(data_->pdf_stream_.getOffset(), 0U);
+ SkAutoDataUnref data(data_->pdf_stream_.copyToData());
+ if (file_util::WriteFile(file_path,
+ reinterpret_cast<const char*>(data->data()),
+ GetDataSize()) != static_cast<int>(GetDataSize())) {
+ DLOG(ERROR) << "Failed to save file " << file_path.value().c_str();
+ return false;
+ }
+ return true;
+}
+
+gfx::Rect PdfMetafileSkia::GetPageBounds(unsigned int page_number) const {
+ // TODO(vandebo) add a method to get the page size for a given page to
+ // SkPDFDocument.
+ NOTIMPLEMENTED();
+ return gfx::Rect();
+}
+
+unsigned int PdfMetafileSkia::GetPageCount() const {
+ // TODO(vandebo) add a method to get the number of pages to SkPDFDocument.
+ NOTIMPLEMENTED();
+ return 0;
+}
+
+gfx::NativeDrawingContext PdfMetafileSkia::context() const {
+ NOTREACHED();
+ return NULL;
+}
+
+#if defined(OS_WIN)
+bool PdfMetafileSkia::Playback(gfx::NativeDrawingContext hdc,
+ const RECT* rect) const {
+ NOTREACHED();
+ return false;
+}
+
+bool PdfMetafileSkia::SafePlayback(gfx::NativeDrawingContext hdc) const {
+ NOTREACHED();
+ return false;
+}
+
+HENHMETAFILE PdfMetafileSkia::emf() const {
+ NOTREACHED();
+ return NULL;
+}
+#elif defined(OS_MACOSX)
+/* TODO(caryclark): The set up of PluginInstance::PrintPDFOutput may result in
+ rasterized output. Even if that flow uses PdfMetafileCg::RenderPage,
+ the drawing of the PDF into the canvas may result in a rasterized output.
+ PDFMetafileSkia::RenderPage should be not implemented as shown and instead
+ should do something like the following CL in PluginInstance::PrintPDFOutput:
+http://codereview.chromium.org/7200040/diff/1/webkit/plugins/ppapi/ppapi_plugin_instance.cc
+*/
+bool PdfMetafileSkia::RenderPage(unsigned int page_number,
+ CGContextRef context,
+ const CGRect rect,
+ const MacRenderPageParams& params) const {
+ DCHECK_GT(data_->pdf_stream_.getOffset(), 0U);
+ if (data_->pdf_cg_.GetDataSize() == 0) {
+ SkAutoDataUnref data(data_->pdf_stream_.copyToData());
+ data_->pdf_cg_.InitFromData(data->bytes(), data->size());
+ }
+ return data_->pdf_cg_.RenderPage(page_number, context, rect, params);
+}
+#endif
+
+#if defined(OS_CHROMEOS)
+bool PdfMetafileSkia::SaveToFD(const base::FileDescriptor& fd) const {
+ DCHECK_GT(data_->pdf_stream_.getOffset(), 0U);
+
+ if (fd.fd < 0) {
+ DLOG(ERROR) << "Invalid file descriptor!";
+ return false;
+ }
+
+ bool result = true;
+ SkAutoDataUnref data(data_->pdf_stream_.copyToData());
+ if (file_util::WriteFileDescriptor(fd.fd,
+ reinterpret_cast<const char*>(data->data()),
+ GetDataSize()) !=
+ static_cast<int>(GetDataSize())) {
+ DLOG(ERROR) << "Failed to save file with fd " << fd.fd;
+ result = false;
+ }
+
+ if (fd.auto_close) {
+ if (HANDLE_EINTR(close(fd.fd)) < 0) {
+ DPLOG(WARNING) << "close";
+ result = false;
+ }
+ }
+ return result;
+}
+#endif
+
+PdfMetafileSkia::PdfMetafileSkia()
+ : data_(new PdfMetafileSkiaData),
+ page_outstanding_(false) {
+}
+
+PdfMetafileSkia* PdfMetafileSkia::GetMetafileForCurrentPage() {
+ SkPDFDocument pdf_doc(SkPDFDocument::kDraftMode_Flags);
+ SkDynamicMemoryWStream pdf_stream;
+ if (!pdf_doc.appendPage(data_->current_page_.get()))
+ return NULL;
+
+ if (!pdf_doc.emitPDF(&pdf_stream))
+ return NULL;
+
+ SkAutoDataUnref data(pdf_stream.copyToData());
+ if (data->size() == 0)
+ return NULL;
+
+ PdfMetafileSkia* metafile = new PdfMetafileSkia;
+ metafile->InitFromData(data->bytes(),
+ base::checked_numeric_cast<uint32>(data->size()));
+ return metafile;
+}
+
+} // namespace printing
diff --git a/chromium/printing/pdf_metafile_skia.h b/chromium/printing/pdf_metafile_skia.h
new file mode 100644
index 00000000000..33be6d2e0f4
--- /dev/null
+++ b/chromium/printing/pdf_metafile_skia.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PDF_METAFILE_SKIA_H_
+#define PRINTING_PDF_METAFILE_SKIA_H_
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "build/build_config.h"
+#include "printing/metafile.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+namespace printing {
+
+struct PdfMetafileSkiaData;
+
+// This class uses Skia graphics library to generate a PDF document.
+class PRINTING_EXPORT PdfMetafileSkia : public Metafile {
+ public:
+ PdfMetafileSkia();
+ virtual ~PdfMetafileSkia();
+
+ // Metafile methods.
+ virtual bool Init() OVERRIDE;
+ virtual bool InitFromData(const void* src_buffer,
+ uint32 src_buffer_size) OVERRIDE;
+
+ virtual SkDevice* StartPageForVectorCanvas(
+ const gfx::Size& page_size,
+ const gfx::Rect& content_area,
+ const float& scale_factor) OVERRIDE;
+
+ virtual bool StartPage(const gfx::Size& page_size,
+ const gfx::Rect& content_area,
+ const float& scale_factor) OVERRIDE;
+ virtual bool FinishPage() OVERRIDE;
+ virtual bool FinishDocument() OVERRIDE;
+
+ virtual uint32 GetDataSize() const OVERRIDE;
+ virtual bool GetData(void* dst_buffer, uint32 dst_buffer_size) const OVERRIDE;
+
+ virtual bool SaveTo(const base::FilePath& file_path) const OVERRIDE;
+
+ virtual gfx::Rect GetPageBounds(unsigned int page_number) const OVERRIDE;
+ virtual unsigned int GetPageCount() const OVERRIDE;
+
+ virtual gfx::NativeDrawingContext context() const OVERRIDE;
+
+#if defined(OS_WIN)
+ virtual bool Playback(gfx::NativeDrawingContext hdc,
+ const RECT* rect) const OVERRIDE;
+ virtual bool SafePlayback(gfx::NativeDrawingContext hdc) const OVERRIDE;
+ virtual HENHMETAFILE emf() const OVERRIDE;
+#elif defined(OS_MACOSX)
+ virtual bool RenderPage(unsigned int page_number,
+ gfx::NativeDrawingContext context,
+ const CGRect rect,
+ const MacRenderPageParams& params) const OVERRIDE;
+#endif
+
+#if defined(OS_CHROMEOS)
+ virtual bool SaveToFD(const base::FileDescriptor& fd) const OVERRIDE;
+#endif // if defined(OS_CHROMEOS)
+
+ // Return a new metafile containing just the current page in draft mode.
+ PdfMetafileSkia* GetMetafileForCurrentPage();
+
+ private:
+ scoped_ptr<PdfMetafileSkiaData> data_;
+
+ // True when finish page is outstanding for current page.
+ bool page_outstanding_;
+
+ DISALLOW_COPY_AND_ASSIGN(PdfMetafileSkia);
+};
+
+} // namespace printing
+
+#endif // PRINTING_PDF_METAFILE_SKIA_H_
diff --git a/chromium/printing/pdf_render_settings.h b/chromium/printing/pdf_render_settings.h
new file mode 100644
index 00000000000..22784b79ed5
--- /dev/null
+++ b/chromium/printing/pdf_render_settings.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PDF_RENDER_SETTINGS_H_
+#define PRINTING_PDF_RENDER_SETTINGS_H_
+
+#include "base/tuple.h"
+#include "ipc/ipc_param_traits.h"
+#include "printing/printing_export.h"
+#include "ui/gfx/rect.h"
+
+namespace printing {
+
+// Defining PDF rendering settings here as a Tuple as following:
+// gfx::Rect - render area
+// int - render dpi
+// bool - autorotate pages to fit paper
+typedef Tuple3<gfx::Rect, int, bool> PdfRenderSettingsBase;
+
+struct PdfRenderSettings : public PdfRenderSettingsBase {
+ public:
+ PdfRenderSettings() : PdfRenderSettingsBase() {}
+ PdfRenderSettings(gfx::Rect area, int dpi, bool autorotate)
+ : PdfRenderSettingsBase(area, dpi, autorotate) {}
+ ~PdfRenderSettings() {}
+
+ const gfx::Rect& area() const { return a; }
+ int dpi() const { return b; }
+ bool autorotate() const { return c; }
+};
+
+} // namespace printing
+
+namespace IPC {
+template <>
+struct SimilarTypeTraits<printing::PdfRenderSettings> {
+ typedef printing::PdfRenderSettingsBase Type;
+};
+
+} // namespace IPC
+
+#endif // PRINTING_PDF_RENDER_SETTINGS_H_
+
diff --git a/chromium/printing/print_destination_interface.h b/chromium/printing/print_destination_interface.h
new file mode 100644
index 00000000000..715f529d026
--- /dev/null
+++ b/chromium/printing/print_destination_interface.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PRINT_DESTINATION_INTERFACE_H_
+#define PRINTING_PRINT_DESTINATION_INTERFACE_H_
+
+#include "base/memory/ref_counted.h"
+#include "printing/printing_export.h"
+
+namespace printing {
+
+class PrintDestinationInterface
+ : public base::RefCountedThreadSafe<PrintDestinationInterface> {
+ public:
+ // Sets the number of pages to print as soon as it is known.
+ virtual void SetPageCount(int page_count) = 0;
+
+ // Sets the metafile bits for a given page as soon as it is ready.
+ virtual void SetPageContent(int page_number,
+ void* content,
+ size_t content_size) = 0;
+ protected:
+ friend class base::RefCountedThreadSafe<PrintDestinationInterface>;
+ virtual ~PrintDestinationInterface() {}
+};
+
+PRINTING_EXPORT PrintDestinationInterface* CreatePrintDestination();
+
+} // namespace printing
+
+#endif // PRINTING_PRINT_DESTINATION_INTERFACE_H_
diff --git a/chromium/printing/print_destination_none.cc b/chromium/printing/print_destination_none.cc
new file mode 100644
index 00000000000..66b838ef257
--- /dev/null
+++ b/chromium/printing/print_destination_none.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/print_destination_interface.h"
+
+namespace printing {
+
+PrintDestinationInterface* CreatePrintDestination() {
+ return NULL;
+}
+
+} // namespace printing
diff --git a/chromium/printing/print_destination_win.cc b/chromium/printing/print_destination_win.cc
new file mode 100644
index 00000000000..6b98f4c5319
--- /dev/null
+++ b/chromium/printing/print_destination_win.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/print_destination_interface.h"
+
+#include "base/safe_numerics.h"
+#include "base/win/metro.h"
+#include "win8/util/win8_util.h"
+
+namespace printing {
+
+class PrintDestinationWin : public PrintDestinationInterface {
+ public:
+ PrintDestinationWin()
+ : metro_set_print_page_count_(NULL),
+ metro_set_print_page_content_(NULL) {
+ HMODULE metro_module = base::win::GetMetroModule();
+ if (metro_module != NULL) {
+ metro_set_print_page_count_ =
+ reinterpret_cast<MetroSetPrintPageCount>(
+ ::GetProcAddress(metro_module, "MetroSetPrintPageCount"));
+ metro_set_print_page_content_ =
+ reinterpret_cast<MetroSetPrintPageContent>(
+ ::GetProcAddress(metro_module, "MetroSetPrintPageContent"));
+ }
+ }
+ virtual void SetPageCount(int page_count) {
+ if (metro_set_print_page_count_)
+ metro_set_print_page_count_(page_count);
+ }
+
+ virtual void SetPageContent(int page_number,
+ void* content,
+ size_t content_size) {
+ if (metro_set_print_page_content_)
+ metro_set_print_page_content_(page_number - 1, content,
+ base::checked_numeric_cast<UINT32>(content_size));
+ }
+ private:
+ typedef void (*MetroSetPrintPageCount)(INT);
+ typedef void (*MetroSetPrintPageContent)(INT, VOID*, UINT32);
+ MetroSetPrintPageCount metro_set_print_page_count_;
+ MetroSetPrintPageContent metro_set_print_page_content_;
+};
+
+PrintDestinationInterface* CreatePrintDestination() {
+ // We currently only support the Metro print destination.
+ if (win8::IsSingleWindowMetroMode())
+ return new PrintDestinationWin;
+ else
+ return NULL;
+}
+
+} // namespace printing
diff --git a/chromium/printing/print_dialog_gtk_interface.h b/chromium/printing/print_dialog_gtk_interface.h
new file mode 100644
index 00000000000..fc238c76217
--- /dev/null
+++ b/chromium/printing/print_dialog_gtk_interface.h
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PRINT_DIALOG_GTK_INTERFACE_H_
+#define PRINTING_PRINT_DIALOG_GTK_INTERFACE_H_
+
+#include "base/strings/string16.h"
+#include "printing/printing_context_gtk.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace printing {
+
+class Metafile;
+class PrintSettings;
+
+// An interface for GTK printing dialogs. Classes that live outside of
+// printing/ can implement this interface and get threading requirements
+// correct without exposing those requirements to printing/.
+class PrintDialogGtkInterface {
+ public:
+ // Tell the dialog to use the default print setting.
+ virtual void UseDefaultSettings() = 0;
+
+ // Update the dialog to use |job_settings| and |ranges|, where |job_settings|
+ // is a dictionary of settings with possible keys from
+ // printing/print_job_constants.h. Only used when printing without the system
+ // print dialog. E.g. for Print Preview. Returns false on error.
+ virtual bool UpdateSettings(const base::DictionaryValue& job_settings,
+ const PageRanges& ranges,
+ PrintSettings* settings) = 0;
+
+ // Shows the dialog and handles the response with |callback|. Only used when
+ // printing with the native print dialog.
+ virtual void ShowDialog(
+ gfx::NativeView parent_view,
+ bool has_selection,
+ const PrintingContextGtk::PrintSettingsCallback& callback) = 0;
+
+ // Prints the document named |document_name| contained in |metafile|.
+ // Called from the print worker thread. Once called, the
+ // PrintDialogGtkInterface instance should not be reused.
+ virtual void PrintDocument(const Metafile* metafile,
+ const string16& document_name) = 0;
+
+ // Same as AddRef/Release, but with different names since
+ // PrintDialogGtkInterface does not inherit from RefCounted.
+ virtual void AddRefToDialog() = 0;
+ virtual void ReleaseDialog() = 0;
+
+ protected:
+ virtual ~PrintDialogGtkInterface() {}
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINT_DIALOG_GTK_INTERFACE_H_
diff --git a/chromium/printing/print_job_constants.cc b/chromium/printing/print_job_constants.cc
new file mode 100644
index 00000000000..f659aeded3b
--- /dev/null
+++ b/chromium/printing/print_job_constants.cc
@@ -0,0 +1,154 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/print_job_constants.h"
+
+namespace printing {
+
+// True if this is the first preview request.
+const char kIsFirstRequest[] = "isFirstRequest";
+
+// Unique ID sent along every preview request.
+const char kPreviewRequestID[] = "requestID";
+
+// Unique ID to identify a print preview UI.
+const char kPreviewUIID[] = "previewUIID";
+
+// Print using cloud print: true if selected, false if not.
+const char kSettingCloudPrintId[] = "cloudPrintID";
+
+// Print using cloud print dialog: true if selected, false if not.
+const char kSettingCloudPrintDialog[] = "printWithCloudPrint";
+
+// Print job setting 'collate'.
+const char kSettingCollate[] = "collate";
+
+// Print out color: true for color, false for grayscale.
+const char kSettingColor[] = "color";
+
+// Default to color on or not.
+const char kSettingSetColorAsDefault[] = "setColorAsDefault";
+
+// Key that specifies the height of the content area of the page.
+const char kSettingContentHeight[] = "contentHeight";
+
+// Key that specifies the width of the content area of the page.
+const char kSettingContentWidth[] = "contentWidth";
+
+// Number of copies.
+const char kSettingCopies[] = "copies";
+
+// Device name: Unique printer identifier.
+const char kSettingDeviceName[] = "deviceName";
+
+// Print job duplex mode.
+const char kSettingDuplexMode[] = "duplex";
+
+// Option to fit source page contents to printer paper size: true if
+// selected else false.
+const char kSettingFitToPageEnabled[] = "fitToPageEnabled";
+
+// True, when a new set of draft preview data is required.
+const char kSettingGenerateDraftData[] = "generateDraftData";
+
+// Option to print headers and Footers: true if selected, false if not.
+const char kSettingHeaderFooterEnabled[] = "headerFooterEnabled";
+
+// Interstice or gap between different header footer components. Hardcoded to
+// about 0.5cm, match the value in PrintSettings::SetPrinterPrintableArea.
+const float kSettingHeaderFooterInterstice = 14.2f;
+
+// Key that specifies the date of the page that will be printed in the headers
+// and footers.
+const char kSettingHeaderFooterDate[] = "date";
+
+// Key that specifies the title of the page that will be printed in the headers
+// and footers.
+const char kSettingHeaderFooterTitle[] = "title";
+
+// Key that specifies the URL of the page that will be printed in the headers
+// and footers.
+const char kSettingHeaderFooterURL[] = "url";
+
+// Page orientation: true for landscape, false for portrait.
+const char kSettingLandscape[] = "landscape";
+
+// Key that specifies the bottom margin of the page.
+const char kSettingMarginBottom[] = "marginBottom";
+
+// Key that specifies the left margin of the page.
+const char kSettingMarginLeft[] = "marginLeft";
+
+// Key that specifies the right margin of the page.
+const char kSettingMarginRight[] = "marginRight";
+
+// Key that specifies the top margin of the page.
+const char kSettingMarginTop[] = "marginTop";
+
+// Key that specifies the dictionary of custom margins as set by the user.
+const char kSettingMarginsCustom[] = "marginsCustom";
+
+// Key that specifies the type of margins to use. Value is an int from the
+// MarginType enum.
+const char kSettingMarginsType[] = "marginsType";
+
+// Number of pages to print.
+const char kSettingPreviewPageCount[] = "pageCount";
+
+// A page range.
+const char kSettingPageRange[] = "pageRange";
+
+// The first page of a page range. (1-based)
+const char kSettingPageRangeFrom[] = "from";
+
+// The last page of a page range. (1-based)
+const char kSettingPageRangeTo[] = "to";
+
+const char kSettingPreviewModifiable[] = "previewModifiable";
+
+// Keys that specifies the printable area details.
+const char kSettingPrintableAreaX[] = "printableAreaX";
+const char kSettingPrintableAreaY[] = "printableAreaY";
+const char kSettingPrintableAreaWidth[] = "printableAreaWidth";
+const char kSettingPrintableAreaHeight[] = "printableAreaHeight";
+
+// Printer name.
+const char kSettingPrinterName[] = "printerName";
+
+// Print to PDF option: true if selected, false if not.
+const char kSettingPrintToPDF[] = "printToPDF";
+
+// Whether to print CSS backgrounds.
+const char kSettingShouldPrintBackgrounds[] = "shouldPrintBackgrounds";
+
+// Whether to print selection only.
+const char kSettingShouldPrintSelectionOnly[] = "shouldPrintSelectionOnly";
+
+// Indices used to represent first preview page and complete preview document.
+const int FIRST_PAGE_INDEX = 0;
+const int COMPLETE_PREVIEW_DOCUMENT_INDEX = -1;
+
+#if defined(OS_MACOSX)
+const char kSettingOpenPDFInPreview[] = "OpenPDFInPreview";
+#endif
+
+#if defined (USE_CUPS)
+const char kBlack[] = "Black";
+const char kCMYK[] = "CMYK";
+const char kKCMY[] = "KCMY";
+const char kCMY_K[] = "CMY+K";
+const char kCMY[] = "CMY";
+const char kColor[] = "Color";
+const char kGray[] = "Gray";
+const char kGrayscale[] = "Grayscale";
+const char kGreyscale[] = "Greyscale";
+const char kMonochrome[] = "Monochrome";
+const char kNormal[] = "Normal";
+const char kNormalGray[] = "Normal.Gray";
+const char kRGB[] = "RGB";
+const char kRGBA[] = "RGBA";
+const char kRGB16[] = "RGB16";
+#endif
+
+} // namespace printing
diff --git a/chromium/printing/print_job_constants.h b/chromium/printing/print_job_constants.h
new file mode 100644
index 00000000000..b5b31306b03
--- /dev/null
+++ b/chromium/printing/print_job_constants.h
@@ -0,0 +1,136 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PRINT_JOB_CONSTANTS_H_
+#define PRINTING_PRINT_JOB_CONSTANTS_H_
+
+#include "build/build_config.h"
+#include "printing/printing_export.h"
+
+namespace printing {
+
+PRINTING_EXPORT extern const char kIsFirstRequest[];
+PRINTING_EXPORT extern const char kPreviewRequestID[];
+PRINTING_EXPORT extern const char kPreviewUIID[];
+PRINTING_EXPORT extern const char kSettingCloudPrintId[];
+PRINTING_EXPORT extern const char kSettingCloudPrintDialog[];
+PRINTING_EXPORT extern const char kSettingCollate[];
+PRINTING_EXPORT extern const char kSettingColor[];
+PRINTING_EXPORT extern const char kSettingSetColorAsDefault[];
+PRINTING_EXPORT extern const char kSettingContentHeight[];
+PRINTING_EXPORT extern const char kSettingContentWidth[];
+PRINTING_EXPORT extern const char kSettingCopies[];
+PRINTING_EXPORT extern const char kSettingDeviceName[];
+PRINTING_EXPORT extern const char kSettingDuplexMode[];
+PRINTING_EXPORT extern const char kSettingFitToPageEnabled[];
+PRINTING_EXPORT extern const char kSettingGenerateDraftData[];
+PRINTING_EXPORT extern const char kSettingHeaderFooterEnabled[];
+PRINTING_EXPORT extern const float kSettingHeaderFooterInterstice;
+PRINTING_EXPORT extern const char kSettingHeaderFooterDate[];
+PRINTING_EXPORT extern const char kSettingHeaderFooterTitle[];
+PRINTING_EXPORT extern const char kSettingHeaderFooterURL[];
+PRINTING_EXPORT extern const char kSettingLandscape[];
+PRINTING_EXPORT extern const char kSettingMarginBottom[];
+PRINTING_EXPORT extern const char kSettingMarginLeft[];
+PRINTING_EXPORT extern const char kSettingMarginRight[];
+PRINTING_EXPORT extern const char kSettingMarginTop[];
+PRINTING_EXPORT extern const char kSettingMarginsCustom[];
+PRINTING_EXPORT extern const char kSettingMarginsType[];
+PRINTING_EXPORT extern const char kSettingPreviewPageCount[];
+PRINTING_EXPORT extern const char kSettingPageRange[];
+PRINTING_EXPORT extern const char kSettingPageRangeFrom[];
+PRINTING_EXPORT extern const char kSettingPageRangeTo[];
+PRINTING_EXPORT extern const char kSettingPreviewModifiable[];
+PRINTING_EXPORT extern const char kSettingPrintableAreaX[];
+PRINTING_EXPORT extern const char kSettingPrintableAreaY[];
+PRINTING_EXPORT extern const char kSettingPrintableAreaWidth[];
+PRINTING_EXPORT extern const char kSettingPrintableAreaHeight[];
+PRINTING_EXPORT extern const char kSettingPrinterName[];
+PRINTING_EXPORT extern const char kSettingPrintToPDF[];
+PRINTING_EXPORT extern const char kSettingShouldPrintBackgrounds[];
+PRINTING_EXPORT extern const char kSettingShouldPrintSelectionOnly[];
+
+PRINTING_EXPORT extern const int FIRST_PAGE_INDEX;
+PRINTING_EXPORT extern const int COMPLETE_PREVIEW_DOCUMENT_INDEX;
+
+#if defined(OS_MACOSX)
+PRINTING_EXPORT extern const char kSettingOpenPDFInPreview[];
+#endif // defined(OS_MACOSX)
+
+#if defined (USE_CUPS)
+// Printer color models
+PRINTING_EXPORT extern const char kBlack[];
+PRINTING_EXPORT extern const char kCMYK[];
+PRINTING_EXPORT extern const char kKCMY[];
+PRINTING_EXPORT extern const char kCMY_K[];
+PRINTING_EXPORT extern const char kCMY[];
+PRINTING_EXPORT extern const char kColor[];
+PRINTING_EXPORT extern const char kGray[];
+PRINTING_EXPORT extern const char kGrayscale[];
+PRINTING_EXPORT extern const char kGreyscale[];
+PRINTING_EXPORT extern const char kMonochrome[];
+PRINTING_EXPORT extern const char kNormal[];
+PRINTING_EXPORT extern const char kNormalGray[];
+PRINTING_EXPORT extern const char kRGB[];
+PRINTING_EXPORT extern const char kRGBA[];
+PRINTING_EXPORT extern const char kRGB16[];
+#endif
+
+// Print job duplex mode values.
+enum DuplexMode {
+ UNKNOWN_DUPLEX_MODE = -1,
+ SIMPLEX,
+ LONG_EDGE,
+ SHORT_EDGE,
+};
+
+// Specifies the horizontal alignment of the headers and footers.
+enum HorizontalHeaderFooterPosition {
+ LEFT,
+ CENTER,
+ RIGHT
+};
+
+// Specifies the vertical alignment of the Headers and Footers.
+enum VerticalHeaderFooterPosition {
+ TOP,
+ BOTTOM
+};
+
+// Print job color mode values.
+enum ColorModels {
+ UNKNOWN_COLOR_MODEL,
+ GRAY,
+ COLOR,
+ CMYK,
+ CMY,
+ KCMY,
+ CMY_K, // CMY_K represents CMY+K.
+ BLACK,
+ GRAYSCALE,
+ RGB,
+ RGB16,
+ RGBA,
+ COLORMODE_COLOR, // Used in samsung printer ppds.
+ COLORMODE_MONOCHROME, // Used in samsung printer ppds.
+ HP_COLOR_COLOR, // Used in HP color printer ppds.
+ HP_COLOR_BLACK, // Used in HP color printer ppds.
+ PRINTOUTMODE_NORMAL, // Used in foomatic ppds.
+ PRINTOUTMODE_NORMAL_GRAY, // Used in foomatic ppds.
+ PROCESSCOLORMODEL_CMYK, // Used in canon printer ppds.
+ PROCESSCOLORMODEL_GREYSCALE, // Used in canon printer ppds.
+ PROCESSCOLORMODEL_RGB, // Used in canon printer ppds
+};
+
+// What kind of margins to use.
+enum MarginType {
+ DEFAULT_MARGINS, // Default varies depending on headers being enabled or not
+ NO_MARGINS,
+ PRINTABLE_AREA_MARGINS,
+ CUSTOM_MARGINS,
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINT_JOB_CONSTANTS_H_
diff --git a/chromium/printing/print_settings.cc b/chromium/printing/print_settings.cc
new file mode 100644
index 00000000000..e665c170221
--- /dev/null
+++ b/chromium/printing/print_settings.cc
@@ -0,0 +1,252 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/print_settings.h"
+
+#include "base/atomic_sequence_num.h"
+#include "base/logging.h"
+#include "printing/print_job_constants.h"
+#include "printing/units.h"
+
+namespace printing {
+
+#if defined(USE_CUPS)
+void GetColorModelForMode(
+ int color_mode, std::string* color_setting_name, std::string* color_value) {
+#if defined(OS_MACOSX)
+ const char kCUPSColorMode[] = "ColorMode";
+ const char kCUPSColorModel[] = "ColorModel";
+ const char kCUPSPrintoutMode[] = "PrintoutMode";
+ const char kCUPSProcessColorModel[] = "ProcessColorModel";
+#else
+ const char kCUPSColorMode[] = "cups-ColorMode";
+ const char kCUPSColorModel[] = "cups-ColorModel";
+ const char kCUPSPrintoutMode[] = "cups-PrintoutMode";
+ const char kCUPSProcessColorModel[] = "cups-ProcessColorModel";
+#endif // defined(OS_MACOSX)
+
+ color_setting_name->assign(kCUPSColorModel);
+ switch (color_mode) {
+ case COLOR:
+ color_value->assign(kColor);
+ break;
+ case CMYK:
+ color_value->assign(kCMYK);
+ break;
+ case PRINTOUTMODE_NORMAL:
+ color_value->assign(kNormal);
+ color_setting_name->assign(kCUPSPrintoutMode);
+ break;
+ case PRINTOUTMODE_NORMAL_GRAY:
+ color_value->assign(kNormalGray);
+ color_setting_name->assign(kCUPSPrintoutMode);
+ break;
+ case RGB16:
+ color_value->assign(kRGB16);
+ break;
+ case RGBA:
+ color_value->assign(kRGBA);
+ break;
+ case RGB:
+ color_value->assign(kRGB);
+ break;
+ case CMY:
+ color_value->assign(kCMY);
+ break;
+ case CMY_K:
+ color_value->assign(kCMY_K);
+ break;
+ case BLACK:
+ color_value->assign(kBlack);
+ break;
+ case GRAY:
+ color_value->assign(kGray);
+ break;
+ case COLORMODE_COLOR:
+ color_setting_name->assign(kCUPSColorMode);
+ color_value->assign(kColor);
+ break;
+ case COLORMODE_MONOCHROME:
+ color_setting_name->assign(kCUPSColorMode);
+ color_value->assign(kMonochrome);
+ break;
+ case HP_COLOR_COLOR:
+ color_setting_name->assign(kColor);
+ color_value->assign(kColor);
+ break;
+ case HP_COLOR_BLACK:
+ color_setting_name->assign(kColor);
+ color_value->assign(kBlack);
+ break;
+ case PROCESSCOLORMODEL_CMYK:
+ color_setting_name->assign(kCUPSProcessColorModel);
+ color_value->assign(kCMYK);
+ break;
+ case PROCESSCOLORMODEL_GREYSCALE:
+ color_setting_name->assign(kCUPSProcessColorModel);
+ color_value->assign(kGreyscale);
+ break;
+ case PROCESSCOLORMODEL_RGB:
+ color_setting_name->assign(kCUPSProcessColorModel);
+ color_value->assign(kRGB);
+ break;
+ default:
+ color_value->assign(kGrayscale);
+ break;
+ }
+}
+#endif // defined(USE_CUPS)
+
+bool isColorModelSelected(int model) {
+ return (model != GRAY &&
+ model != BLACK &&
+ model != PRINTOUTMODE_NORMAL_GRAY &&
+ model != COLORMODE_MONOCHROME &&
+ model != PROCESSCOLORMODEL_GREYSCALE &&
+ model != HP_COLOR_BLACK);
+}
+
+// Global SequenceNumber used for generating unique cookie values.
+static base::StaticAtomicSequenceNumber cookie_seq;
+
+PrintSettings::PrintSettings()
+ : min_shrink(1.25),
+ max_shrink(2.0),
+ desired_dpi(72),
+ selection_only(false),
+ margin_type(DEFAULT_MARGINS),
+ display_header_footer(false),
+ should_print_backgrounds(false),
+ dpi_(0),
+ landscape_(false),
+ supports_alpha_blend_(true) {
+}
+
+PrintSettings::~PrintSettings() {
+}
+
+void PrintSettings::Clear() {
+ ranges.clear();
+ min_shrink = 1.25;
+ max_shrink = 2.;
+ desired_dpi = 72;
+ selection_only = false;
+ date = string16();
+ title = string16();
+ url = string16();
+ display_header_footer = false;
+ printer_name_.clear();
+ device_name_.clear();
+ page_setup_device_units_.Clear();
+ dpi_ = 0;
+ landscape_ = false;
+ supports_alpha_blend_ = true;
+ should_print_backgrounds = false;
+}
+
+void PrintSettings::SetPrinterPrintableArea(
+ gfx::Size const& physical_size_device_units,
+ gfx::Rect const& printable_area_device_units,
+ int units_per_inch) {
+ int header_footer_text_height = 0;
+ if (display_header_footer) {
+ // Hard-code text_height = 0.5cm = ~1/5 of inch.
+ header_footer_text_height = ConvertUnit(kSettingHeaderFooterInterstice,
+ kPointsPerInch, units_per_inch);
+ }
+
+ PageMargins margins;
+ switch (margin_type) {
+ case DEFAULT_MARGINS: {
+ // Default margins 1.0cm = ~2/5 of an inch.
+ int margin_printer_units = ConvertUnit(1000, kHundrethsMMPerInch,
+ units_per_inch);
+ margins.header = header_footer_text_height;
+ margins.footer = header_footer_text_height;
+ margins.top = margin_printer_units;
+ margins.bottom = margin_printer_units;
+ margins.left = margin_printer_units;
+ margins.right = margin_printer_units;
+ break;
+ }
+ case NO_MARGINS:
+ case PRINTABLE_AREA_MARGINS: {
+ margins.header = 0;
+ margins.footer = 0;
+ margins.top = 0;
+ margins.bottom = 0;
+ margins.left = 0;
+ margins.right = 0;
+ break;
+ }
+ case CUSTOM_MARGINS: {
+ margins.header = 0;
+ margins.footer = 0;
+ margins.top = ConvertUnitDouble(
+ requested_custom_margins_in_points_.top,
+ kPointsPerInch,
+ units_per_inch);
+ margins.bottom = ConvertUnitDouble(
+ requested_custom_margins_in_points_.bottom,
+ kPointsPerInch,
+ units_per_inch);
+ margins.left = ConvertUnitDouble(
+ requested_custom_margins_in_points_.left,
+ kPointsPerInch,
+ units_per_inch);
+ margins.right = ConvertUnitDouble(
+ requested_custom_margins_in_points_.right,
+ kPointsPerInch,
+ units_per_inch);
+ break;
+ }
+ default: {
+ NOTREACHED();
+ }
+ }
+
+ if (margin_type == DEFAULT_MARGINS || margin_type == PRINTABLE_AREA_MARGINS)
+ page_setup_device_units_.SetRequestedMargins(margins);
+ else
+ page_setup_device_units_.ForceRequestedMargins(margins);
+
+ page_setup_device_units_.Init(physical_size_device_units,
+ printable_area_device_units,
+ header_footer_text_height);
+}
+
+void PrintSettings::SetCustomMargins(
+ const PageMargins& requested_margins_in_points) {
+ requested_custom_margins_in_points_ = requested_margins_in_points;
+ margin_type = CUSTOM_MARGINS;
+}
+
+bool PrintSettings::Equals(const PrintSettings& rhs) const {
+ // Do not test the display device name (printer_name_) for equality since it
+ // may sometimes be chopped off at 30 chars. As long as device_name is the
+ // same, that's fine.
+ return ranges == rhs.ranges &&
+ min_shrink == rhs.min_shrink &&
+ max_shrink == rhs.max_shrink &&
+ desired_dpi == rhs.desired_dpi &&
+ device_name_ == rhs.device_name_ &&
+ page_setup_device_units_.Equals(rhs.page_setup_device_units_) &&
+ dpi_ == rhs.dpi_ &&
+ landscape_ == rhs.landscape_ &&
+ should_print_backgrounds == rhs.should_print_backgrounds;
+}
+
+int PrintSettings::NewCookie() {
+ // A cookie of 0 is used to mark a document as unassigned, count from 1.
+ return cookie_seq.GetNext() + 1;
+}
+
+void PrintSettings::SetOrientation(bool landscape) {
+ if (landscape_ != landscape) {
+ landscape_ = landscape;
+ page_setup_device_units_.FlipOrientation();
+ }
+}
+
+} // namespace printing
diff --git a/chromium/printing/print_settings.h b/chromium/printing/print_settings.h
new file mode 100644
index 00000000000..5728bf7128f
--- /dev/null
+++ b/chromium/printing/print_settings.h
@@ -0,0 +1,150 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PRINT_SETTINGS_H_
+#define PRINTING_PRINT_SETTINGS_H_
+
+#include <string>
+
+#include "base/strings/string16.h"
+#include "printing/page_range.h"
+#include "printing/page_setup.h"
+#include "printing/print_job_constants.h"
+#include "printing/printing_export.h"
+#include "ui/gfx/rect.h"
+
+namespace printing {
+
+// Returns true if color model is selected.
+PRINTING_EXPORT bool isColorModelSelected(int model);
+
+#if defined(USE_CUPS)
+// Get the color model setting name and value for the |color_mode|.
+PRINTING_EXPORT void GetColorModelForMode(int color_mode,
+ std::string* color_setting_name,
+ std::string* color_value);
+#endif
+
+// OS-independent print settings.
+class PRINTING_EXPORT PrintSettings {
+ public:
+ PrintSettings();
+ ~PrintSettings();
+
+ // Reinitialize the settings to the default values.
+ void Clear();
+
+ // Set printer printable area in in device units.
+ void SetPrinterPrintableArea(gfx::Size const& physical_size_device_units,
+ gfx::Rect const& printable_area_device_units,
+ int units_per_inch);
+
+ void SetCustomMargins(const PageMargins& requested_margins_in_points);
+
+ // Equality operator.
+ // NOTE: printer_name is NOT tested for equality since it doesn't affect the
+ // output.
+ bool Equals(const PrintSettings& rhs) const;
+
+ void set_landscape(bool landscape) { landscape_ = landscape; }
+ void set_printer_name(const string16& printer_name) {
+ printer_name_ = printer_name;
+ }
+ const string16& printer_name() const { return printer_name_; }
+ void set_device_name(const string16& device_name) {
+ device_name_ = device_name;
+ }
+ const string16& device_name() const { return device_name_; }
+ void set_dpi(int dpi) { dpi_ = dpi; }
+ int dpi() const { return dpi_; }
+ void set_supports_alpha_blend(bool supports_alpha_blend) {
+ supports_alpha_blend_ = supports_alpha_blend;
+ }
+ bool supports_alpha_blend() const { return supports_alpha_blend_; }
+ const PageSetup& page_setup_device_units() const {
+ return page_setup_device_units_;
+ }
+ int device_units_per_inch() const {
+#if defined(OS_MACOSX)
+ return 72;
+#else // defined(OS_MACOSX)
+ return dpi();
+#endif // defined(OS_MACOSX)
+ }
+
+ // Multi-page printing. Each PageRange describes a from-to page combination.
+ // This permits printing selected pages only.
+ PageRanges ranges;
+
+ // By imaging to a width a little wider than the available pixels, thin pages
+ // will be scaled down a little, matching the way they print in IE and Camino.
+ // This lets them use fewer sheets than they would otherwise, which is
+ // presumably why other browsers do this. Wide pages will be scaled down more
+ // than this.
+ double min_shrink;
+
+ // This number determines how small we are willing to reduce the page content
+ // in order to accommodate the widest line. If the page would have to be
+ // reduced smaller to make the widest line fit, we just clip instead (this
+ // behavior matches MacIE and Mozilla, at least)
+ double max_shrink;
+
+ // Desired visible dots per inch rendering for output. Printing should be
+ // scaled to ScreenDpi/dpix*desired_dpi.
+ int desired_dpi;
+
+ // Indicates if the user only wants to print the current selection.
+ bool selection_only;
+
+ // Indicates what kind of margins should be applied to the printable area.
+ MarginType margin_type;
+
+ // Cookie generator. It is used to initialize PrintedDocument with its
+ // associated PrintSettings, to be sure that each generated PrintedPage is
+ // correctly associated with its corresponding PrintedDocument.
+ static int NewCookie();
+
+ // Updates the orientation and flip the page if needed.
+ void SetOrientation(bool landscape);
+
+ // Strings to be printed as headers and footers if requested by the user.
+ string16 date;
+ string16 title;
+ string16 url;
+
+ // True if the user wants headers and footers to be displayed.
+ bool display_header_footer;
+
+ // True if the user wants to print CSS backgrounds.
+ bool should_print_backgrounds;
+
+ private:
+ //////////////////////////////////////////////////////////////////////////////
+ // Settings that can't be changed without side-effects.
+
+ // Printer name as shown to the user.
+ string16 printer_name_;
+
+ // Printer device name as opened by the OS.
+ string16 device_name_;
+
+ // Page setup in device units.
+ PageSetup page_setup_device_units_;
+
+ // Printer's device effective dots per inch in both axis.
+ int dpi_;
+
+ // Is the orientation landscape or portrait.
+ bool landscape_;
+
+ // True if this printer supports AlphaBlend.
+ bool supports_alpha_blend_;
+
+ // If margin type is custom, this is what was requested.
+ PageMargins requested_custom_margins_in_points_;
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINT_SETTINGS_H_
diff --git a/chromium/printing/print_settings_initializer.cc b/chromium/printing/print_settings_initializer.cc
new file mode 100644
index 00000000000..80fa6366d57
--- /dev/null
+++ b/chromium/printing/print_settings_initializer.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/print_settings_initializer.h"
+
+#include <algorithm>
+#include <cmath>
+#include <string>
+
+#include "base/i18n/time_formatting.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "printing/print_job_constants.h"
+#include "printing/print_settings.h"
+#include "printing/units.h"
+#include "ui/base/text/text_elider.h"
+#include "url/gurl.h"
+
+using base::DictionaryValue;
+
+namespace printing {
+
+void PrintSettingsInitializer::InitHeaderFooterStrings(
+ const DictionaryValue& job_settings,
+ PrintSettings* print_settings) {
+ if (!job_settings.GetBoolean(kSettingHeaderFooterEnabled,
+ &print_settings->display_header_footer)) {
+ NOTREACHED();
+ }
+ if (!print_settings->display_header_footer)
+ return;
+
+ string16 date = base::TimeFormatShortDateNumeric(base::Time::Now());
+ string16 title;
+ string16 url;
+ if (!job_settings.GetString(kSettingHeaderFooterTitle, &title) ||
+ !job_settings.GetString(kSettingHeaderFooterURL, &url)) {
+ NOTREACHED();
+ }
+
+ print_settings->date = date;
+ print_settings->title = title;
+ print_settings->url = ui::ElideUrl(GURL(url), gfx::Font(), 0, std::string());
+}
+
+} // namespace printing
diff --git a/chromium/printing/print_settings_initializer.h b/chromium/printing/print_settings_initializer.h
new file mode 100644
index 00000000000..4897be87e18
--- /dev/null
+++ b/chromium/printing/print_settings_initializer.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PRINT_SETTINGS_INITIALIZER_H_
+#define PRINTING_PRINT_SETTINGS_INITIALIZER_H_
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "printing/printing_export.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace printing {
+
+class PrintSettings;
+
+// Initializes the header footer strings in the PrintSettings object from the
+// provided |job_settings|.
+class PRINTING_EXPORT PrintSettingsInitializer {
+ public:
+ static void InitHeaderFooterStrings(
+ const base::DictionaryValue& job_settings,
+ PrintSettings* print_settings);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(PrintSettingsInitializer);
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINT_SETTINGS_INITIALIZER_H_
+
diff --git a/chromium/printing/print_settings_initializer_gtk.cc b/chromium/printing/print_settings_initializer_gtk.cc
new file mode 100644
index 00000000000..d4f21ed1067
--- /dev/null
+++ b/chromium/printing/print_settings_initializer_gtk.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/print_settings_initializer_gtk.h"
+
+#include <gtk/gtk.h>
+#include <gtk/gtkunixprint.h>
+
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "printing/print_settings.h"
+#include "printing/units.h"
+
+namespace printing {
+
+// static
+void PrintSettingsInitializerGtk::InitPrintSettings(
+ GtkPrintSettings* settings,
+ GtkPageSetup* page_setup,
+ const PageRanges& new_ranges,
+ bool print_selection_only,
+ PrintSettings* print_settings) {
+ DCHECK(settings);
+ DCHECK(page_setup);
+ DCHECK(print_settings);
+
+ string16 name(UTF8ToUTF16(static_cast<const char*>(
+ gtk_print_settings_get_printer(settings))));
+ print_settings->set_printer_name(name);
+ print_settings->set_device_name(print_settings->printer_name());
+ print_settings->ranges = new_ranges;
+ print_settings->selection_only = print_selection_only;
+
+ gfx::Size physical_size_device_units;
+ gfx::Rect printable_area_device_units;
+ int dpi = gtk_print_settings_get_resolution(settings);
+ if (dpi) {
+ // Initialize page_setup_device_units_.
+ physical_size_device_units.SetSize(
+ gtk_page_setup_get_paper_width(page_setup, GTK_UNIT_INCH) * dpi,
+ gtk_page_setup_get_paper_height(page_setup, GTK_UNIT_INCH) * dpi);
+ printable_area_device_units.SetRect(
+ gtk_page_setup_get_left_margin(page_setup, GTK_UNIT_INCH) * dpi,
+ gtk_page_setup_get_top_margin(page_setup, GTK_UNIT_INCH) * dpi,
+ gtk_page_setup_get_page_width(page_setup, GTK_UNIT_INCH) * dpi,
+ gtk_page_setup_get_page_height(page_setup, GTK_UNIT_INCH) * dpi);
+ } else {
+ // Use default values if we cannot get valid values from the print dialog.
+ dpi = kPixelsPerInch;
+ double page_width_in_pixel = 8.5 * dpi;
+ double page_height_in_pixel = 11.0 * dpi;
+ physical_size_device_units.SetSize(
+ static_cast<int>(page_width_in_pixel),
+ static_cast<int>(page_height_in_pixel));
+ printable_area_device_units.SetRect(
+ static_cast<int>(kLeftMarginInInch * dpi),
+ static_cast<int>(kTopMarginInInch * dpi),
+ page_width_in_pixel - (kLeftMarginInInch + kRightMarginInInch) * dpi,
+ page_height_in_pixel - (kTopMarginInInch + kBottomMarginInInch) * dpi);
+ }
+
+ print_settings->set_dpi(dpi);
+ print_settings->SetPrinterPrintableArea(physical_size_device_units,
+ printable_area_device_units,
+ dpi);
+
+ // Note: With the normal GTK print dialog, when the user selects the landscape
+ // orientation, all that does is change the paper size. Which seems to be
+ // enough to render the right output and send it to the printer.
+ // The orientation value stays as portrait and does not actually affect
+ // printing.
+ // Thus this is only useful in print preview mode, where we manually set the
+ // orientation and change the paper size ourselves.
+ GtkPageOrientation orientation = gtk_print_settings_get_orientation(settings);
+ print_settings->SetOrientation(orientation == GTK_PAGE_ORIENTATION_LANDSCAPE);
+}
+
+const double PrintSettingsInitializerGtk::kTopMarginInInch = 0.25;
+const double PrintSettingsInitializerGtk::kBottomMarginInInch = 0.56;
+const double PrintSettingsInitializerGtk::kLeftMarginInInch = 0.25;
+const double PrintSettingsInitializerGtk::kRightMarginInInch = 0.25;
+
+} // namespace printing
diff --git a/chromium/printing/print_settings_initializer_gtk.h b/chromium/printing/print_settings_initializer_gtk.h
new file mode 100644
index 00000000000..5807500d9a0
--- /dev/null
+++ b/chromium/printing/print_settings_initializer_gtk.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PRINT_SETTINGS_INITIALIZER_GTK_H_
+#define PRINTING_PRINT_SETTINGS_INITIALIZER_GTK_H_
+
+#include "base/logging.h"
+#include "printing/page_range.h"
+
+typedef struct _GtkPrintSettings GtkPrintSettings;
+typedef struct _GtkPageSetup GtkPageSetup;
+
+namespace printing {
+
+class PrintSettings;
+
+// Initializes a PrintSettings object from the provided Gtk printer objects.
+class PRINTING_EXPORT PrintSettingsInitializerGtk {
+ public:
+ static void InitPrintSettings(GtkPrintSettings* settings,
+ GtkPageSetup* page_setup,
+ const PageRanges& new_ranges,
+ bool print_selection_only,
+ PrintSettings* print_settings);
+
+ // The default margins, in points. These values are based on 72 dpi,
+ // with 0.25 margins on top, left, and right, and 0.56 on bottom.
+ static const double kTopMarginInInch;
+ static const double kRightMarginInInch;
+ static const double kBottomMarginInInch;
+ static const double kLeftMarginInInch;
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(PrintSettingsInitializerGtk);
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINT_SETTINGS_INITIALIZER_GTK_H_
diff --git a/chromium/printing/print_settings_initializer_mac.cc b/chromium/printing/print_settings_initializer_mac.cc
new file mode 100644
index 00000000000..fd665989a59
--- /dev/null
+++ b/chromium/printing/print_settings_initializer_mac.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/print_settings_initializer_mac.h"
+
+#include "base/strings/sys_string_conversions.h"
+#include "printing/print_settings.h"
+
+namespace printing {
+
+// static
+void PrintSettingsInitializerMac::InitPrintSettings(
+ PMPrinter printer,
+ PMPageFormat page_format,
+ const PageRanges& new_ranges,
+ bool print_selection_only,
+ PrintSettings* print_settings) {
+ DCHECK(print_settings);
+
+ print_settings->set_printer_name(
+ base::SysCFStringRefToUTF16(PMPrinterGetName(printer)));
+ print_settings->set_device_name(
+ base::SysCFStringRefToUTF16(PMPrinterGetID(printer)));
+ print_settings->ranges = new_ranges;
+
+ PMOrientation orientation = kPMPortrait;
+ PMGetOrientation(page_format, &orientation);
+ print_settings->set_landscape(orientation == kPMLandscape);
+ print_settings->selection_only = print_selection_only;
+
+ UInt32 resolution_count = 0;
+ PMResolution best_resolution = { 72.0, 72.0 };
+ OSStatus status = PMPrinterGetPrinterResolutionCount(printer,
+ &resolution_count);
+ if (status == noErr) {
+ // Resolution indexes are 1-based.
+ for (uint32 i = 1; i <= resolution_count; ++i) {
+ PMResolution resolution;
+ PMPrinterGetIndexedPrinterResolution(printer, i, &resolution);
+ if (resolution.hRes > best_resolution.hRes)
+ best_resolution = resolution;
+ }
+ }
+ int dpi = best_resolution.hRes;
+ print_settings->set_dpi(dpi);
+
+ DCHECK_EQ(dpi, best_resolution.vRes);
+
+ // Get printable area and paper rects (in points)
+ PMRect page_rect, paper_rect;
+ PMGetAdjustedPageRect(page_format, &page_rect);
+ PMGetAdjustedPaperRect(page_format, &paper_rect);
+
+ // Device units are in points. Units per inch is 72.
+ gfx::Size physical_size_device_units(
+ (paper_rect.right - paper_rect.left),
+ (paper_rect.bottom - paper_rect.top));
+ gfx::Rect printable_area_device_units(
+ (page_rect.left - paper_rect.left),
+ (page_rect.top - paper_rect.top),
+ (page_rect.right - page_rect.left),
+ (page_rect.bottom - page_rect.top));
+
+ print_settings->SetPrinterPrintableArea(physical_size_device_units,
+ printable_area_device_units,
+ 72);
+}
+
+} // namespace printing
diff --git a/chromium/printing/print_settings_initializer_mac.h b/chromium/printing/print_settings_initializer_mac.h
new file mode 100644
index 00000000000..2b7925189f2
--- /dev/null
+++ b/chromium/printing/print_settings_initializer_mac.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PRINTING_SETTINGS_INITIALIZER_MAC_H_
+#define PRINTING_PRINTING_SETTINGS_INITIALIZER_MAC_H_
+
+#import <ApplicationServices/ApplicationServices.h>
+
+#include "base/logging.h"
+#include "printing/page_range.h"
+
+namespace printing {
+
+class PrintSettings;
+
+// Initializes a PrintSettings object from the provided device context.
+class PRINTING_EXPORT PrintSettingsInitializerMac {
+ public:
+ static void InitPrintSettings(PMPrinter printer,
+ PMPageFormat page_format,
+ const PageRanges& new_ranges,
+ bool print_selection_only,
+ PrintSettings* print_settings);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(PrintSettingsInitializerMac);
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINTING_SETTINGS_INITIALIZER_MAC_H_
diff --git a/chromium/printing/print_settings_initializer_win.cc b/chromium/printing/print_settings_initializer_win.cc
new file mode 100644
index 00000000000..0ec96d0ab62
--- /dev/null
+++ b/chromium/printing/print_settings_initializer_win.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/print_settings_initializer_win.h"
+
+#include <windows.h>
+
+#include "printing/print_settings.h"
+
+namespace printing {
+
+// static
+void PrintSettingsInitializerWin::InitPrintSettings(
+ HDC hdc,
+ const DEVMODE& dev_mode,
+ const PageRanges& new_ranges,
+ const std::wstring& new_device_name,
+ bool print_selection_only,
+ PrintSettings* print_settings) {
+ DCHECK(hdc);
+ DCHECK(print_settings);
+
+ print_settings->set_printer_name(dev_mode.dmDeviceName);
+ print_settings->set_device_name(new_device_name);
+ print_settings->ranges = const_cast<PageRanges&>(new_ranges);
+ print_settings->set_landscape(dev_mode.dmOrientation == DMORIENT_LANDSCAPE);
+ print_settings->selection_only = print_selection_only;
+
+ int dpi = GetDeviceCaps(hdc, LOGPIXELSX);
+ print_settings->set_dpi(dpi);
+ const int kAlphaCaps = SB_CONST_ALPHA | SB_PIXEL_ALPHA;
+ print_settings->set_supports_alpha_blend(
+ (GetDeviceCaps(hdc, SHADEBLENDCAPS) & kAlphaCaps) == kAlphaCaps);
+ // No printer device is known to advertise different dpi in X and Y axis; even
+ // the fax device using the 200x100 dpi setting. It's ought to break so many
+ // applications that it's not even needed to care about. WebKit doesn't
+ // support different dpi settings in X and Y axis.
+ DCHECK_EQ(dpi, GetDeviceCaps(hdc, LOGPIXELSY));
+
+ DCHECK_EQ(GetDeviceCaps(hdc, SCALINGFACTORX), 0);
+ DCHECK_EQ(GetDeviceCaps(hdc, SCALINGFACTORY), 0);
+
+ // Initialize page_setup_device_units_.
+ gfx::Size physical_size_device_units(GetDeviceCaps(hdc, PHYSICALWIDTH),
+ GetDeviceCaps(hdc, PHYSICALHEIGHT));
+ gfx::Rect printable_area_device_units(GetDeviceCaps(hdc, PHYSICALOFFSETX),
+ GetDeviceCaps(hdc, PHYSICALOFFSETY),
+ GetDeviceCaps(hdc, HORZRES),
+ GetDeviceCaps(hdc, VERTRES));
+
+ // Sanity check the printable_area: we've seen crashes caused by a printable
+ // area rect of 0, 0, 0, 0, so it seems some drivers don't set it.
+ if (printable_area_device_units.IsEmpty() ||
+ !gfx::Rect(physical_size_device_units).Contains(
+ printable_area_device_units)) {
+ printable_area_device_units = gfx::Rect(physical_size_device_units);
+ }
+
+ print_settings->SetPrinterPrintableArea(physical_size_device_units,
+ printable_area_device_units,
+ dpi);
+}
+
+} // namespace printing
diff --git a/chromium/printing/print_settings_initializer_win.h b/chromium/printing/print_settings_initializer_win.h
new file mode 100644
index 00000000000..720abae7f79
--- /dev/null
+++ b/chromium/printing/print_settings_initializer_win.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PRINTING_SETTINGS_INITIALIZER_WIN_H_
+#define PRINTING_PRINTING_SETTINGS_INITIALIZER_WIN_H_
+
+#include <string>
+
+#include "base/logging.h"
+#include "printing/page_range.h"
+
+typedef struct HDC__* HDC;
+typedef struct _devicemodeW DEVMODE;
+
+namespace printing {
+
+class PrintSettings;
+
+// Initializes a PrintSettings object from the provided device context.
+class PRINTING_EXPORT PrintSettingsInitializerWin {
+ public:
+ static void InitPrintSettings(HDC hdc,
+ const DEVMODE& dev_mode,
+ const PageRanges& new_ranges,
+ const std::wstring& new_device_name,
+ bool print_selection_only,
+ PrintSettings* print_settings);
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(PrintSettingsInitializerWin);
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINTING_SETTINGS_INITIALIZER_WIN_H_
diff --git a/chromium/printing/printed_document.cc b/chromium/printing/printed_document.cc
new file mode 100644
index 00000000000..6e912a11be9
--- /dev/null
+++ b/chromium/printing/printed_document.cc
@@ -0,0 +1,235 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/printed_document.h"
+
+#include <algorithm>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/i18n/file_util_icu.h"
+#include "base/i18n/time_formatting.h"
+#include "base/lazy_instance.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "printing/page_number.h"
+#include "printing/printed_page.h"
+#include "printing/printed_pages_source.h"
+#include "printing/units.h"
+#include "skia/ext/platform_device.h"
+#include "ui/base/text/text_elider.h"
+#include "ui/gfx/font.h"
+
+namespace {
+
+struct PrintDebugDumpPath {
+ PrintDebugDumpPath()
+ : enabled(false) {
+ }
+
+ bool enabled;
+ base::FilePath debug_dump_path;
+};
+
+base::LazyInstance<PrintDebugDumpPath> g_debug_dump_info =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+namespace printing {
+
+PrintedDocument::PrintedDocument(const PrintSettings& settings,
+ PrintedPagesSource* source,
+ int cookie)
+ : mutable_(source),
+ immutable_(settings, source, cookie) {
+
+ // Records the expected page count if a range is setup.
+ if (!settings.ranges.empty()) {
+ // If there is a range, set the number of page
+ for (unsigned i = 0; i < settings.ranges.size(); ++i) {
+ const PageRange& range = settings.ranges[i];
+ mutable_.expected_page_count_ += range.to - range.from + 1;
+ }
+ }
+}
+
+PrintedDocument::~PrintedDocument() {
+}
+
+void PrintedDocument::SetPage(int page_number,
+ Metafile* metafile,
+ double shrink,
+ const gfx::Size& paper_size,
+ const gfx::Rect& page_rect) {
+ // Notice the page_number + 1, the reason is that this is the value that will
+ // be shown. Users dislike 0-based counting.
+ scoped_refptr<PrintedPage> page(
+ new PrintedPage(page_number + 1,
+ metafile,
+ paper_size,
+ page_rect,
+ shrink));
+ {
+ base::AutoLock lock(lock_);
+ mutable_.pages_[page_number] = page;
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ if (page_number < mutable_.first_page)
+ mutable_.first_page = page_number;
+#endif
+ }
+ DebugDump(*page.get());
+}
+
+bool PrintedDocument::GetPage(int page_number,
+ scoped_refptr<PrintedPage>* page) {
+ base::AutoLock lock(lock_);
+ PrintedPages::const_iterator itr = mutable_.pages_.find(page_number);
+ if (itr != mutable_.pages_.end()) {
+ if (itr->second.get()) {
+ *page = itr->second;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PrintedDocument::IsComplete() const {
+ base::AutoLock lock(lock_);
+ if (!mutable_.page_count_)
+ return false;
+ PageNumber page(immutable_.settings_, mutable_.page_count_);
+ if (page == PageNumber::npos())
+ return false;
+
+ for (; page != PageNumber::npos(); ++page) {
+#if defined(OS_WIN) || defined(OS_MACOSX)
+ const bool metafile_must_be_valid = true;
+#elif defined(OS_POSIX)
+ const bool metafile_must_be_valid = (page.ToInt() == mutable_.first_page);
+#endif
+ PrintedPages::const_iterator itr = mutable_.pages_.find(page.ToInt());
+ if (itr == mutable_.pages_.end() || !itr->second.get())
+ return false;
+ if (metafile_must_be_valid && !itr->second->metafile())
+ return false;
+ }
+ return true;
+}
+
+void PrintedDocument::DisconnectSource() {
+ base::AutoLock lock(lock_);
+ mutable_.source_ = NULL;
+}
+
+uint32 PrintedDocument::MemoryUsage() const {
+ std::vector< scoped_refptr<PrintedPage> > pages_copy;
+ {
+ base::AutoLock lock(lock_);
+ pages_copy.reserve(mutable_.pages_.size());
+ PrintedPages::const_iterator end = mutable_.pages_.end();
+ for (PrintedPages::const_iterator itr = mutable_.pages_.begin();
+ itr != end; ++itr) {
+ if (itr->second.get()) {
+ pages_copy.push_back(itr->second);
+ }
+ }
+ }
+ uint32 total = 0;
+ for (size_t i = 0; i < pages_copy.size(); ++i) {
+ total += pages_copy[i]->metafile()->GetDataSize();
+ }
+ return total;
+}
+
+void PrintedDocument::set_page_count(int max_page) {
+ base::AutoLock lock(lock_);
+ DCHECK_EQ(0, mutable_.page_count_);
+ mutable_.page_count_ = max_page;
+ if (immutable_.settings_.ranges.empty()) {
+ mutable_.expected_page_count_ = max_page;
+ } else {
+ // If there is a range, don't bother since expected_page_count_ is already
+ // initialized.
+ DCHECK_NE(mutable_.expected_page_count_, 0);
+ }
+}
+
+int PrintedDocument::page_count() const {
+ base::AutoLock lock(lock_);
+ return mutable_.page_count_;
+}
+
+int PrintedDocument::expected_page_count() const {
+ base::AutoLock lock(lock_);
+ return mutable_.expected_page_count_;
+}
+
+void PrintedDocument::DebugDump(const PrintedPage& page) {
+ if (!g_debug_dump_info.Get().enabled)
+ return;
+
+ string16 filename;
+ filename += name();
+ filename += ASCIIToUTF16("_");
+ filename += ASCIIToUTF16(base::StringPrintf("%02d", page.page_number()));
+#if defined(OS_WIN)
+ filename += ASCIIToUTF16("_.emf");
+ page.metafile()->SaveTo(
+ g_debug_dump_info.Get().debug_dump_path.Append(filename));
+#else // OS_WIN
+ filename += ASCIIToUTF16("_.pdf");
+ page.metafile()->SaveTo(
+ g_debug_dump_info.Get().debug_dump_path.Append(UTF16ToUTF8(filename)));
+#endif // OS_WIN
+}
+
+void PrintedDocument::set_debug_dump_path(
+ const base::FilePath& debug_dump_path) {
+ g_debug_dump_info.Get().enabled = !debug_dump_path.empty();
+ g_debug_dump_info.Get().debug_dump_path = debug_dump_path;
+}
+
+const base::FilePath& PrintedDocument::debug_dump_path() {
+ return g_debug_dump_info.Get().debug_dump_path;
+}
+
+PrintedDocument::Mutable::Mutable(PrintedPagesSource* source)
+ : source_(source),
+ expected_page_count_(0),
+ page_count_(0) {
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ first_page = INT_MAX;
+#endif
+}
+
+PrintedDocument::Mutable::~Mutable() {
+}
+
+PrintedDocument::Immutable::Immutable(const PrintSettings& settings,
+ PrintedPagesSource* source,
+ int cookie)
+ : settings_(settings),
+ source_message_loop_(base::MessageLoop::current()),
+ name_(source->RenderSourceName()),
+ cookie_(cookie) {
+}
+
+PrintedDocument::Immutable::~Immutable() {
+}
+
+#if defined(OS_POSIX) && defined(USE_AURA)
+// This function is not used on aura linux/chromeos.
+void PrintedDocument::RenderPrintedPage(const PrintedPage& page,
+ PrintingContext* context) const {
+}
+#endif
+
+} // namespace printing
diff --git a/chromium/printing/printed_document.h b/chromium/printing/printed_document.h
new file mode 100644
index 00000000000..4ebcfbf5bc6
--- /dev/null
+++ b/chromium/printing/printed_document.h
@@ -0,0 +1,180 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PRINTED_DOCUMENT_H_
+#define PRINTING_PRINTED_DOCUMENT_H_
+
+#include <map>
+
+#include "base/memory/ref_counted.h"
+#include "base/strings/string16.h"
+#include "base/synchronization/lock.h"
+#include "printing/print_settings.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace base {
+class FilePath;
+class MessageLoop;
+}
+
+namespace printing {
+
+class Metafile;
+class PrintedPage;
+class PrintedPagesSource;
+class PrintingContext;
+
+// A collection of rendered pages. The settings are immutable. If the print
+// settings are changed, a new PrintedDocument must be created.
+// Warning: May be accessed from many threads at the same time. Only one thread
+// will have write access. Sensible functions are protected by a lock.
+// Warning: Once a page is loaded, it cannot be replaced. Pages may be discarded
+// under low memory conditions.
+class PRINTING_EXPORT PrintedDocument
+ : public base::RefCountedThreadSafe<PrintedDocument> {
+ public:
+ // The cookie shall be unique and has a specific relationship with its
+ // originating source and settings.
+ PrintedDocument(const PrintSettings& settings,
+ PrintedPagesSource* source,
+ int cookie);
+
+ // Sets a page's data. 0-based. Takes metafile ownership.
+ // Note: locks for a short amount of time.
+ void SetPage(int page_number, Metafile* metafile, double shrink,
+ const gfx::Size& paper_size, const gfx::Rect& page_rect);
+
+ // Retrieves a page. If the page is not available right now, it
+ // requests to have this page be rendered and returns false.
+ // Note: locks for a short amount of time.
+ bool GetPage(int page_number, scoped_refptr<PrintedPage>* page);
+
+ // Draws the page in the context.
+ // Note: locks for a short amount of time in debug only.
+#if defined(OS_WIN) || defined(OS_MACOSX) && !defined(USE_AURA)
+ void RenderPrintedPage(const PrintedPage& page,
+ gfx::NativeDrawingContext context) const;
+#elif defined(OS_POSIX)
+ void RenderPrintedPage(const PrintedPage& page,
+ PrintingContext* context) const;
+#endif
+
+ // Returns true if all the necessary pages for the settings are already
+ // rendered.
+ // Note: locks while parsing the whole tree.
+ bool IsComplete() const;
+
+ // Disconnects the PrintedPage source (PrintedPagesSource). It is done when
+ // the source is being destroyed.
+ void DisconnectSource();
+
+ // Retrieves the current memory usage of the renderer pages.
+ // Note: locks for a short amount of time.
+ uint32 MemoryUsage() const;
+
+ // Sets the number of pages in the document to be rendered. Can only be set
+ // once.
+ // Note: locks for a short amount of time.
+ void set_page_count(int max_page);
+
+ // Number of pages in the document. Used for headers/footers.
+ // Note: locks for a short amount of time.
+ int page_count() const;
+
+ // Returns the number of expected pages to be rendered. It is a non-linear
+ // series if settings().ranges is not empty. It is the same value as
+ // document_page_count() otherwise.
+ // Note: locks for a short amount of time.
+ int expected_page_count() const;
+
+ // Getters. All these items are immutable hence thread-safe.
+ const PrintSettings& settings() const { return immutable_.settings_; }
+ const string16& name() const { return immutable_.name_; }
+ int cookie() const { return immutable_.cookie_; }
+
+ // Sets a path where to dump printing output files for debugging. If never set
+ // no files are generated.
+ static void set_debug_dump_path(const base::FilePath& debug_dump_path);
+
+ static const base::FilePath& debug_dump_path();
+
+ private:
+ friend class base::RefCountedThreadSafe<PrintedDocument>;
+
+ virtual ~PrintedDocument();
+
+ // Array of data for each print previewed page.
+ typedef std::map<int, scoped_refptr<PrintedPage> > PrintedPages;
+
+ // Contains all the mutable stuff. All this stuff MUST be accessed with the
+ // lock held.
+ struct Mutable {
+ explicit Mutable(PrintedPagesSource* source);
+ ~Mutable();
+
+ // Source that generates the PrintedPage's (i.e. a TabContents). It will be
+ // set back to NULL if the source is deleted before this object.
+ PrintedPagesSource* source_;
+
+ // Contains the pages' representation. This is a collection of PrintedPage.
+ // Warning: Lock must be held when accessing this member.
+ PrintedPages pages_;
+
+ // Number of expected pages to be rendered.
+ // Warning: Lock must be held when accessing this member.
+ int expected_page_count_;
+
+ // The total number of pages in the document.
+ int page_count_;
+
+#if defined(OS_POSIX) && !defined(OS_MACOSX)
+ // Page number of the first page.
+ int first_page;
+#endif
+ };
+
+ // Contains all the immutable stuff. All this stuff can be accessed without
+ // any lock held. This is because it can't be changed after the object's
+ // construction.
+ struct Immutable {
+ Immutable(const PrintSettings& settings, PrintedPagesSource* source,
+ int cookie);
+ ~Immutable();
+
+ // Print settings used to generate this document. Immutable.
+ PrintSettings settings_;
+
+ // Native thread for the render source.
+ base::MessageLoop* source_message_loop_;
+
+ // Document name. Immutable.
+ string16 name_;
+
+ // Cookie to uniquely identify this document. It is used to make sure that a
+ // PrintedPage is correctly belonging to the PrintedDocument. Since
+ // PrintedPage generation is completely asynchronous, it could be easy to
+ // mess up and send the page to the wrong document. It can be viewed as a
+ // simpler hash of PrintSettings since a new document is made each time the
+ // print settings change.
+ int cookie_;
+ };
+
+ void DebugDump(const PrintedPage& page);
+
+ // All writable data member access must be guarded by this lock. Needs to be
+ // mutable since it can be acquired from const member functions.
+ mutable base::Lock lock_;
+
+ // All the mutable members.
+ Mutable mutable_;
+
+ // All the immutable members.
+ const Immutable immutable_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrintedDocument);
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINTED_DOCUMENT_H_
diff --git a/chromium/printing/printed_document_gtk.cc b/chromium/printing/printed_document_gtk.cc
new file mode 100644
index 00000000000..0970b251e2d
--- /dev/null
+++ b/chromium/printing/printed_document_gtk.cc
@@ -0,0 +1,35 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/printed_document.h"
+
+#include "base/logging.h"
+#include "printing/page_number.h"
+#include "printing/printed_page.h"
+#include "printing/printing_context_gtk.h"
+
+namespace printing {
+
+void PrintedDocument::RenderPrintedPage(
+ const PrintedPage& page, PrintingContext* context) const {
+#ifndef NDEBUG
+ {
+ // Make sure the page is from our list.
+ base::AutoLock lock(lock_);
+ DCHECK(&page == mutable_.pages_.find(page.page_number() - 1)->second.get());
+ }
+#endif
+
+ DCHECK(context);
+
+ {
+ base::AutoLock lock(lock_);
+ if (page.page_number() - 1 == mutable_.first_page) {
+ reinterpret_cast<PrintingContextGtk*>(context)->PrintDocument(
+ page.metafile());
+ }
+ }
+}
+
+} // namespace printing
diff --git a/chromium/printing/printed_document_mac.cc b/chromium/printing/printed_document_mac.cc
new file mode 100644
index 00000000000..cc671e13d2d
--- /dev/null
+++ b/chromium/printing/printed_document_mac.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/printed_document.h"
+
+#import <ApplicationServices/ApplicationServices.h>
+#import <CoreFoundation/CoreFoundation.h>
+
+#include "base/logging.h"
+#include "printing/page_number.h"
+#include "printing/printed_page.h"
+
+namespace printing {
+
+void PrintedDocument::RenderPrintedPage(
+ const PrintedPage& page, gfx::NativeDrawingContext context) const {
+#ifndef NDEBUG
+ {
+ // Make sure the page is from our list.
+ base::AutoLock lock(lock_);
+ DCHECK(&page == mutable_.pages_.find(page.page_number() - 1)->second.get());
+ }
+#endif
+
+ DCHECK(context);
+
+ const PageSetup& page_setup(immutable_.settings_.page_setup_device_units());
+ gfx::Rect content_area;
+ page.GetCenteredPageContentRect(page_setup.physical_size(), &content_area);
+
+ const Metafile* metafile = page.metafile();
+ // Each Metafile is a one-page PDF, and pages use 1-based indexing.
+ const int page_number = 1;
+ struct Metafile::MacRenderPageParams params;
+ params.autorotate = true;
+ metafile->RenderPage(page_number, context, content_area.ToCGRect(), params);
+}
+
+} // namespace printing
diff --git a/chromium/printing/printed_document_win.cc b/chromium/printing/printed_document_win.cc
new file mode 100644
index 00000000000..392679384c5
--- /dev/null
+++ b/chromium/printing/printed_document_win.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/printed_document.h"
+
+#include "base/logging.h"
+#include "printing/page_number.h"
+#include "printing/printed_pages_source.h"
+#include "printing/printed_page.h"
+#include "printing/units.h"
+#include "skia/ext/platform_device.h"
+
+namespace {
+
+void SimpleModifyWorldTransform(HDC context,
+ int offset_x,
+ int offset_y,
+ double shrink_factor) {
+ XFORM xform = { 0 };
+ xform.eDx = static_cast<float>(offset_x);
+ xform.eDy = static_cast<float>(offset_y);
+ xform.eM11 = xform.eM22 = static_cast<float>(1. / shrink_factor);
+ BOOL res = ModifyWorldTransform(context, &xform, MWT_LEFTMULTIPLY);
+ DCHECK_NE(res, 0);
+}
+
+void DrawRect(HDC context, gfx::Rect rect) {
+ Rectangle(context, rect.x(), rect.y(), rect.right(), rect.bottom());
+}
+
+} // namespace
+
+namespace printing {
+
+void PrintedDocument::RenderPrintedPage(
+ const PrintedPage& page, gfx::NativeDrawingContext context) const {
+#ifndef NDEBUG
+ {
+ // Make sure the page is from our list.
+ base::AutoLock lock(lock_);
+ DCHECK(&page == mutable_.pages_.find(page.page_number() - 1)->second.get());
+ }
+#endif
+
+ DCHECK(context);
+
+ const PageSetup& page_setup(immutable_.settings_.page_setup_device_units());
+ gfx::Rect content_area;
+ page.GetCenteredPageContentRect(page_setup.physical_size(), &content_area);
+
+ // Save the state to make sure the context this function call does not modify
+ // the device context.
+ int saved_state = SaveDC(context);
+ DCHECK_NE(saved_state, 0);
+ skia::InitializeDC(context);
+ {
+ // Save the state (again) to apply the necessary world transformation.
+ int saved_state = SaveDC(context);
+ DCHECK_NE(saved_state, 0);
+
+ // Setup the matrix to translate and scale to the right place. Take in
+ // account the actual shrinking factor.
+ // Note that the printing output is relative to printable area of the page.
+ // That is 0,0 is offset by PHYSICALOFFSETX/Y from the page.
+ SimpleModifyWorldTransform(
+ context,
+ content_area.x() - page_setup.printable_area().x(),
+ content_area.y() - page_setup.printable_area().y(),
+ page.shrink_factor());
+
+ if (!page.metafile()->SafePlayback(context)) {
+ NOTREACHED();
+ }
+
+ BOOL res = RestoreDC(context, saved_state);
+ DCHECK_NE(res, 0);
+ }
+
+ int res = RestoreDC(context, saved_state);
+ DCHECK_NE(res, 0);
+}
+
+} // namespace printing
diff --git a/chromium/printing/printed_page.cc b/chromium/printing/printed_page.cc
new file mode 100644
index 00000000000..af7b7388f86
--- /dev/null
+++ b/chromium/printing/printed_page.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/printed_page.h"
+
+namespace printing {
+
+PrintedPage::PrintedPage(int page_number,
+ Metafile* metafile,
+ const gfx::Size& page_size,
+ const gfx::Rect& page_content_rect,
+ double shrink_factor)
+ : page_number_(page_number),
+ metafile_(metafile),
+ page_size_(page_size),
+ page_content_rect_(page_content_rect),
+ shrink_factor_(shrink_factor) {
+}
+
+PrintedPage::~PrintedPage() {
+}
+
+const Metafile* PrintedPage::metafile() const {
+ return metafile_.get();
+}
+
+void PrintedPage::GetCenteredPageContentRect(
+ const gfx::Size& paper_size, gfx::Rect* content_rect) const {
+ *content_rect = page_content_rect();
+ if (paper_size.width() > page_size().width()) {
+ int diff = paper_size.width() - page_size().width();
+ content_rect->set_x(content_rect->x() + diff / 2);
+ }
+ if (paper_size.height() > page_size().height()) {
+ int diff = paper_size.height() - page_size().height();
+ content_rect->set_y(content_rect->y() + diff / 2);
+ }
+}
+
+} // namespace printing
diff --git a/chromium/printing/printed_page.h b/chromium/printing/printed_page.h
new file mode 100644
index 00000000000..40e96e041d3
--- /dev/null
+++ b/chromium/printing/printed_page.h
@@ -0,0 +1,69 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PRINTED_PAGE_H_
+#define PRINTING_PRINTED_PAGE_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "printing/metafile.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/size.h"
+
+namespace printing {
+
+// Contains the data to reproduce a printed page, either on screen or on
+// paper. Once created, this object is immutable. It has no reference to the
+// PrintedDocument containing this page.
+// Note: May be accessed from many threads at the same time. This is an non
+// issue since this object is immutable. The reason is that a page may be
+// printed and be displayed at the same time.
+class PRINTING_EXPORT PrintedPage
+ : public base::RefCountedThreadSafe<PrintedPage> {
+ public:
+ PrintedPage(int page_number,
+ Metafile* metafile,
+ const gfx::Size& page_size,
+ const gfx::Rect& page_content_rect,
+ double shrink_factor);
+
+ // Getters
+ int page_number() const { return page_number_; }
+ const Metafile* metafile() const;
+ const gfx::Size& page_size() const { return page_size_; }
+ const gfx::Rect& page_content_rect() const { return page_content_rect_; }
+ double shrink_factor() const { return shrink_factor_; }
+
+ // Get page content rect adjusted based on
+ // http://dev.w3.org/csswg/css3-page/#positioning-page-box
+ void GetCenteredPageContentRect(const gfx::Size& paper_size,
+ gfx::Rect* content_rect) const;
+
+ private:
+ friend class base::RefCountedThreadSafe<PrintedPage>;
+
+ ~PrintedPage();
+
+ // Page number inside the printed document.
+ const int page_number_;
+
+ // Actual paint data.
+ const scoped_ptr<Metafile> metafile_;
+
+ // The physical page size. To support multiple page formats inside on print
+ // job.
+ const gfx::Size page_size_;
+
+ // The printable area of the page.
+ const gfx::Rect page_content_rect_;
+
+ // Shrink done in comparison to desired_dpi.
+ double shrink_factor_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrintedPage);
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINTED_PAGE_H_
diff --git a/chromium/printing/printed_page_unittest.cc b/chromium/printing/printed_page_unittest.cc
new file mode 100644
index 00000000000..9cce52a1cb8
--- /dev/null
+++ b/chromium/printing/printed_page_unittest.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/printed_page.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace printing {
+
+TEST(PrintedPageTest, GetCenteredPageContentRect) {
+ scoped_refptr<PrintedPage> page;
+ gfx::Rect page_content;
+
+ // No centering.
+ page = new PrintedPage(1,
+ NULL,
+ gfx::Size(1200, 1200),
+ gfx::Rect(0, 0, 400, 1100),
+ 0.2f);
+ page->GetCenteredPageContentRect(gfx::Size(1000, 1000), &page_content);
+ EXPECT_EQ(0, page_content.x());
+ EXPECT_EQ(0, page_content.y());
+ EXPECT_EQ(400, page_content.width());
+ EXPECT_EQ(1100, page_content.height());
+ EXPECT_EQ(0.2f, page->shrink_factor());
+
+ // X centered.
+ page = new PrintedPage(1,
+ NULL,
+ gfx::Size(500, 1200),
+ gfx::Rect(0, 0, 400, 1100),
+ 0.8f);
+ page->GetCenteredPageContentRect(gfx::Size(1000, 1000), &page_content);
+ EXPECT_EQ(250, page_content.x());
+ EXPECT_EQ(0, page_content.y());
+ EXPECT_EQ(400, page_content.width());
+ EXPECT_EQ(1100, page_content.height());
+ EXPECT_EQ(0.8f, page->shrink_factor());
+
+ // Y centered.
+ page = new PrintedPage(1,
+ NULL,
+ gfx::Size(1200, 500),
+ gfx::Rect(0, 0, 400, 1100),
+ 1.0f);
+ page->GetCenteredPageContentRect(gfx::Size(1000, 1000), &page_content);
+ EXPECT_EQ(0, page_content.x());
+ EXPECT_EQ(250, page_content.y());
+ EXPECT_EQ(400, page_content.width());
+ EXPECT_EQ(1100, page_content.height());
+ EXPECT_EQ(1.0f, page->shrink_factor());
+
+ // Both X and Y centered.
+ page = new PrintedPage(1,
+ NULL,
+ gfx::Size(500, 500),
+ gfx::Rect(0, 0, 400, 1100),
+ 0.3f);
+ page->GetCenteredPageContentRect(gfx::Size(1000, 1000), &page_content);
+ EXPECT_EQ(250, page_content.x());
+ EXPECT_EQ(250, page_content.y());
+ EXPECT_EQ(400, page_content.width());
+ EXPECT_EQ(1100, page_content.height());
+ EXPECT_EQ(0.3f, page->shrink_factor());
+}
+
+} // namespace printing
diff --git a/chromium/printing/printed_pages_source.h b/chromium/printing/printed_pages_source.h
new file mode 100644
index 00000000000..6da9ddb5835
--- /dev/null
+++ b/chromium/printing/printed_pages_source.h
@@ -0,0 +1,24 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PRINTED_PAGES_SOURCE_H_
+#define PRINTING_PRINTED_PAGES_SOURCE_H_
+
+#include "base/strings/string16.h"
+
+namespace printing {
+
+// Source of printed pages.
+class PrintedPagesSource {
+ public:
+ // Returns the document title.
+ virtual string16 RenderSourceName() = 0;
+
+ protected:
+ virtual ~PrintedPagesSource() {}
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINTED_PAGES_SOURCE_H_
diff --git a/chromium/printing/printing.gyp b/chromium/printing/printing.gyp
new file mode 100644
index 00000000000..b3bbf2b04bb
--- /dev/null
+++ b/chromium/printing/printing.gyp
@@ -0,0 +1,329 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'targets': [
+ {
+ 'target_name': 'printing',
+ 'type': '<(component)',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ '../base/base.gyp:base_i18n',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '../skia/skia.gyp:skia',
+ '../third_party/icu/icu.gyp:icui18n',
+ '../third_party/icu/icu.gyp:icuuc',
+ '../ui/ui.gyp:ui',
+ '../url/url.gyp:url_lib',
+ ],
+ 'defines': [
+ 'PRINTING_IMPLEMENTATION',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'backend/print_backend.cc',
+ 'backend/print_backend.h',
+ 'backend/print_backend_consts.cc',
+ 'backend/print_backend_consts.h',
+ 'backend/print_backend_dummy.cc',
+ 'backend/printing_info_win.cc',
+ 'backend/printing_info_win.h',
+ 'emf_win.cc',
+ 'emf_win.h',
+ 'image.cc',
+ 'image_linux.cc',
+ 'image_mac.cc',
+ 'image_win.cc',
+ 'image.h',
+ 'metafile.h',
+ 'metafile_impl.h',
+ 'metafile_skia_wrapper.h',
+ 'metafile_skia_wrapper.cc',
+ 'page_number.cc',
+ 'page_number.h',
+ 'page_range.cc',
+ 'page_range.h',
+ 'page_setup.cc',
+ 'page_setup.h',
+ 'page_size_margins.cc',
+ 'page_size_margins.h',
+ 'pdf_metafile_cg_mac.cc',
+ 'pdf_metafile_cg_mac.h',
+ 'pdf_metafile_skia.h',
+ 'pdf_metafile_skia.cc',
+ 'print_destination_interface.h',
+ 'print_destination_none.cc',
+ 'print_destination_win.cc',
+ 'printed_document_gtk.cc',
+ 'printed_document.cc',
+ 'printed_document.h',
+ 'printed_document_mac.cc',
+ 'printed_document_win.cc',
+ 'printed_page.cc',
+ 'printed_page.h',
+ 'printed_pages_source.h',
+ 'printing_context.cc',
+ 'printing_context.h',
+ 'print_dialog_gtk_interface.h',
+ 'print_job_constants.cc',
+ 'print_job_constants.h',
+ 'print_settings.cc',
+ 'print_settings.h',
+ 'print_settings_initializer.cc',
+ 'print_settings_initializer.h',
+ 'print_settings_initializer_gtk.cc',
+ 'print_settings_initializer_gtk.h',
+ 'print_settings_initializer_mac.cc',
+ 'print_settings_initializer_mac.h',
+ 'print_settings_initializer_win.cc',
+ 'print_settings_initializer_win.h',
+ 'units.cc',
+ 'units.h',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '..',
+ ],
+ },
+ 'conditions': [
+ ['enable_printing==0', {
+ 'sources/': [
+ ['exclude', '.'],
+ ],
+ }],
+ ['use_aura==1', {
+ 'dependencies': [
+ '<(DEPTH)/ui/aura/aura.gyp:aura',
+ ],
+ }],
+ ['toolkit_uses_gtk == 0',{
+ 'sources/': [['exclude', '_cairo\\.cc$']]
+ }],
+ ['OS!="mac"', {'sources/': [['exclude', '_mac\\.(cc|mm?)$']]}],
+ ['OS!="win"', {'sources/': [['exclude', '_win\\.cc$']]
+ }, { # else: OS=="win"
+ 'sources/': [['exclude', '_posix\\.cc$']]
+ }],
+ ['toolkit_uses_gtk == 1', {
+ 'dependencies': [
+ # For FT_Init_FreeType and friends.
+ '../build/linux/system.gyp:freetype2',
+ '../build/linux/system.gyp:gtk',
+ '../build/linux/system.gyp:gtkprint',
+ ],
+ }],
+ # Mac-Aura does not support printing.
+ ['OS=="mac" and use_aura==1',{
+ 'sources!': [
+ 'printed_document_mac.cc',
+ 'printing_context_mac.mm',
+ 'printing_context_mac.h',
+ ],
+ }],
+ ['OS=="mac" and use_aura==0',{
+ 'sources': [
+ 'printing_context_mac.mm',
+ 'printing_context_mac.h',
+ ],
+ }],
+ ['OS=="win"', {
+ 'dependencies': [
+ '../win8/win8.gyp:win8_util',
+ ],
+ 'conditions': [
+ ['use_aura==1', {
+ 'dependencies': [
+ '<(DEPTH)/ui/aura/aura.gyp:aura',
+ ],
+ }]],
+ 'defines': [
+ # PRINT_BACKEND_AVAILABLE disables the default dummy implementation
+ # of the print backend and enables a custom implementation instead.
+ 'PRINT_BACKEND_AVAILABLE',
+ ],
+ 'sources': [
+ 'backend/win_helper.cc',
+ 'backend/win_helper.h',
+ 'backend/print_backend_win.cc',
+ 'printing_context_win.cc',
+ 'printing_context_win.h',
+ ],
+ 'sources!': [
+ 'print_destination_none.cc',
+ ],
+ }],
+ ['chromeos==1 or (use_aura==1 and OS!="win")',{
+ 'sources': [
+ 'printing_context_no_system_dialog.cc',
+ 'printing_context_no_system_dialog.h',
+ ],
+ }],
+ ['use_cups==1', {
+ 'dependencies': [
+ 'cups',
+ ],
+ 'variables': {
+ 'cups_version': '<!(cups-config --api-version)',
+ },
+ 'conditions': [
+ ['OS!="mac"', {
+ 'dependencies': [
+ '../build/linux/system.gyp:libgcrypt',
+ ],
+ }],
+ ['cups_version in ["1.6", "1.7"]', {
+ 'cflags': [
+ # CUPS 1.6 deprecated the PPD APIs, but we will stay with this
+ # API for now as supported Linux and Mac OS'es are still using
+ # older versions of CUPS. More info: crbug.com/226176
+ '-Wno-deprecated-declarations',
+ # CUPS 1.7 deprecates httpConnectEncrypt(), see the mac section
+ # below.
+ ],
+ }],
+ ['OS=="mac" and mac_sdk=="10.9"', {
+ # The 10.9 SDK includes cups 1.7, which deprecates
+ # httpConnectEncrypt() in favor of httpConnect2(). hhttpConnect2()
+ # is new in 1.7, so it doesn't exist on OS X 10.6-10.8 and we
+ # can't use it until 10.9 is our minimum system version.
+ # (cups_version isn't reliable on OS X, so key the check off of
+ # mac_sdk).
+ 'xcode_settings': {
+ 'WARNING_CFLAGS': [
+ '-Wno-deprecated-declarations',
+ ],
+ },
+ }],
+ ],
+ 'defines': [
+ # PRINT_BACKEND_AVAILABLE disables the default dummy implementation
+ # of the print backend and enables a custom implementation instead.
+ 'PRINT_BACKEND_AVAILABLE',
+ ],
+ 'sources': [
+ 'backend/cups_helper.cc',
+ 'backend/cups_helper.h',
+ 'backend/print_backend_cups.cc',
+ ],
+ }],
+ ['OS=="linux" and chromeos==1', {
+ 'defines': [
+ # PRINT_BACKEND_AVAILABLE disables the default dummy implementation
+ # of the print backend and enables a custom implementation instead.
+ 'PRINT_BACKEND_AVAILABLE',
+ ],
+ 'sources': [
+ 'backend/print_backend_chromeos.cc',
+ ],
+ }],
+ ['toolkit_uses_gtk==1 and chromeos==0', {
+ 'sources': [
+ 'printing_context_gtk.cc',
+ 'printing_context_gtk.h',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'printing_unittests',
+ 'type': 'executable',
+ 'dependencies': [
+ 'printing',
+ '../testing/gtest.gyp:gtest',
+ '../base/base.gyp:test_support_base',
+ '../ui/ui.gyp:ui',
+ ],
+ 'sources': [
+ 'backend/print_backend_unittest.cc',
+ 'emf_win_unittest.cc',
+ 'printing_test.h',
+ 'page_number_unittest.cc',
+ 'page_range_unittest.cc',
+ 'page_setup_unittest.cc',
+ 'pdf_metafile_cg_mac_unittest.cc',
+ 'printed_page_unittest.cc',
+ 'run_all_unittests.cc',
+ 'units_unittest.cc',
+ ],
+ 'conditions': [
+ ['enable_printing==0', {
+ 'sources/': [
+ ['exclude', '.'],
+ ['include', 'run_all_unittests.cc'],
+ ],
+ }],
+ ['toolkit_uses_gtk == 0', {'sources/': [['exclude', '_gtk_unittest\\.cc$']]}],
+ ['OS!="mac"', {'sources/': [['exclude', '_mac_unittest\\.(cc|mm?)$']]}],
+ ['OS!="win"', {'sources/': [['exclude', '_win_unittest\\.cc$']]}],
+ ['OS=="win" and use_aura == 0', {
+ 'sources': [
+ 'printing_context_win_unittest.cc',
+ ]
+ }],
+ ['use_cups==1', {
+ 'defines': [
+ 'USE_CUPS',
+ ],
+ 'sources': [
+ 'backend/cups_helper_unittest.cc',
+ ],
+ }],
+ ['toolkit_uses_gtk == 1', {
+ 'dependencies': [
+ '../build/linux/system.gyp:gtk',
+ ],
+ }],
+ [ 'os_posix == 1 and OS != "mac" and OS != "android" and OS != "ios"', {
+ 'conditions': [
+ ['linux_use_tcmalloc == 1', {
+ 'dependencies': [
+ '../base/allocator/allocator.gyp:allocator',
+ ],
+ }],
+ ],
+ }],
+ ],
+ # TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
+ 'msvs_disabled_warnings': [ 4267, ],
+ },
+ {
+ 'target_name': 'cups',
+ 'type': 'none',
+ 'conditions': [
+ ['use_cups==1', {
+ 'direct_dependent_settings': {
+ 'defines': [
+ 'USE_CUPS',
+ ],
+ 'conditions': [
+ ['OS=="mac"', {
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/usr/lib/libcups.dylib',
+ ]
+ },
+ }, {
+ 'link_settings': {
+ 'libraries': [
+ '<!@(python cups_config_helper.py --libs)',
+ ],
+ },
+ }],
+ [ 'os_bsd==1', {
+ 'cflags': [
+ '<!@(python cups_config_helper.py --cflags)',
+ ],
+ }],
+ ],
+ },
+ }],
+ ],
+ },
+ ],
+}
diff --git a/chromium/printing/printing_context.cc b/chromium/printing/printing_context.cc
new file mode 100644
index 00000000000..98ea11cba79
--- /dev/null
+++ b/chromium/printing/printing_context.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/printing_context.h"
+
+#include "base/logging.h"
+#include "base/values.h"
+#include "printing/page_setup.h"
+#include "printing/page_size_margins.h"
+#include "printing/print_settings_initializer.h"
+
+namespace printing {
+
+PrintingContext::PrintingContext(const std::string& app_locale)
+ : dialog_box_dismissed_(false),
+ in_print_job_(false),
+ abort_printing_(false),
+ app_locale_(app_locale) {
+}
+
+PrintingContext::~PrintingContext() {
+}
+
+void PrintingContext::set_margin_type(MarginType type) {
+ DCHECK(type != CUSTOM_MARGINS);
+ settings_.margin_type = type;
+}
+
+void PrintingContext::ResetSettings() {
+ ReleaseContext();
+
+ settings_.Clear();
+
+ in_print_job_ = false;
+ dialog_box_dismissed_ = false;
+ abort_printing_ = false;
+}
+
+PrintingContext::Result PrintingContext::OnError() {
+ ResetSettings();
+ return abort_printing_ ? CANCEL : FAILED;
+}
+
+PrintingContext::Result PrintingContext::UpdatePrintSettings(
+ const base::DictionaryValue& job_settings,
+ const PageRanges& ranges) {
+ ResetSettings();
+
+ job_settings.GetBoolean(kSettingHeaderFooterEnabled,
+ &settings_.display_header_footer);
+
+ int margin_type = DEFAULT_MARGINS;
+ if (!job_settings.GetInteger(kSettingMarginsType, &margin_type) ||
+ (margin_type != DEFAULT_MARGINS &&
+ margin_type != NO_MARGINS &&
+ margin_type != CUSTOM_MARGINS &&
+ margin_type != PRINTABLE_AREA_MARGINS)) {
+ margin_type = DEFAULT_MARGINS;
+ }
+ settings_.margin_type = static_cast<MarginType>(margin_type);
+
+ if (margin_type == CUSTOM_MARGINS) {
+ printing::PageSizeMargins page_size_margins;
+ GetCustomMarginsFromJobSettings(job_settings, &page_size_margins);
+
+ PageMargins margins_in_points;
+ margins_in_points.Clear();
+ margins_in_points.top = page_size_margins.margin_top;
+ margins_in_points.bottom = page_size_margins.margin_bottom;
+ margins_in_points.left = page_size_margins.margin_left;
+ margins_in_points.right = page_size_margins.margin_right;
+
+ settings_.SetCustomMargins(margins_in_points);
+ }
+
+ PrintingContext::Result result = UpdatePrinterSettings(job_settings, ranges);
+ PrintSettingsInitializer::InitHeaderFooterStrings(job_settings, &settings_);
+
+ job_settings.GetBoolean(kSettingShouldPrintBackgrounds,
+ &settings_.should_print_backgrounds);
+ job_settings.GetBoolean(kSettingShouldPrintSelectionOnly,
+ &settings_.selection_only);
+ return result;
+}
+
+} // namespace printing
diff --git a/chromium/printing/printing_context.h b/chromium/printing/printing_context.h
new file mode 100644
index 00000000000..6ad1e8d3f63
--- /dev/null
+++ b/chromium/printing/printing_context.h
@@ -0,0 +1,136 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PRINTING_CONTEXT_H_
+#define PRINTING_PRINTING_CONTEXT_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/strings/string16.h"
+#include "printing/print_settings.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace printing {
+
+// An abstraction of a printer context, implemented by objects that describe the
+// user selected printing context. This includes the OS-dependent UI to ask the
+// user about the print settings. Concrete implementations directly talk to the
+// printer and manage the document and page breaks.
+class PRINTING_EXPORT PrintingContext {
+ public:
+ // Tri-state result for user behavior-dependent functions.
+ enum Result {
+ OK,
+ CANCEL,
+ FAILED,
+ };
+
+ virtual ~PrintingContext();
+
+ // Callback of AskUserForSettings, used to notify the PrintJobWorker when
+ // print settings are available.
+ typedef base::Callback<void(Result)> PrintSettingsCallback;
+
+ // Asks the user what printer and format should be used to print. Updates the
+ // context with the select device settings. The result of the call is returned
+ // in the callback. This is necessary for Linux, which only has an
+ // asynchronous printing API.
+ virtual void AskUserForSettings(gfx::NativeView parent_view,
+ int max_pages,
+ bool has_selection,
+ const PrintSettingsCallback& callback) = 0;
+
+ // Selects the user's default printer and format. Updates the context with the
+ // default device settings.
+ virtual Result UseDefaultSettings() = 0;
+
+ // Updates printer related settings. |job_settings| contains all print job
+ // settings information. |ranges| has the new page range settings.
+ virtual Result UpdatePrinterSettings(
+ const base::DictionaryValue& job_settings,
+ const PageRanges& ranges) = 0;
+
+ // Updates Print Settings. |job_settings| contains all print job
+ // settings information. |ranges| has the new page range settings.
+ Result UpdatePrintSettings(const base::DictionaryValue& job_settings,
+ const PageRanges& ranges);
+
+ // Initializes with predefined settings.
+ virtual Result InitWithSettings(const PrintSettings& settings) = 0;
+
+ // Does platform specific setup of the printer before the printing. Signal the
+ // printer that a document is about to be spooled.
+ // Warning: This function enters a message loop. That may cause side effects
+ // like IPC message processing! Some printers have side-effects on this call
+ // like virtual printers that ask the user for the path of the saved document;
+ // for example a PDF printer.
+ virtual Result NewDocument(const string16& document_name) = 0;
+
+ // Starts a new page.
+ virtual Result NewPage() = 0;
+
+ // Closes the printed page.
+ virtual Result PageDone() = 0;
+
+ // Closes the printing job. After this call the object is ready to start a new
+ // document.
+ virtual Result DocumentDone() = 0;
+
+ // Cancels printing. Can be used in a multi-threaded context. Takes effect
+ // immediately.
+ virtual void Cancel() = 0;
+
+ // Releases the native printing context.
+ virtual void ReleaseContext() = 0;
+
+ // Returns the native context used to print.
+ virtual gfx::NativeDrawingContext context() const = 0;
+
+ // Creates an instance of this object. Implementers of this interface should
+ // implement this method to create an object of their implementation. The
+ // caller owns the returned object.
+ static PrintingContext* Create(const std::string& app_locale);
+
+ void set_margin_type(MarginType type);
+
+ const PrintSettings& settings() const {
+ return settings_;
+ }
+
+ protected:
+ explicit PrintingContext(const std::string& app_locale);
+
+ // Reinitializes the settings for object reuse.
+ void ResetSettings();
+
+ // Does bookkeeping when an error occurs.
+ PrintingContext::Result OnError();
+
+ // Complete print context settings.
+ PrintSettings settings_;
+
+ // The dialog box has been dismissed.
+ volatile bool dialog_box_dismissed_;
+
+ // Is a print job being done.
+ volatile bool in_print_job_;
+
+ // Did the user cancel the print job.
+ volatile bool abort_printing_;
+
+ // The application locale.
+ std::string app_locale_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrintingContext);
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINTING_CONTEXT_H_
diff --git a/chromium/printing/printing_context_gtk.cc b/chromium/printing/printing_context_gtk.cc
new file mode 100644
index 00000000000..4bfa666b236
--- /dev/null
+++ b/chromium/printing/printing_context_gtk.cc
@@ -0,0 +1,159 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/printing_context_gtk.h"
+
+#include <gtk/gtk.h>
+#include <gtk/gtkunixprint.h>
+
+#include "base/logging.h"
+#include "base/values.h"
+#include "printing/metafile.h"
+#include "printing/print_dialog_gtk_interface.h"
+#include "printing/print_job_constants.h"
+#include "printing/units.h"
+
+namespace {
+
+// Function pointer for creating print dialogs. |callback| is only used when
+// |show_dialog| is true.
+printing::PrintDialogGtkInterface* (*create_dialog_func_)(
+ printing::PrintingContextGtk* context) = NULL;
+
+} // namespace
+
+namespace printing {
+
+// static
+PrintingContext* PrintingContext::Create(const std::string& app_locale) {
+ return static_cast<PrintingContext*>(new PrintingContextGtk(app_locale));
+}
+
+PrintingContextGtk::PrintingContextGtk(const std::string& app_locale)
+ : PrintingContext(app_locale),
+ print_dialog_(NULL) {
+}
+
+PrintingContextGtk::~PrintingContextGtk() {
+ ReleaseContext();
+
+ if (print_dialog_)
+ print_dialog_->ReleaseDialog();
+}
+
+// static
+void PrintingContextGtk::SetCreatePrintDialogFunction(
+ PrintDialogGtkInterface* (*create_dialog_func)(
+ PrintingContextGtk* context)) {
+ DCHECK(create_dialog_func);
+ DCHECK(!create_dialog_func_);
+ create_dialog_func_ = create_dialog_func;
+}
+
+void PrintingContextGtk::PrintDocument(const Metafile* metafile) {
+ DCHECK(print_dialog_);
+ DCHECK(metafile);
+ print_dialog_->PrintDocument(metafile, document_name_);
+}
+
+void PrintingContextGtk::AskUserForSettings(
+ gfx::NativeView parent_view,
+ int max_pages,
+ bool has_selection,
+ const PrintSettingsCallback& callback) {
+ print_dialog_->ShowDialog(parent_view, has_selection, callback);
+}
+
+PrintingContext::Result PrintingContextGtk::UseDefaultSettings() {
+ DCHECK(!in_print_job_);
+
+ ResetSettings();
+ if (!print_dialog_) {
+ print_dialog_ = create_dialog_func_(this);
+ print_dialog_->AddRefToDialog();
+ }
+ print_dialog_->UseDefaultSettings();
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextGtk::UpdatePrinterSettings(
+ const DictionaryValue& job_settings, const PageRanges& ranges) {
+ DCHECK(!in_print_job_);
+
+ if (!print_dialog_) {
+ print_dialog_ = create_dialog_func_(this);
+ print_dialog_->AddRefToDialog();
+ }
+
+ if (!print_dialog_->UpdateSettings(job_settings, ranges, &settings_))
+ return OnError();
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextGtk::InitWithSettings(
+ const PrintSettings& settings) {
+ DCHECK(!in_print_job_);
+
+ settings_ = settings;
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextGtk::NewDocument(
+ const string16& document_name) {
+ DCHECK(!in_print_job_);
+ in_print_job_ = true;
+
+ document_name_ = document_name;
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextGtk::NewPage() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ // Intentional No-op.
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextGtk::PageDone() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ // Intentional No-op.
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextGtk::DocumentDone() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ ResetSettings();
+ return OK;
+}
+
+void PrintingContextGtk::Cancel() {
+ abort_printing_ = true;
+ in_print_job_ = false;
+}
+
+void PrintingContextGtk::ReleaseContext() {
+ // Intentional No-op.
+}
+
+gfx::NativeDrawingContext PrintingContextGtk::context() const {
+ // Intentional No-op.
+ return NULL;
+}
+
+} // namespace printing
+
diff --git a/chromium/printing/printing_context_gtk.h b/chromium/printing/printing_context_gtk.h
new file mode 100644
index 00000000000..8d671aec482
--- /dev/null
+++ b/chromium/printing/printing_context_gtk.h
@@ -0,0 +1,63 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PRINTING_CONTEXT_GTK_H_
+#define PRINTING_PRINTING_CONTEXT_GTK_H_
+
+#include <string>
+
+#include "printing/printing_context.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace printing {
+
+class Metafile;
+class PrintDialogGtkInterface;
+
+class PRINTING_EXPORT PrintingContextGtk : public PrintingContext {
+ public:
+ explicit PrintingContextGtk(const std::string& app_locale);
+ virtual ~PrintingContextGtk();
+
+ // Sets the function that creates the print dialog.
+ static void SetCreatePrintDialogFunction(
+ PrintDialogGtkInterface* (*create_dialog_func)(
+ PrintingContextGtk* context));
+
+ // Prints the document contained in |metafile|.
+ void PrintDocument(const Metafile* metafile);
+
+ // PrintingContext implementation.
+ virtual void AskUserForSettings(
+ gfx::NativeView parent_view,
+ int max_pages,
+ bool has_selection,
+ const PrintSettingsCallback& callback) OVERRIDE;
+ virtual Result UseDefaultSettings() OVERRIDE;
+ virtual Result UpdatePrinterSettings(
+ const base::DictionaryValue& job_settings,
+ const PageRanges& ranges) OVERRIDE;
+ virtual Result InitWithSettings(const PrintSettings& settings) OVERRIDE;
+ virtual Result NewDocument(const string16& document_name) OVERRIDE;
+ virtual Result NewPage() OVERRIDE;
+ virtual Result PageDone() OVERRIDE;
+ virtual Result DocumentDone() OVERRIDE;
+ virtual void Cancel() OVERRIDE;
+ virtual void ReleaseContext() OVERRIDE;
+ virtual gfx::NativeDrawingContext context() const OVERRIDE;
+
+ private:
+ string16 document_name_;
+ PrintDialogGtkInterface* print_dialog_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrintingContextGtk);
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINTING_CONTEXT_GTK_H_
+
diff --git a/chromium/printing/printing_context_mac.h b/chromium/printing/printing_context_mac.h
new file mode 100644
index 00000000000..c0edc323db8
--- /dev/null
+++ b/chromium/printing/printing_context_mac.h
@@ -0,0 +1,101 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PRINTING_CONTEXT_MAC_H_
+#define PRINTING_PRINTING_CONTEXT_MAC_H_
+
+#include <string>
+
+#include "base/mac/scoped_nsobject.h"
+#include "printing/print_job_constants.h"
+#include "printing/printing_context.h"
+
+#ifdef __OBJC__
+@class NSPrintInfo;
+#else
+class NSPrintInfo;
+#endif // __OBJC__
+
+namespace printing {
+
+class PRINTING_EXPORT PrintingContextMac : public PrintingContext {
+ public:
+ explicit PrintingContextMac(const std::string& app_locale);
+ virtual ~PrintingContextMac();
+
+ // PrintingContext implementation.
+ virtual void AskUserForSettings(
+ gfx::NativeView parent_view,
+ int max_pages,
+ bool has_selection,
+ const PrintSettingsCallback& callback) OVERRIDE;
+ virtual Result UseDefaultSettings() OVERRIDE;
+ virtual Result UpdatePrinterSettings(
+ const base::DictionaryValue& job_settings,
+ const PageRanges& ranges) OVERRIDE;
+ virtual Result InitWithSettings(const PrintSettings& settings) OVERRIDE;
+ virtual Result NewDocument(const string16& document_name) OVERRIDE;
+ virtual Result NewPage() OVERRIDE;
+ virtual Result PageDone() OVERRIDE;
+ virtual Result DocumentDone() OVERRIDE;
+ virtual void Cancel() OVERRIDE;
+ virtual void ReleaseContext() OVERRIDE;
+ virtual gfx::NativeDrawingContext context() const OVERRIDE;
+
+ private:
+ // Initializes PrintSettings from |print_info_|. This must be called
+ // after changes to |print_info_| in order for the changes to take effect in
+ // printing.
+ // This function ignores the page range information specified in the print
+ // info object and use |ranges| instead.
+ void InitPrintSettingsFromPrintInfo(const PageRanges& ranges);
+
+ // Returns the set of page ranges constructed from |print_info_|.
+ PageRanges GetPageRangesFromPrintInfo();
+
+ // Updates |print_info_| to use the given printer.
+ // Returns true if the printer was set else returns false.
+ bool SetPrinter(const std::string& device_name);
+
+ // Updates |print_info_| page format with user default paper information.
+ // Returns true if the paper was set else returns false.
+ bool UpdatePageFormatWithPaperInfo();
+
+ // Sets the print job destination type as preview job.
+ // Returns true if the print job destination type is set.
+ bool SetPrintPreviewJob();
+
+ // Sets |copies| in PMPrintSettings.
+ // Returns true if the number of copies is set.
+ bool SetCopiesInPrintSettings(int copies);
+
+ // Sets |collate| in PMPrintSettings.
+ // Returns true if |collate| is set.
+ bool SetCollateInPrintSettings(bool collate);
+
+ // Sets orientation in native print info object.
+ // Returns true if the orientation was set.
+ bool SetOrientationIsLandscape(bool landscape);
+
+ // Sets duplex mode in PMPrintSettings.
+ // Returns true if duplex mode is set.
+ bool SetDuplexModeInPrintSettings(DuplexMode mode);
+
+ // Sets output color mode in PMPrintSettings.
+ // Returns true if color mode is set.
+ bool SetOutputColor(int color_mode);
+
+ // The native print info object.
+ base::scoped_nsobject<NSPrintInfo> print_info_;
+
+ // The current page's context; only valid between NewPage and PageDone call
+ // pairs.
+ CGContext* context_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrintingContextMac);
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINTING_CONTEXT_MAC_H_
diff --git a/chromium/printing/printing_context_mac.mm b/chromium/printing/printing_context_mac.mm
new file mode 100644
index 00000000000..4d83c130718
--- /dev/null
+++ b/chromium/printing/printing_context_mac.mm
@@ -0,0 +1,519 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/printing_context_mac.h"
+
+#import <ApplicationServices/ApplicationServices.h>
+#import <AppKit/AppKit.h>
+
+#import <iomanip>
+#import <numeric>
+
+#include "base/logging.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/mac/scoped_nsexception_enabler.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/values.h"
+#include "printing/print_settings_initializer_mac.h"
+
+namespace printing {
+
+namespace {
+
+// Return true if PPD name of paper is equal.
+bool IsPaperNameEqual(const PMPaper& paper1, const PMPaper& paper2) {
+ CFStringRef name1 = NULL;
+ CFStringRef name2 = NULL;
+ return (PMPaperGetPPDPaperName(paper1, &name1) == noErr) &&
+ (PMPaperGetPPDPaperName(paper2, &name2) == noErr) &&
+ (CFStringCompare(name1, name2,
+ kCFCompareCaseInsensitive) == kCFCompareEqualTo);
+}
+
+} // namespace
+
+// static
+PrintingContext* PrintingContext::Create(const std::string& app_locale) {
+ return static_cast<PrintingContext*>(new PrintingContextMac(app_locale));
+}
+
+PrintingContextMac::PrintingContextMac(const std::string& app_locale)
+ : PrintingContext(app_locale),
+ print_info_([[NSPrintInfo sharedPrintInfo] copy]),
+ context_(NULL) {
+}
+
+PrintingContextMac::~PrintingContextMac() {
+ ReleaseContext();
+}
+
+void PrintingContextMac::AskUserForSettings(
+ gfx::NativeView parent_view,
+ int max_pages,
+ bool has_selection,
+ const PrintSettingsCallback& callback) {
+ // Third-party print drivers seem to be an area prone to raising exceptions.
+ // This will allow exceptions to be raised, but does not handle them. The
+ // NSPrintPanel appears to have appropriate NSException handlers.
+ base::mac::ScopedNSExceptionEnabler enabler;
+
+ // Exceptions can also happen when the NSPrintPanel is being
+ // deallocated, so it must be autoreleased within this scope.
+ base::mac::ScopedNSAutoreleasePool pool;
+
+ DCHECK([NSThread isMainThread]);
+
+ // We deliberately don't feed max_pages into the dialog, because setting
+ // NSPrintLastPage makes the print dialog pre-select the option to only print
+ // a range.
+
+ // TODO(stuartmorgan): implement 'print selection only' (probably requires
+ // adding a new custom view to the panel on 10.5; 10.6 has
+ // NSPrintPanelShowsPrintSelection).
+ NSPrintPanel* panel = [NSPrintPanel printPanel];
+ NSPrintInfo* printInfo = print_info_.get();
+
+ NSPrintPanelOptions options = [panel options];
+ options |= NSPrintPanelShowsPaperSize;
+ options |= NSPrintPanelShowsOrientation;
+ options |= NSPrintPanelShowsScaling;
+ [panel setOptions:options];
+
+ // Set the print job title text.
+ if (parent_view) {
+ NSString* job_title = [[parent_view window] title];
+ if (job_title) {
+ PMPrintSettings printSettings =
+ (PMPrintSettings)[printInfo PMPrintSettings];
+ PMPrintSettingsSetJobName(printSettings, (CFStringRef)job_title);
+ [printInfo updateFromPMPrintSettings];
+ }
+ }
+
+ // TODO(stuartmorgan): We really want a tab sheet here, not a modal window.
+ // Will require restructuring the PrintingContext API to use a callback.
+ NSInteger selection = [panel runModalWithPrintInfo:printInfo];
+ if (selection == NSOKButton) {
+ print_info_.reset([[panel printInfo] retain]);
+ InitPrintSettingsFromPrintInfo(GetPageRangesFromPrintInfo());
+ callback.Run(OK);
+ } else {
+ callback.Run(CANCEL);
+ }
+}
+
+PrintingContext::Result PrintingContextMac::UseDefaultSettings() {
+ DCHECK(!in_print_job_);
+
+ print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
+ InitPrintSettingsFromPrintInfo(GetPageRangesFromPrintInfo());
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextMac::UpdatePrinterSettings(
+ const DictionaryValue& job_settings, const PageRanges& ranges) {
+ DCHECK(!in_print_job_);
+
+ // NOTE: Reset |print_info_| with a copy of |sharedPrintInfo| so as to start
+ // with a clean slate.
+ print_info_.reset([[NSPrintInfo sharedPrintInfo] copy]);
+
+ bool collate;
+ int color;
+ bool landscape;
+ bool print_to_pdf;
+ bool is_cloud_dialog;
+ int copies;
+ int duplex_mode;
+ std::string device_name;
+
+ if (!job_settings.GetBoolean(kSettingLandscape, &landscape) ||
+ !job_settings.GetBoolean(kSettingCollate, &collate) ||
+ !job_settings.GetInteger(kSettingColor, &color) ||
+ !job_settings.GetBoolean(kSettingPrintToPDF, &print_to_pdf) ||
+ !job_settings.GetInteger(kSettingDuplexMode, &duplex_mode) ||
+ !job_settings.GetInteger(kSettingCopies, &copies) ||
+ !job_settings.GetString(kSettingDeviceName, &device_name) ||
+ !job_settings.GetBoolean(kSettingCloudPrintDialog, &is_cloud_dialog)) {
+ return OnError();
+ }
+
+ bool print_to_cloud = job_settings.HasKey(kSettingCloudPrintId);
+ bool open_pdf_in_preview = job_settings.HasKey(kSettingOpenPDFInPreview);
+
+ if (!print_to_pdf && !print_to_cloud && !is_cloud_dialog) {
+ if (!SetPrinter(device_name))
+ return OnError();
+
+ if (!SetCopiesInPrintSettings(copies))
+ return OnError();
+
+ if (!SetCollateInPrintSettings(collate))
+ return OnError();
+
+ if (!SetDuplexModeInPrintSettings(
+ static_cast<DuplexMode>(duplex_mode))) {
+ return OnError();
+ }
+
+ if (!SetOutputColor(color))
+ return OnError();
+ }
+ if (open_pdf_in_preview) {
+ if (!SetPrintPreviewJob())
+ return OnError();
+ }
+
+ if (!UpdatePageFormatWithPaperInfo())
+ return OnError();
+
+ if (!SetOrientationIsLandscape(landscape))
+ return OnError();
+
+ [print_info_.get() updateFromPMPrintSettings];
+
+ InitPrintSettingsFromPrintInfo(ranges);
+ return OK;
+}
+
+bool PrintingContextMac::SetPrintPreviewJob() {
+ PMPrintSession print_session =
+ static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+ PMPrintSettings print_settings =
+ static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
+ return PMSessionSetDestination(
+ print_session, print_settings, kPMDestinationPreview,
+ NULL, NULL) == noErr;
+}
+
+void PrintingContextMac::InitPrintSettingsFromPrintInfo(
+ const PageRanges& ranges) {
+ PMPrintSession print_session =
+ static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+ PMPageFormat page_format =
+ static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
+ PMPrinter printer;
+ PMSessionGetCurrentPrinter(print_session, &printer);
+ PrintSettingsInitializerMac::InitPrintSettings(
+ printer, page_format, ranges, false, &settings_);
+}
+
+bool PrintingContextMac::SetPrinter(const std::string& device_name) {
+ DCHECK(print_info_.get());
+ PMPrintSession print_session =
+ static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+
+ PMPrinter current_printer;
+ if (PMSessionGetCurrentPrinter(print_session, &current_printer) != noErr)
+ return false;
+
+ CFStringRef current_printer_id = PMPrinterGetID(current_printer);
+ if (!current_printer_id)
+ return false;
+
+ base::ScopedCFTypeRef<CFStringRef> new_printer_id(
+ base::SysUTF8ToCFStringRef(device_name));
+ if (!new_printer_id.get())
+ return false;
+
+ if (CFStringCompare(new_printer_id.get(), current_printer_id, 0) ==
+ kCFCompareEqualTo) {
+ return true;
+ }
+
+ PMPrinter new_printer = PMPrinterCreateFromPrinterID(new_printer_id.get());
+ if (new_printer == NULL)
+ return false;
+
+ OSStatus status = PMSessionSetCurrentPMPrinter(print_session, new_printer);
+ PMRelease(new_printer);
+ return status == noErr;
+}
+
+bool PrintingContextMac::UpdatePageFormatWithPaperInfo() {
+ PMPrintSession print_session =
+ static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+
+ PMPageFormat default_page_format =
+ static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
+
+ PMPaper default_paper;
+ if (PMGetPageFormatPaper(default_page_format, &default_paper) != noErr)
+ return false;
+
+ double default_page_width = 0.0;
+ double default_page_height = 0.0;
+ if (PMPaperGetWidth(default_paper, &default_page_width) != noErr)
+ return false;
+
+ if (PMPaperGetHeight(default_paper, &default_page_height) != noErr)
+ return false;
+
+ PMPrinter current_printer = NULL;
+ if (PMSessionGetCurrentPrinter(print_session, &current_printer) != noErr)
+ return false;
+
+ if (current_printer == nil)
+ return false;
+
+ CFArrayRef paper_list = NULL;
+ if (PMPrinterGetPaperList(current_printer, &paper_list) != noErr)
+ return false;
+
+ double best_match = std::numeric_limits<double>::max();
+ PMPaper best_matching_paper = kPMNoData;
+ int num_papers = CFArrayGetCount(paper_list);
+ for (int i = 0; i < num_papers; ++i) {
+ PMPaper paper = (PMPaper)[(NSArray*)paper_list objectAtIndex: i];
+ double paper_width = 0.0;
+ double paper_height = 0.0;
+ PMPaperGetWidth(paper, &paper_width);
+ PMPaperGetHeight(paper, &paper_height);
+ double current_match = std::max(fabs(default_page_width - paper_width),
+ fabs(default_page_height - paper_height));
+ // Ignore paper sizes that are very different.
+ if (current_match > 2)
+ continue;
+ current_match += IsPaperNameEqual(paper, default_paper) ? 0 : 1;
+ if (current_match < best_match) {
+ best_matching_paper = paper;
+ best_match = current_match;
+ }
+ }
+
+ if (best_matching_paper == kPMNoData) {
+ PMPaper paper = kPMNoData;
+ // Create a custom paper for the specified default page size.
+ PMPaperMargins default_margins;
+ if (PMPaperGetMargins(default_paper, &default_margins) != noErr)
+ return false;
+
+ const PMPaperMargins margins =
+ {default_margins.top, default_margins.left, default_margins.bottom,
+ default_margins.right};
+ CFStringRef paper_id = CFSTR("Custom paper ID");
+ CFStringRef paper_name = CFSTR("Custom paper");
+ if (PMPaperCreateCustom(current_printer, paper_id, paper_name,
+ default_page_width, default_page_height, &margins, &paper) !=
+ noErr) {
+ return false;
+ }
+ [print_info_.get() updateFromPMPageFormat];
+ PMRelease(paper);
+ } else {
+ PMPageFormat chosen_page_format = NULL;
+ if (PMCreatePageFormat((PMPageFormat*) &chosen_page_format) != noErr)
+ return false;
+
+ // Create page format from that paper.
+ if (PMCreatePageFormatWithPMPaper(&chosen_page_format,
+ best_matching_paper) != noErr) {
+ PMRelease(chosen_page_format);
+ return false;
+ }
+ // Copy over the original format with the new page format.
+ if (PMCopyPageFormat(chosen_page_format, default_page_format) != noErr) {
+ PMRelease(chosen_page_format);
+ return false;
+ }
+ [print_info_.get() updateFromPMPageFormat];
+ PMRelease(chosen_page_format);
+ }
+ return true;
+}
+
+bool PrintingContextMac::SetCopiesInPrintSettings(int copies) {
+ if (copies < 1)
+ return false;
+
+ PMPrintSettings pmPrintSettings =
+ static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
+ return PMSetCopies(pmPrintSettings, copies, false) == noErr;
+}
+
+bool PrintingContextMac::SetCollateInPrintSettings(bool collate) {
+ PMPrintSettings pmPrintSettings =
+ static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
+ return PMSetCollate(pmPrintSettings, collate) == noErr;
+}
+
+bool PrintingContextMac::SetOrientationIsLandscape(bool landscape) {
+ PMPageFormat page_format =
+ static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
+
+ PMOrientation orientation = landscape ? kPMLandscape : kPMPortrait;
+
+ if (PMSetOrientation(page_format, orientation, false) != noErr)
+ return false;
+
+ PMPrintSession print_session =
+ static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+
+ PMSessionValidatePageFormat(print_session, page_format, kPMDontWantBoolean);
+
+ [print_info_.get() updateFromPMPageFormat];
+ return true;
+}
+
+bool PrintingContextMac::SetDuplexModeInPrintSettings(DuplexMode mode) {
+ PMDuplexMode duplexSetting;
+ switch (mode) {
+ case LONG_EDGE:
+ duplexSetting = kPMDuplexNoTumble;
+ break;
+ case SHORT_EDGE:
+ duplexSetting = kPMDuplexTumble;
+ break;
+ case SIMPLEX:
+ duplexSetting = kPMDuplexNone;
+ break;
+ default: // UNKNOWN_DUPLEX_MODE
+ return true;
+ }
+
+ PMPrintSettings pmPrintSettings =
+ static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
+ return PMSetDuplex(pmPrintSettings, duplexSetting) == noErr;
+}
+
+bool PrintingContextMac::SetOutputColor(int color_mode) {
+ PMPrintSettings pmPrintSettings =
+ static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
+ std::string color_setting_name;
+ std::string color_value;
+ GetColorModelForMode(color_mode, &color_setting_name, &color_value);
+ base::ScopedCFTypeRef<CFStringRef> color_setting(
+ base::SysUTF8ToCFStringRef(color_setting_name));
+ base::ScopedCFTypeRef<CFStringRef> output_color(
+ base::SysUTF8ToCFStringRef(color_value));
+
+ return PMPrintSettingsSetValue(pmPrintSettings,
+ color_setting.get(),
+ output_color.get(),
+ false) == noErr;
+}
+
+PageRanges PrintingContextMac::GetPageRangesFromPrintInfo() {
+ PageRanges page_ranges;
+ NSDictionary* print_info_dict = [print_info_.get() dictionary];
+ if (![[print_info_dict objectForKey:NSPrintAllPages] boolValue]) {
+ PageRange range;
+ range.from = [[print_info_dict objectForKey:NSPrintFirstPage] intValue] - 1;
+ range.to = [[print_info_dict objectForKey:NSPrintLastPage] intValue] - 1;
+ page_ranges.push_back(range);
+ }
+ return page_ranges;
+}
+
+PrintingContext::Result PrintingContextMac::InitWithSettings(
+ const PrintSettings& settings) {
+ DCHECK(!in_print_job_);
+
+ settings_ = settings;
+
+ NOTIMPLEMENTED();
+
+ return FAILED;
+}
+
+PrintingContext::Result PrintingContextMac::NewDocument(
+ const string16& document_name) {
+ DCHECK(!in_print_job_);
+
+ in_print_job_ = true;
+
+ PMPrintSession print_session =
+ static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+ PMPrintSettings print_settings =
+ static_cast<PMPrintSettings>([print_info_.get() PMPrintSettings]);
+ PMPageFormat page_format =
+ static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
+
+ base::ScopedCFTypeRef<CFStringRef> job_title(
+ base::SysUTF16ToCFStringRef(document_name));
+ PMPrintSettingsSetJobName(print_settings, job_title.get());
+
+ OSStatus status = PMSessionBeginCGDocumentNoDialog(print_session,
+ print_settings,
+ page_format);
+ if (status != noErr)
+ return OnError();
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextMac::NewPage() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+ DCHECK(!context_);
+
+ PMPrintSession print_session =
+ static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+ PMPageFormat page_format =
+ static_cast<PMPageFormat>([print_info_.get() PMPageFormat]);
+ OSStatus status;
+ status = PMSessionBeginPageNoDialog(print_session, page_format, NULL);
+ if (status != noErr)
+ return OnError();
+ status = PMSessionGetCGGraphicsContext(print_session, &context_);
+ if (status != noErr)
+ return OnError();
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextMac::PageDone() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+ DCHECK(context_);
+
+ PMPrintSession print_session =
+ static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+ OSStatus status = PMSessionEndPageNoDialog(print_session);
+ if (status != noErr)
+ OnError();
+ context_ = NULL;
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextMac::DocumentDone() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ PMPrintSession print_session =
+ static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+ OSStatus status = PMSessionEndDocumentNoDialog(print_session);
+ if (status != noErr)
+ OnError();
+
+ ResetSettings();
+ return OK;
+}
+
+void PrintingContextMac::Cancel() {
+ abort_printing_ = true;
+ in_print_job_ = false;
+ context_ = NULL;
+
+ PMPrintSession print_session =
+ static_cast<PMPrintSession>([print_info_.get() PMPrintSession]);
+ PMSessionEndPageNoDialog(print_session);
+}
+
+void PrintingContextMac::ReleaseContext() {
+ print_info_.reset();
+ context_ = NULL;
+}
+
+gfx::NativeDrawingContext PrintingContextMac::context() const {
+ return context_;
+}
+
+} // namespace printing
diff --git a/chromium/printing/printing_context_no_system_dialog.cc b/chromium/printing/printing_context_no_system_dialog.cc
new file mode 100644
index 00000000000..958d901700e
--- /dev/null
+++ b/chromium/printing/printing_context_no_system_dialog.cc
@@ -0,0 +1,158 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/printing_context_no_system_dialog.h"
+
+#include <unicode/ulocdata.h>
+
+#include "base/logging.h"
+#include "base/values.h"
+#include "printing/metafile.h"
+#include "printing/print_job_constants.h"
+#include "printing/units.h"
+
+namespace printing {
+
+// static
+PrintingContext* PrintingContext::Create(const std::string& app_locale) {
+ return static_cast<PrintingContext*>(
+ new PrintingContextNoSystemDialog(app_locale));
+}
+
+PrintingContextNoSystemDialog::PrintingContextNoSystemDialog(
+ const std::string& app_locale) : PrintingContext(app_locale) {
+}
+
+PrintingContextNoSystemDialog::~PrintingContextNoSystemDialog() {
+ ReleaseContext();
+}
+
+void PrintingContextNoSystemDialog::AskUserForSettings(
+ gfx::NativeView parent_view,
+ int max_pages,
+ bool has_selection,
+ const PrintSettingsCallback& callback) {
+ // We don't want to bring up a dialog here. Ever. Just signal the callback.
+ callback.Run(OK);
+}
+
+PrintingContext::Result PrintingContextNoSystemDialog::UseDefaultSettings() {
+ DCHECK(!in_print_job_);
+
+ ResetSettings();
+ // TODO(abodenha): Fetch these settings from the OS where possible. See
+ // bug 102583.
+ // TODO(sanjeevr): We need a better feedback loop between the cloud print
+ // dialog and this code.
+ int dpi = 300;
+ gfx::Size physical_size_device_units;
+ gfx::Rect printable_area_device_units;
+ int32_t width = 0;
+ int32_t height = 0;
+ UErrorCode error = U_ZERO_ERROR;
+ ulocdata_getPaperSize(app_locale_.c_str(), &height, &width, &error);
+ if (error > U_ZERO_ERROR) {
+ // If the call failed, assume a paper size of 8.5 x 11 inches.
+ LOG(WARNING) << "ulocdata_getPaperSize failed, using 8.5 x 11, error: "
+ << error;
+ width = static_cast<int>(8.5 * dpi);
+ height = static_cast<int>(11 * dpi);
+ } else {
+ // ulocdata_getPaperSize returns the width and height in mm.
+ // Convert this to pixels based on the dpi.
+ width = static_cast<int>(ConvertUnitDouble(width, 25.4, 1.0) * dpi);
+ height = static_cast<int>(ConvertUnitDouble(height, 25.4, 1.0) * dpi);
+ }
+
+ physical_size_device_units.SetSize(width, height);
+
+ // Assume full page is printable for now.
+ printable_area_device_units.SetRect(0, 0, width, height);
+
+ settings_.set_dpi(dpi);
+ settings_.SetPrinterPrintableArea(physical_size_device_units,
+ printable_area_device_units,
+ dpi);
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextNoSystemDialog::UpdatePrinterSettings(
+ const base::DictionaryValue& job_settings, const PageRanges& ranges) {
+ bool landscape = false;
+
+ if (!job_settings.GetBoolean(kSettingLandscape, &landscape))
+ return OnError();
+
+ if (settings_.dpi() == 0)
+ UseDefaultSettings();
+
+ settings_.SetOrientation(landscape);
+ settings_.ranges = ranges;
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextNoSystemDialog::InitWithSettings(
+ const PrintSettings& settings) {
+ DCHECK(!in_print_job_);
+
+ settings_ = settings;
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextNoSystemDialog::NewDocument(
+ const string16& document_name) {
+ DCHECK(!in_print_job_);
+ in_print_job_ = true;
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextNoSystemDialog::NewPage() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ // Intentional No-op.
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextNoSystemDialog::PageDone() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ // Intentional No-op.
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextNoSystemDialog::DocumentDone() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ ResetSettings();
+ return OK;
+}
+
+void PrintingContextNoSystemDialog::Cancel() {
+ abort_printing_ = true;
+ in_print_job_ = false;
+}
+
+void PrintingContextNoSystemDialog::ReleaseContext() {
+ // Intentional No-op.
+}
+
+gfx::NativeDrawingContext PrintingContextNoSystemDialog::context() const {
+ // Intentional No-op.
+ return NULL;
+}
+
+} // namespace printing
+
diff --git a/chromium/printing/printing_context_no_system_dialog.h b/chromium/printing/printing_context_no_system_dialog.h
new file mode 100644
index 00000000000..b88578ba526
--- /dev/null
+++ b/chromium/printing/printing_context_no_system_dialog.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PRINTING_CONTEXT_NO_SYSTEM_DIALOG_H_
+#define PRINTING_PRINTING_CONTEXT_NO_SYSTEM_DIALOG_H_
+
+#include <string>
+
+#include "printing/printing_context.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace printing {
+
+class PRINTING_EXPORT PrintingContextNoSystemDialog : public PrintingContext {
+ public:
+ explicit PrintingContextNoSystemDialog(const std::string& app_locale);
+ virtual ~PrintingContextNoSystemDialog();
+
+ // PrintingContext implementation.
+ virtual void AskUserForSettings(
+ gfx::NativeView parent_view,
+ int max_pages,
+ bool has_selection,
+ const PrintSettingsCallback& callback) OVERRIDE;
+ virtual Result UseDefaultSettings() OVERRIDE;
+ virtual Result UpdatePrinterSettings(
+ const base::DictionaryValue& job_settings,
+ const PageRanges& ranges) OVERRIDE;
+ virtual Result InitWithSettings(const PrintSettings& settings) OVERRIDE;
+ virtual Result NewDocument(const string16& document_name) OVERRIDE;
+ virtual Result NewPage() OVERRIDE;
+ virtual Result PageDone() OVERRIDE;
+ virtual Result DocumentDone() OVERRIDE;
+ virtual void Cancel() OVERRIDE;
+ virtual void ReleaseContext() OVERRIDE;
+ virtual gfx::NativeDrawingContext context() const OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrintingContextNoSystemDialog);
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINTING_CONTEXT_NO_SYSTEM_DIALOG_H_
diff --git a/chromium/printing/printing_context_win.cc b/chromium/printing/printing_context_win.cc
new file mode 100644
index 00000000000..d4f0b029190
--- /dev/null
+++ b/chromium/printing/printing_context_win.cc
@@ -0,0 +1,778 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/printing_context_win.h"
+
+#include <winspool.h>
+
+#include <algorithm>
+
+#include "base/i18n/file_util_icu.h"
+#include "base/i18n/time_formatting.h"
+#include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "base/win/metro.h"
+#include "printing/backend/print_backend.h"
+#include "printing/backend/printing_info_win.h"
+#include "printing/backend/win_helper.h"
+#include "printing/print_job_constants.h"
+#include "printing/print_settings_initializer_win.h"
+#include "printing/printed_document.h"
+#include "printing/units.h"
+#include "skia/ext/platform_device.h"
+#include "win8/util/win8_util.h"
+
+#if defined(USE_AURA)
+#include "ui/aura/remote_root_window_host_win.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/window.h"
+#endif
+
+using base::Time;
+
+namespace {
+
+// Constants for setting default PDF settings.
+const int kPDFDpi = 300; // 300 dpi
+// LETTER: 8.5 x 11 inches
+const int kPDFLetterWidth = 8.5 * kPDFDpi;
+const int kPDFLetterHeight = 11 * kPDFDpi;
+// LEGAL: 8.5 x 14 inches
+const int kPDFLegalWidth = 8.5 * kPDFDpi;
+const int kPDFLegalHeight = 14 * kPDFDpi;
+// A4: 8.27 x 11.69 inches
+const int kPDFA4Width = 8.27 * kPDFDpi;
+const int kPDFA4Height = 11.69 * kPDFDpi;
+// A3: 11.69 x 16.54 inches
+const int kPDFA3Width = 11.69 * kPDFDpi;
+const int kPDFA3Height = 16.54 * kPDFDpi;
+
+HWND GetRootWindow(gfx::NativeView view) {
+ HWND window = NULL;
+#if defined(USE_AURA)
+ if (view)
+ window = view->GetRootWindow()->GetAcceleratedWidget();
+#else
+ if (view && IsWindow(view)) {
+ window = GetAncestor(view, GA_ROOTOWNER);
+ }
+#endif
+ if (!window) {
+ // TODO(maruel): bug 1214347 Get the right browser window instead.
+ return GetDesktopWindow();
+ }
+ return window;
+}
+
+} // anonymous namespace
+
+namespace printing {
+
+class PrintingContextWin::CallbackHandler : public IPrintDialogCallback,
+ public IObjectWithSite {
+ public:
+ CallbackHandler(PrintingContextWin& owner, HWND owner_hwnd)
+ : owner_(owner),
+ owner_hwnd_(owner_hwnd),
+ services_(NULL) {
+ }
+
+ ~CallbackHandler() {
+ if (services_)
+ services_->Release();
+ }
+
+ IUnknown* ToIUnknown() {
+ return static_cast<IUnknown*>(static_cast<IPrintDialogCallback*>(this));
+ }
+
+ // IUnknown
+ virtual HRESULT WINAPI QueryInterface(REFIID riid, void**object) {
+ if (riid == IID_IUnknown) {
+ *object = ToIUnknown();
+ } else if (riid == IID_IPrintDialogCallback) {
+ *object = static_cast<IPrintDialogCallback*>(this);
+ } else if (riid == IID_IObjectWithSite) {
+ *object = static_cast<IObjectWithSite*>(this);
+ } else {
+ return E_NOINTERFACE;
+ }
+ return S_OK;
+ }
+
+ // No real ref counting.
+ virtual ULONG WINAPI AddRef() {
+ return 1;
+ }
+ virtual ULONG WINAPI Release() {
+ return 1;
+ }
+
+ // IPrintDialogCallback methods
+ virtual HRESULT WINAPI InitDone() {
+ return S_OK;
+ }
+
+ virtual HRESULT WINAPI SelectionChange() {
+ if (services_) {
+ // TODO(maruel): Get the devmode for the new printer with
+ // services_->GetCurrentDevMode(&devmode, &size), send that information
+ // back to our client and continue. The client needs to recalculate the
+ // number of rendered pages and send back this information here.
+ }
+ return S_OK;
+ }
+
+ virtual HRESULT WINAPI HandleMessage(HWND dialog,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam,
+ LRESULT* result) {
+ // Cheap way to retrieve the window handle.
+ if (!owner_.dialog_box_) {
+ // The handle we receive is the one of the groupbox in the General tab. We
+ // need to get the grand-father to get the dialog box handle.
+ owner_.dialog_box_ = GetAncestor(dialog, GA_ROOT);
+ // Trick to enable the owner window. This can cause issues with navigation
+ // events so it may have to be disabled if we don't fix the side-effects.
+ EnableWindow(owner_hwnd_, TRUE);
+ }
+ return S_FALSE;
+ }
+
+ virtual HRESULT WINAPI SetSite(IUnknown* site) {
+ if (!site) {
+ DCHECK(services_);
+ services_->Release();
+ services_ = NULL;
+ // The dialog box is destroying, PrintJob::Worker don't need the handle
+ // anymore.
+ owner_.dialog_box_ = NULL;
+ } else {
+ DCHECK(services_ == NULL);
+ HRESULT hr = site->QueryInterface(IID_IPrintDialogServices,
+ reinterpret_cast<void**>(&services_));
+ DCHECK(SUCCEEDED(hr));
+ }
+ return S_OK;
+ }
+
+ virtual HRESULT WINAPI GetSite(REFIID riid, void** site) {
+ return E_NOTIMPL;
+ }
+
+ private:
+ PrintingContextWin& owner_;
+ HWND owner_hwnd_;
+ IPrintDialogServices* services_;
+
+ DISALLOW_COPY_AND_ASSIGN(CallbackHandler);
+};
+
+// static
+PrintingContext* PrintingContext::Create(const std::string& app_locale) {
+ return static_cast<PrintingContext*>(new PrintingContextWin(app_locale));
+}
+
+PrintingContextWin::PrintingContextWin(const std::string& app_locale)
+ : PrintingContext(app_locale),
+ context_(NULL),
+ dialog_box_(NULL),
+ print_dialog_func_(&PrintDlgEx) {
+}
+
+PrintingContextWin::~PrintingContextWin() {
+ ReleaseContext();
+}
+
+// TODO(vitalybuka): Implement as ui::BaseShellDialog crbug.com/180997.
+void PrintingContextWin::AskUserForSettings(
+ gfx::NativeView view, int max_pages, bool has_selection,
+ const PrintSettingsCallback& callback) {
+ DCHECK(!in_print_job_);
+ if (win8::IsSingleWindowMetroMode()) {
+ // The system dialog can not be opened while running in Metro.
+ // But we can programatically launch the Metro print device charm though.
+ HMODULE metro_module = base::win::GetMetroModule();
+ if (metro_module != NULL) {
+ typedef void (*MetroShowPrintUI)();
+ MetroShowPrintUI metro_show_print_ui =
+ reinterpret_cast<MetroShowPrintUI>(
+ ::GetProcAddress(metro_module, "MetroShowPrintUI"));
+ if (metro_show_print_ui) {
+ // TODO(mad): Remove this once we can send user metrics from the metro
+ // driver. crbug.com/142330
+ UMA_HISTOGRAM_ENUMERATION("Metro.Print", 1, 2);
+ metro_show_print_ui();
+ }
+ }
+ return callback.Run(CANCEL);
+ }
+ dialog_box_dismissed_ = false;
+
+ HWND window = GetRootWindow(view);
+ DCHECK(window);
+
+ // Show the OS-dependent dialog box.
+ // If the user press
+ // - OK, the settings are reset and reinitialized with the new settings. OK is
+ // returned.
+ // - Apply then Cancel, the settings are reset and reinitialized with the new
+ // settings. CANCEL is returned.
+ // - Cancel, the settings are not changed, the previous setting, if it was
+ // initialized before, are kept. CANCEL is returned.
+ // On failure, the settings are reset and FAILED is returned.
+ PRINTDLGEX dialog_options = { sizeof(PRINTDLGEX) };
+ dialog_options.hwndOwner = window;
+ // Disable options we don't support currently.
+ // TODO(maruel): Reuse the previously loaded settings!
+ dialog_options.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE |
+ PD_NOCURRENTPAGE | PD_HIDEPRINTTOFILE;
+ if (!has_selection)
+ dialog_options.Flags |= PD_NOSELECTION;
+
+ PRINTPAGERANGE ranges[32];
+ dialog_options.nStartPage = START_PAGE_GENERAL;
+ if (max_pages) {
+ // Default initialize to print all the pages.
+ memset(ranges, 0, sizeof(ranges));
+ ranges[0].nFromPage = 1;
+ ranges[0].nToPage = max_pages;
+ dialog_options.nPageRanges = 1;
+ dialog_options.nMaxPageRanges = arraysize(ranges);
+ dialog_options.nMinPage = 1;
+ dialog_options.nMaxPage = max_pages;
+ dialog_options.lpPageRanges = ranges;
+ } else {
+ // No need to bother, we don't know how many pages are available.
+ dialog_options.Flags |= PD_NOPAGENUMS;
+ }
+
+ HRESULT hr = (*print_dialog_func_)(&dialog_options);
+ if (hr != S_OK) {
+ ResetSettings();
+ callback.Run(FAILED);
+ }
+
+ // TODO(maruel): Support PD_PRINTTOFILE.
+ callback.Run(ParseDialogResultEx(dialog_options));
+}
+
+PrintingContext::Result PrintingContextWin::UseDefaultSettings() {
+ DCHECK(!in_print_job_);
+
+ PRINTDLG dialog_options = { sizeof(PRINTDLG) };
+ dialog_options.Flags = PD_RETURNDC | PD_RETURNDEFAULT;
+ if (PrintDlg(&dialog_options))
+ return ParseDialogResult(dialog_options);
+
+ // No default printer configured, do we have any printers at all?
+ DWORD bytes_needed = 0;
+ DWORD count_returned = 0;
+ (void)::EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS,
+ NULL, 2, NULL, 0, &bytes_needed, &count_returned);
+ if (bytes_needed) {
+ DCHECK(bytes_needed >= count_returned * sizeof(PRINTER_INFO_2));
+ scoped_ptr<BYTE[]> printer_info_buffer(new BYTE[bytes_needed]);
+ BOOL ret = ::EnumPrinters(PRINTER_ENUM_LOCAL|PRINTER_ENUM_CONNECTIONS,
+ NULL, 2, printer_info_buffer.get(),
+ bytes_needed, &bytes_needed,
+ &count_returned);
+ if (ret && count_returned) { // have printers
+ // Open the first successfully found printer.
+ for (DWORD count = 0; count < count_returned; ++count) {
+ PRINTER_INFO_2* info_2 = reinterpret_cast<PRINTER_INFO_2*>(
+ printer_info_buffer.get() + count * sizeof(PRINTER_INFO_2));
+ std::wstring printer_name = info_2->pPrinterName;
+ if (info_2->pDevMode == NULL || printer_name.length() == 0)
+ continue;
+ if (!AllocateContext(printer_name, info_2->pDevMode, &context_))
+ break;
+ if (InitializeSettings(*info_2->pDevMode, printer_name,
+ NULL, 0, false)) {
+ break;
+ }
+ ReleaseContext();
+ }
+ if (context_)
+ return OK;
+ }
+ }
+
+ ResetSettings();
+ return FAILED;
+}
+
+PrintingContext::Result PrintingContextWin::UpdatePrinterSettings(
+ const DictionaryValue& job_settings,
+ const PageRanges& ranges) {
+ DCHECK(!in_print_job_);
+
+ bool collate;
+ int color;
+ bool landscape;
+ bool print_to_pdf;
+ bool is_cloud_dialog;
+ int copies;
+ int duplex_mode;
+ string16 device_name;
+
+ if (!job_settings.GetBoolean(kSettingLandscape, &landscape) ||
+ !job_settings.GetBoolean(kSettingCollate, &collate) ||
+ !job_settings.GetInteger(kSettingColor, &color) ||
+ !job_settings.GetBoolean(kSettingPrintToPDF, &print_to_pdf) ||
+ !job_settings.GetInteger(kSettingDuplexMode, &duplex_mode) ||
+ !job_settings.GetInteger(kSettingCopies, &copies) ||
+ !job_settings.GetString(kSettingDeviceName, &device_name) ||
+ !job_settings.GetBoolean(kSettingCloudPrintDialog, &is_cloud_dialog)) {
+ return OnError();
+ }
+
+ bool print_to_cloud = job_settings.HasKey(kSettingCloudPrintId);
+
+ if (print_to_pdf || print_to_cloud || is_cloud_dialog) {
+ // Default fallback to Letter size.
+ gfx::Size paper_size;
+ gfx::Rect paper_rect;
+ paper_size.SetSize(kPDFLetterWidth, kPDFLetterHeight);
+
+ // Get settings from locale. Paper type buffer length is at most 4.
+ const int paper_type_buffer_len = 4;
+ wchar_t paper_type_buffer[paper_type_buffer_len] = {0};
+ GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IPAPERSIZE, paper_type_buffer,
+ paper_type_buffer_len);
+ if (wcslen(paper_type_buffer)) { // The call succeeded.
+ int paper_code = _wtoi(paper_type_buffer);
+ switch (paper_code) {
+ case DMPAPER_LEGAL:
+ paper_size.SetSize(kPDFLegalWidth, kPDFLegalHeight);
+ break;
+ case DMPAPER_A4:
+ paper_size.SetSize(kPDFA4Width, kPDFA4Height);
+ break;
+ case DMPAPER_A3:
+ paper_size.SetSize(kPDFA3Width, kPDFA3Height);
+ break;
+ default: // DMPAPER_LETTER is used for default fallback.
+ break;
+ }
+ }
+ paper_rect.SetRect(0, 0, paper_size.width(), paper_size.height());
+ settings_.SetPrinterPrintableArea(paper_size, paper_rect, kPDFDpi);
+ settings_.set_dpi(kPDFDpi);
+ settings_.SetOrientation(landscape);
+ settings_.ranges = ranges;
+ return OK;
+ }
+
+ ScopedPrinterHandle printer;
+ LPWSTR device_name_wide = const_cast<wchar_t*>(device_name.c_str());
+ if (!printer.OpenPrinter(device_name_wide))
+ return OnError();
+
+ // Make printer changes local to Chrome.
+ // See MSDN documentation regarding DocumentProperties.
+ scoped_ptr<uint8[]> buffer;
+ DEVMODE* dev_mode = NULL;
+ LONG buffer_size = DocumentProperties(NULL, printer, device_name_wide,
+ NULL, NULL, 0);
+ if (buffer_size > 0) {
+ buffer.reset(new uint8[buffer_size]);
+ memset(buffer.get(), 0, buffer_size);
+ if (DocumentProperties(NULL, printer, device_name_wide,
+ reinterpret_cast<PDEVMODE>(buffer.get()), NULL,
+ DM_OUT_BUFFER) == IDOK) {
+ dev_mode = reinterpret_cast<PDEVMODE>(buffer.get());
+ }
+ }
+ if (dev_mode == NULL) {
+ buffer.reset();
+ return OnError();
+ }
+
+ if (color == GRAY)
+ dev_mode->dmColor = DMCOLOR_MONOCHROME;
+ else
+ dev_mode->dmColor = DMCOLOR_COLOR;
+
+ dev_mode->dmCopies = std::max(copies, 1);
+ if (dev_mode->dmCopies > 1) // do not change collate unless multiple copies
+ dev_mode->dmCollate = collate ? DMCOLLATE_TRUE : DMCOLLATE_FALSE;
+ switch (duplex_mode) {
+ case LONG_EDGE:
+ dev_mode->dmDuplex = DMDUP_VERTICAL;
+ break;
+ case SHORT_EDGE:
+ dev_mode->dmDuplex = DMDUP_HORIZONTAL;
+ break;
+ case SIMPLEX:
+ dev_mode->dmDuplex = DMDUP_SIMPLEX;
+ break;
+ default: // UNKNOWN_DUPLEX_MODE
+ break;
+ }
+ dev_mode->dmOrientation = landscape ? DMORIENT_LANDSCAPE : DMORIENT_PORTRAIT;
+
+ // Update data using DocumentProperties.
+ if (DocumentProperties(NULL, printer, device_name_wide, dev_mode, dev_mode,
+ DM_IN_BUFFER | DM_OUT_BUFFER) != IDOK) {
+ return OnError();
+ }
+
+ // Set printer then refresh printer settings.
+ if (!AllocateContext(device_name, dev_mode, &context_)) {
+ return OnError();
+ }
+ PrintSettingsInitializerWin::InitPrintSettings(context_, *dev_mode,
+ ranges, device_name,
+ false, &settings_);
+ return OK;
+}
+
+PrintingContext::Result PrintingContextWin::InitWithSettings(
+ const PrintSettings& settings) {
+ DCHECK(!in_print_job_);
+
+ settings_ = settings;
+
+ // TODO(maruel): settings_.ToDEVMODE()
+ ScopedPrinterHandle printer;
+ if (!printer.OpenPrinter(settings_.device_name().c_str())) {
+ return FAILED;
+ }
+
+ Result status = OK;
+
+ if (!GetPrinterSettings(printer, settings_.device_name()))
+ status = FAILED;
+
+ if (status != OK)
+ ResetSettings();
+ return status;
+}
+
+PrintingContext::Result PrintingContextWin::NewDocument(
+ const string16& document_name) {
+ DCHECK(!in_print_job_);
+ if (!context_)
+ return OnError();
+
+ // Set the flag used by the AbortPrintJob dialog procedure.
+ abort_printing_ = false;
+
+ in_print_job_ = true;
+
+ // Register the application's AbortProc function with GDI.
+ if (SP_ERROR == SetAbortProc(context_, &AbortProc))
+ return OnError();
+
+ DCHECK(PrintBackend::SimplifyDocumentTitle(document_name) == document_name);
+ DOCINFO di = { sizeof(DOCINFO) };
+ const std::wstring& document_name_wide = UTF16ToWide(document_name);
+ di.lpszDocName = document_name_wide.c_str();
+
+ // Is there a debug dump directory specified? If so, force to print to a file.
+ base::FilePath debug_dump_path = PrintedDocument::debug_dump_path();
+ if (!debug_dump_path.empty()) {
+ // Create a filename.
+ std::wstring filename;
+ Time now(Time::Now());
+ filename = base::TimeFormatShortDateNumeric(now);
+ filename += L"_";
+ filename += base::TimeFormatTimeOfDay(now);
+ filename += L"_";
+ filename += UTF16ToWide(document_name);
+ filename += L"_";
+ filename += L"buffer.prn";
+ file_util::ReplaceIllegalCharactersInPath(&filename, '_');
+ debug_dump_path.Append(filename);
+ di.lpszOutput = debug_dump_path.value().c_str();
+ }
+
+ // No message loop running in unit tests.
+ DCHECK(!base::MessageLoop::current() ||
+ !base::MessageLoop::current()->NestableTasksAllowed());
+
+ // Begin a print job by calling the StartDoc function.
+ // NOTE: StartDoc() starts a message loop. That causes a lot of problems with
+ // IPC. Make sure recursive task processing is disabled.
+ if (StartDoc(context_, &di) <= 0)
+ return OnError();
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextWin::NewPage() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(context_);
+ DCHECK(in_print_job_);
+
+ // Intentional No-op. NativeMetafile::SafePlayback takes care of calling
+ // ::StartPage().
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextWin::PageDone() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ // Intentional No-op. NativeMetafile::SafePlayback takes care of calling
+ // ::EndPage().
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContextWin::DocumentDone() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+ DCHECK(context_);
+
+ // Inform the driver that document has ended.
+ if (EndDoc(context_) <= 0)
+ return OnError();
+
+ ResetSettings();
+ return OK;
+}
+
+void PrintingContextWin::Cancel() {
+ abort_printing_ = true;
+ in_print_job_ = false;
+ if (context_)
+ CancelDC(context_);
+ if (dialog_box_) {
+ DestroyWindow(dialog_box_);
+ dialog_box_dismissed_ = true;
+ }
+}
+
+void PrintingContextWin::ReleaseContext() {
+ if (context_) {
+ DeleteDC(context_);
+ context_ = NULL;
+ }
+}
+
+gfx::NativeDrawingContext PrintingContextWin::context() const {
+ return context_;
+}
+
+// static
+BOOL PrintingContextWin::AbortProc(HDC hdc, int nCode) {
+ if (nCode) {
+ // TODO(maruel): Need a way to find the right instance to set. Should
+ // leverage PrintJobManager here?
+ // abort_printing_ = true;
+ }
+ return true;
+}
+
+bool PrintingContextWin::InitializeSettings(const DEVMODE& dev_mode,
+ const std::wstring& new_device_name,
+ const PRINTPAGERANGE* ranges,
+ int number_ranges,
+ bool selection_only) {
+ skia::InitializeDC(context_);
+ DCHECK(GetDeviceCaps(context_, CLIPCAPS));
+ DCHECK(GetDeviceCaps(context_, RASTERCAPS) & RC_STRETCHDIB);
+ DCHECK(GetDeviceCaps(context_, RASTERCAPS) & RC_BITMAP64);
+ // Some printers don't advertise these.
+ // DCHECK(GetDeviceCaps(context_, RASTERCAPS) & RC_SCALING);
+ // DCHECK(GetDeviceCaps(context_, SHADEBLENDCAPS) & SB_CONST_ALPHA);
+ // DCHECK(GetDeviceCaps(context_, SHADEBLENDCAPS) & SB_PIXEL_ALPHA);
+
+ // StretchDIBits() support is needed for printing.
+ if (!(GetDeviceCaps(context_, RASTERCAPS) & RC_STRETCHDIB) ||
+ !(GetDeviceCaps(context_, RASTERCAPS) & RC_BITMAP64)) {
+ NOTREACHED();
+ ResetSettings();
+ return false;
+ }
+
+ DCHECK(!in_print_job_);
+ DCHECK(context_);
+ PageRanges ranges_vector;
+ if (!selection_only) {
+ // Convert the PRINTPAGERANGE array to a PrintSettings::PageRanges vector.
+ ranges_vector.reserve(number_ranges);
+ for (int i = 0; i < number_ranges; ++i) {
+ PageRange range;
+ // Transfer from 1-based to 0-based.
+ range.from = ranges[i].nFromPage - 1;
+ range.to = ranges[i].nToPage - 1;
+ ranges_vector.push_back(range);
+ }
+ }
+
+ PrintSettingsInitializerWin::InitPrintSettings(context_,
+ dev_mode,
+ ranges_vector,
+ new_device_name,
+ selection_only,
+ &settings_);
+
+ return true;
+}
+
+bool PrintingContextWin::GetPrinterSettings(HANDLE printer,
+ const std::wstring& device_name) {
+ DCHECK(!in_print_job_);
+
+ UserDefaultDevMode user_settings;
+
+ if (!user_settings.Init(printer) ||
+ !AllocateContext(device_name, user_settings.get(), &context_)) {
+ ResetSettings();
+ return false;
+ }
+
+ return InitializeSettings(*user_settings.get(), device_name, NULL, 0, false);
+}
+
+// static
+bool PrintingContextWin::AllocateContext(const std::wstring& device_name,
+ const DEVMODE* dev_mode,
+ gfx::NativeDrawingContext* context) {
+ *context = CreateDC(L"WINSPOOL", device_name.c_str(), NULL, dev_mode);
+ DCHECK(*context);
+ return *context != NULL;
+}
+
+PrintingContext::Result PrintingContextWin::ParseDialogResultEx(
+ const PRINTDLGEX& dialog_options) {
+ // If the user clicked OK or Apply then Cancel, but not only Cancel.
+ if (dialog_options.dwResultAction != PD_RESULT_CANCEL) {
+ // Start fresh.
+ ResetSettings();
+
+ DEVMODE* dev_mode = NULL;
+ if (dialog_options.hDevMode) {
+ dev_mode =
+ reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode));
+ DCHECK(dev_mode);
+ }
+
+ std::wstring device_name;
+ if (dialog_options.hDevNames) {
+ DEVNAMES* dev_names =
+ reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames));
+ DCHECK(dev_names);
+ if (dev_names) {
+ device_name =
+ reinterpret_cast<const wchar_t*>(
+ reinterpret_cast<const wchar_t*>(dev_names) +
+ dev_names->wDeviceOffset);
+ GlobalUnlock(dialog_options.hDevNames);
+ }
+ }
+
+ bool success = false;
+ if (dev_mode && !device_name.empty()) {
+ context_ = dialog_options.hDC;
+ PRINTPAGERANGE* page_ranges = NULL;
+ DWORD num_page_ranges = 0;
+ bool print_selection_only = false;
+ if (dialog_options.Flags & PD_PAGENUMS) {
+ page_ranges = dialog_options.lpPageRanges;
+ num_page_ranges = dialog_options.nPageRanges;
+ }
+ if (dialog_options.Flags & PD_SELECTION) {
+ print_selection_only = true;
+ }
+ success = InitializeSettings(*dev_mode,
+ device_name,
+ page_ranges,
+ num_page_ranges,
+ print_selection_only);
+ }
+
+ if (!success && dialog_options.hDC) {
+ DeleteDC(dialog_options.hDC);
+ context_ = NULL;
+ }
+
+ if (dev_mode) {
+ GlobalUnlock(dialog_options.hDevMode);
+ }
+ } else {
+ if (dialog_options.hDC) {
+ DeleteDC(dialog_options.hDC);
+ }
+ }
+
+ if (dialog_options.hDevMode != NULL)
+ GlobalFree(dialog_options.hDevMode);
+ if (dialog_options.hDevNames != NULL)
+ GlobalFree(dialog_options.hDevNames);
+
+ switch (dialog_options.dwResultAction) {
+ case PD_RESULT_PRINT:
+ return context_ ? OK : FAILED;
+ case PD_RESULT_APPLY:
+ return context_ ? CANCEL : FAILED;
+ case PD_RESULT_CANCEL:
+ return CANCEL;
+ default:
+ return FAILED;
+ }
+}
+
+PrintingContext::Result PrintingContextWin::ParseDialogResult(
+ const PRINTDLG& dialog_options) {
+ // If the user clicked OK or Apply then Cancel, but not only Cancel.
+ // Start fresh.
+ ResetSettings();
+
+ DEVMODE* dev_mode = NULL;
+ if (dialog_options.hDevMode) {
+ dev_mode =
+ reinterpret_cast<DEVMODE*>(GlobalLock(dialog_options.hDevMode));
+ DCHECK(dev_mode);
+ }
+
+ std::wstring device_name;
+ if (dialog_options.hDevNames) {
+ DEVNAMES* dev_names =
+ reinterpret_cast<DEVNAMES*>(GlobalLock(dialog_options.hDevNames));
+ DCHECK(dev_names);
+ if (dev_names) {
+ device_name =
+ reinterpret_cast<const wchar_t*>(
+ reinterpret_cast<const wchar_t*>(dev_names) +
+ dev_names->wDeviceOffset);
+ GlobalUnlock(dialog_options.hDevNames);
+ }
+ }
+
+ bool success = false;
+ if (dev_mode && !device_name.empty()) {
+ context_ = dialog_options.hDC;
+ success = InitializeSettings(*dev_mode, device_name, NULL, 0, false);
+ }
+
+ if (!success && dialog_options.hDC) {
+ DeleteDC(dialog_options.hDC);
+ context_ = NULL;
+ }
+
+ if (dev_mode) {
+ GlobalUnlock(dialog_options.hDevMode);
+ }
+
+ if (dialog_options.hDevMode != NULL)
+ GlobalFree(dialog_options.hDevMode);
+ if (dialog_options.hDevNames != NULL)
+ GlobalFree(dialog_options.hDevNames);
+
+ return context_ ? OK : FAILED;
+}
+
+} // namespace printing
diff --git a/chromium/printing/printing_context_win.h b/chromium/printing/printing_context_win.h
new file mode 100644
index 00000000000..a4773d04f79
--- /dev/null
+++ b/chromium/printing/printing_context_win.h
@@ -0,0 +1,96 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PRINTING_CONTEXT_WIN_H_
+#define PRINTING_PRINTING_CONTEXT_WIN_H_
+
+#include <ocidl.h>
+#include <commdlg.h>
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "build/build_config.h"
+#include "printing/printing_context.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace printing {
+
+class PRINTING_EXPORT PrintingContextWin : public PrintingContext {
+ public:
+ explicit PrintingContextWin(const std::string& app_locale);
+ ~PrintingContextWin();
+
+ // PrintingContext implementation.
+ virtual void AskUserForSettings(
+ gfx::NativeView parent_view,
+ int max_pages,
+ bool has_selection,
+ const PrintSettingsCallback& callback) OVERRIDE;
+ virtual Result UseDefaultSettings() OVERRIDE;
+ virtual Result UpdatePrinterSettings(
+ const base::DictionaryValue& job_settings,
+ const PageRanges& ranges) OVERRIDE;
+ virtual Result InitWithSettings(const PrintSettings& settings) OVERRIDE;
+ virtual Result NewDocument(const string16& document_name) OVERRIDE;
+ virtual Result NewPage() OVERRIDE;
+ virtual Result PageDone() OVERRIDE;
+ virtual Result DocumentDone() OVERRIDE;
+ virtual void Cancel() OVERRIDE;
+ virtual void ReleaseContext() OVERRIDE;
+ virtual gfx::NativeDrawingContext context() const OVERRIDE;
+
+#if defined(UNIT_TEST) || defined(PRINTING_IMPLEMENTATION)
+ // Sets a fake PrintDlgEx function pointer in tests.
+ void SetPrintDialog(HRESULT (__stdcall *print_dialog_func)(LPPRINTDLGEX)) {
+ print_dialog_func_ = print_dialog_func;
+ }
+#endif // defined(UNIT_TEST)
+
+ // Allocates the HDC for a specific DEVMODE.
+ static bool AllocateContext(const std::wstring& printer_name,
+ const DEVMODE* dev_mode,
+ gfx::NativeDrawingContext* context);
+
+ private:
+ // Class that manages the PrintDlgEx() callbacks. This is meant to be a
+ // temporary object used during the Print... dialog display.
+ class CallbackHandler;
+
+ // Used in response to the user canceling the printing.
+ static BOOL CALLBACK AbortProc(HDC hdc, int nCode);
+
+ // Reads the settings from the selected device context. Updates settings_ and
+ // its margins.
+ bool InitializeSettings(const DEVMODE& dev_mode,
+ const std::wstring& new_device_name,
+ const PRINTPAGERANGE* ranges,
+ int number_ranges,
+ bool selection_only);
+
+ // Retrieves the printer's default low-level settings. On Windows, context_ is
+ // allocated with this call.
+ bool GetPrinterSettings(HANDLE printer,
+ const std::wstring& device_name);
+
+ // Parses the result of a PRINTDLGEX result.
+ Result ParseDialogResultEx(const PRINTDLGEX& dialog_options);
+ Result ParseDialogResult(const PRINTDLG& dialog_options);
+
+ // The selected printer context.
+ HDC context_;
+
+ // The dialog box for the time it is shown.
+ volatile HWND dialog_box_;
+
+ // Function pointer that defaults to PrintDlgEx. It can be changed using
+ // SetPrintDialog() in tests.
+ HRESULT (__stdcall *print_dialog_func_)(LPPRINTDLGEX);
+
+ DISALLOW_COPY_AND_ASSIGN(PrintingContextWin);
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINTING_CONTEXT_WIN_H_
diff --git a/chromium/printing/printing_context_win_unittest.cc b/chromium/printing/printing_context_win_unittest.cc
new file mode 100644
index 00000000000..5c6d1665549
--- /dev/null
+++ b/chromium/printing/printing_context_win_unittest.cc
@@ -0,0 +1,174 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <ocidl.h>
+#include <commdlg.h>
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/scoped_ptr.h"
+#include "printing/backend/printing_info_win.h"
+#include "printing/printing_test.h"
+#include "printing/printing_context.h"
+#include "printing/printing_context_win.h"
+#include "printing/print_settings.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// This test is automatically disabled if no printer is available.
+class PrintingContextTest : public PrintingTest<testing::Test> {
+ public:
+ void PrintSettingsCallback(printing::PrintingContext::Result result) {
+ result_ = result;
+ }
+
+ protected:
+ printing::PrintingContext::Result result() const { return result_; }
+
+ private:
+ printing::PrintingContext::Result result_;
+};
+
+// This is a fake PrintDlgEx implementation that sets the right fields in
+// |lppd| to trigger a bug in older revisions of PrintingContext.
+HRESULT WINAPI PrintDlgExMock(LPPRINTDLGEX lppd) {
+ // The interesting bits:
+ // Pretend the user hit print
+ lppd->dwResultAction = PD_RESULT_PRINT;
+
+ // Pretend the page range is 1-5, but since lppd->Flags does not have
+ // PD_SELECTION set, this really shouldn't matter.
+ lppd->nPageRanges = 1;
+ lppd->lpPageRanges[0].nFromPage = 1;
+ lppd->lpPageRanges[0].nToPage = 5;
+
+ // Painful paperwork.
+ std::wstring printer_name = PrintingContextTest::GetDefaultPrinter();
+ HANDLE printer;
+ if (!OpenPrinter(const_cast<wchar_t*>(printer_name.c_str()), &printer, NULL))
+ return E_FAIL;
+
+ scoped_ptr<uint8[]> buffer;
+ const DEVMODE* dev_mode = NULL;
+ HRESULT result = S_OK;
+ lppd->hDC = NULL;
+ lppd->hDevMode = NULL;
+ lppd->hDevNames = NULL;
+
+ printing::PrinterInfo2 info_2;
+ if (info_2.Init(printer)) {
+ dev_mode = info_2.get()->pDevMode;
+ }
+ if (!dev_mode) {
+ result = E_FAIL;
+ goto Cleanup;
+ }
+
+ if (!printing::PrintingContextWin::AllocateContext(printer_name, dev_mode,
+ &lppd->hDC)) {
+ result = E_FAIL;
+ goto Cleanup;
+ }
+
+ size_t dev_mode_size = dev_mode->dmSize + dev_mode->dmDriverExtra;
+ lppd->hDevMode = GlobalAlloc(GMEM_MOVEABLE, dev_mode_size);
+ if (!lppd->hDevMode) {
+ result = E_FAIL;
+ goto Cleanup;
+ }
+ void* dev_mode_ptr = GlobalLock(lppd->hDevMode);
+ if (!dev_mode_ptr) {
+ result = E_FAIL;
+ goto Cleanup;
+ }
+ memcpy(dev_mode_ptr, dev_mode, dev_mode_size);
+ GlobalUnlock(lppd->hDevMode);
+ dev_mode_ptr = NULL;
+
+ size_t driver_size = 2 + sizeof(wchar_t) * lstrlen(info_2.get()->pDriverName);
+ size_t printer_size = 2 + sizeof(wchar_t) *
+ lstrlen(info_2.get()->pPrinterName);
+ size_t port_size = 2 + sizeof(wchar_t) * lstrlen(info_2.get()->pPortName);
+ size_t dev_names_size = sizeof(DEVNAMES) + driver_size + printer_size +
+ port_size;
+ lppd->hDevNames = GlobalAlloc(GHND, dev_names_size);
+ if (!lppd->hDevNames) {
+ result = E_FAIL;
+ goto Cleanup;
+ }
+ void* dev_names_ptr = GlobalLock(lppd->hDevNames);
+ if (!dev_names_ptr) {
+ result = E_FAIL;
+ goto Cleanup;
+ }
+ DEVNAMES* dev_names = reinterpret_cast<DEVNAMES*>(dev_names_ptr);
+ dev_names->wDefault = 1;
+ dev_names->wDriverOffset = sizeof(DEVNAMES);
+ memcpy(reinterpret_cast<uint8*>(dev_names_ptr) + dev_names->wDriverOffset,
+ info_2.get()->pDriverName, driver_size);
+ dev_names->wDeviceOffset = dev_names->wDriverOffset + driver_size;
+ memcpy(reinterpret_cast<uint8*>(dev_names_ptr) + dev_names->wDeviceOffset,
+ info_2.get()->pPrinterName, printer_size);
+ dev_names->wOutputOffset = dev_names->wDeviceOffset + printer_size;
+ memcpy(reinterpret_cast<uint8*>(dev_names_ptr) + dev_names->wOutputOffset,
+ info_2.get()->pPortName, port_size);
+ GlobalUnlock(lppd->hDevNames);
+ dev_names_ptr = NULL;
+
+Cleanup:
+ // Note: This section does proper deallocation/free of DC/global handles. We
+ // did not use ScopedHGlobal or ScopedHandle because they did not
+ // perform what we need. Goto's are used based on Windows programming
+ // idiom, to avoid deeply nested if's, and try-catch-finally is not
+ // allowed in Chromium.
+ if (FAILED(result)) {
+ if (lppd->hDC) {
+ DeleteDC(lppd->hDC);
+ }
+ if (lppd->hDevMode) {
+ GlobalFree(lppd->hDevMode);
+ }
+ if (lppd->hDevNames) {
+ GlobalFree(lppd->hDevNames);
+ }
+ }
+ ClosePrinter(printer);
+ return result;
+}
+
+TEST_F(PrintingContextTest, Base) {
+ // Sometimes ::GetDefaultPrinter() fails? bug 61509.
+ if (IsTestCaseDisabled())
+ return;
+
+ printing::PrintSettings settings;
+ settings.set_device_name(GetDefaultPrinter());
+ // Initialize it.
+ scoped_ptr<printing::PrintingContext> context(
+ printing::PrintingContext::Create(std::string()));
+ EXPECT_EQ(printing::PrintingContext::OK, context->InitWithSettings(settings));
+
+ // The print may lie to use and may not support world transformation.
+ // Verify right now.
+ XFORM random_matrix = { 1, 0.1f, 0, 1.5f, 0, 1 };
+ EXPECT_TRUE(SetWorldTransform(context->context(), &random_matrix));
+ EXPECT_TRUE(ModifyWorldTransform(context->context(), NULL, MWT_IDENTITY));
+}
+
+TEST_F(PrintingContextTest, PrintAll) {
+ // Sometimes ::GetDefaultPrinter() fails? bug 61509.
+ if (IsTestCaseDisabled())
+ return;
+
+ std::string dummy_locale;
+ printing::PrintingContextWin context(dummy_locale);
+ context.SetPrintDialog(&PrintDlgExMock);
+ context.AskUserForSettings(
+ NULL, 123, false, base::Bind(&PrintingContextTest::PrintSettingsCallback,
+ base::Unretained(this)));
+ EXPECT_EQ(printing::PrintingContext::OK, result());
+ printing::PrintSettings settings = context.settings();
+ EXPECT_EQ(settings.ranges.size(), 0);
+}
diff --git a/chromium/printing/printing_export.h b/chromium/printing/printing_export.h
new file mode 100644
index 00000000000..a8371910d47
--- /dev/null
+++ b/chromium/printing/printing_export.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_PRINTING_EXPORT_H_
+#define PRINTING_PRINTING_EXPORT_H_
+
+#if defined(COMPONENT_BUILD)
+#if defined(WIN32)
+
+#if defined(PRINTING_IMPLEMENTATION)
+#define PRINTING_EXPORT __declspec(dllexport)
+#else
+#define PRINTING_EXPORT __declspec(dllimport)
+#endif // defined(PRINTING_IMPLEMENTATION)
+
+#else // defined(WIN32)
+#if defined(PRINTING_IMPLEMENTATION)
+#define PRINTING_EXPORT __attribute__((visibility("default")))
+#else
+#define PRINTING_EXPORT
+#endif
+#endif
+
+#else // defined(COMPONENT_BUILD)
+#define PRINTING_EXPORT
+#endif
+
+#endif // PRINTING_PRINTING_EXPORT_H_
diff --git a/chromium/printing/printing_test.h b/chromium/printing/printing_test.h
new file mode 100644
index 00000000000..35b91697a0a
--- /dev/null
+++ b/chromium/printing/printing_test.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_PRINTING_PRINTING_TEST_H__
+#define CHROME_BROWSER_PRINTING_PRINTING_TEST_H__
+
+#include <windows.h>
+#include <winspool.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+
+// Disable the whole test case when executing on a computer that has no printer
+// installed.
+// Note: Parent should be testing::Test or InProcessBrowserTest.
+template<typename Parent>
+class PrintingTest : public Parent {
+ public:
+ static bool IsTestCaseDisabled() {
+ return GetDefaultPrinter().empty();
+ }
+ static std::wstring GetDefaultPrinter() {
+ wchar_t printer_name[MAX_PATH];
+ DWORD size = arraysize(printer_name);
+ BOOL result = ::GetDefaultPrinter(printer_name, &size);
+ if (result == 0) {
+ if (GetLastError() == ERROR_FILE_NOT_FOUND) {
+ printf("There is no printer installed, printing can't be tested!\n");
+ return std::wstring();
+ }
+ printf("INTERNAL PRINTER ERROR!\n");
+ return std::wstring();
+ }
+ return printer_name;
+ }
+};
+
+#endif // CHROME_BROWSER_PRINTING_PRINTING_TEST_H__
diff --git a/chromium/printing/run_all_unittests.cc b/chromium/printing/run_all_unittests.cc
new file mode 100644
index 00000000000..b715a328b99
--- /dev/null
+++ b/chromium/printing/run_all_unittests.cc
@@ -0,0 +1,9 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/test/test_suite.h"
+
+int main(int argc, char** argv) {
+ return base::TestSuite(argc, argv).Run();
+}
diff --git a/chromium/printing/units.cc b/chromium/printing/units.cc
new file mode 100644
index 00000000000..dcee9ea56af
--- /dev/null
+++ b/chromium/printing/units.cc
@@ -0,0 +1,55 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/units.h"
+
+#include "base/logging.h"
+#include "printing/print_job_constants.h"
+
+namespace printing {
+
+int ConvertUnit(int value, int old_unit, int new_unit) {
+ DCHECK_GT(new_unit, 0);
+ DCHECK_GT(old_unit, 0);
+ // With integer arithmetic, to divide a value with correct rounding, you need
+ // to add half of the divisor value to the dividend value. You need to do the
+ // reverse with negative number.
+ if (value >= 0) {
+ return ((value * new_unit) + (old_unit / 2)) / old_unit;
+ } else {
+ return ((value * new_unit) - (old_unit / 2)) / old_unit;
+ }
+}
+
+double ConvertUnitDouble(double value, double old_unit, double new_unit) {
+ DCHECK_GT(new_unit, 0);
+ DCHECK_GT(old_unit, 0);
+ return value * new_unit / old_unit;
+}
+
+int ConvertMilliInchToHundredThousanthMeter(int milli_inch) {
+ // 1" == 25.4 mm
+ // 1" == 25400 um
+ // 0.001" == 25.4 um
+ // 0.001" == 2.54 cmm
+ return ConvertUnit(milli_inch, 100, 254);
+}
+
+int ConvertHundredThousanthMeterToMilliInch(int cmm) {
+ return ConvertUnit(cmm, 254, 100);
+}
+
+int ConvertPixelsToPoint(int pixels) {
+ return ConvertUnit(pixels, kPixelsPerInch, kPointsPerInch);
+}
+
+double ConvertPixelsToPointDouble(double pixels) {
+ return ConvertUnitDouble(pixels, kPixelsPerInch, kPointsPerInch);
+}
+
+double ConvertPointsToPixelDouble(double points) {
+ return ConvertUnitDouble(points, kPointsPerInch, kPixelsPerInch);
+}
+
+} // namespace printing
diff --git a/chromium/printing/units.h b/chromium/printing/units.h
new file mode 100644
index 00000000000..8f028324301
--- /dev/null
+++ b/chromium/printing/units.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef PRINTING_UNITS_H_
+#define PRINTING_UNITS_H_
+
+#include "printing/printing_export.h"
+
+namespace printing {
+
+// Length of a thousanth of inches in 0.01mm unit.
+const int kHundrethsMMPerInch = 2540;
+
+// Length of an inch in CSS's 1pt unit.
+// http://dev.w3.org/csswg/css3-values/#absolute-length-units-cm-mm.-in-pt-pc
+const int kPointsPerInch = 72;
+
+// Length of an inch in CSS's 1px unit.
+// http://dev.w3.org/csswg/css3-values/#the-px-unit
+const int kPixelsPerInch = 96;
+
+// Converts from one unit system to another using integer arithmetics.
+PRINTING_EXPORT int ConvertUnit(int value, int old_unit, int new_unit);
+
+// Converts from one unit system to another using doubles.
+PRINTING_EXPORT double ConvertUnitDouble(double value, double old_unit,
+ double new_unit);
+
+// Converts from 0.001 inch unit to 0.00001 meter.
+PRINTING_EXPORT int ConvertMilliInchToHundredThousanthMeter(int milli_inch);
+
+// Converts from 0.00001 meter unit to 0.001 inch.
+PRINTING_EXPORT int ConvertHundredThousanthMeterToMilliInch(int cmm);
+
+// Converts from 1 pixel to 1 point using integers.
+PRINTING_EXPORT int ConvertPixelsToPoint(int pixels);
+
+// Converts from 1 pixel to 1 point using doubles.
+PRINTING_EXPORT double ConvertPixelsToPointDouble(double pixels);
+
+// Converts from 1 point to 1 pixel using doubles.
+double ConvertPointsToPixelDouble(double points);
+
+} // namespace printing
+
+#endif // PRINTING_UNITS_H_
diff --git a/chromium/printing/units_unittest.cc b/chromium/printing/units_unittest.cc
new file mode 100644
index 00000000000..fe160b3f405
--- /dev/null
+++ b/chromium/printing/units_unittest.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/units.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gtest/include/gtest/gtest-spi.h"
+
+namespace printing {
+
+TEST(UnitsTest, Convertions) {
+ EXPECT_EQ(100, ConvertUnit(100, 100, 100));
+ EXPECT_EQ(-100, ConvertUnit(-100, 100, 100));
+ EXPECT_EQ(0, ConvertUnit(0, 100, 100));
+ EXPECT_EQ(99, ConvertUnit(99, 100, 100));
+ EXPECT_EQ(101, ConvertUnit(101, 100, 100));
+ EXPECT_EQ(99900, ConvertUnit(999, 10, 1000));
+ EXPECT_EQ(100100, ConvertUnit(1001, 10, 1000));
+
+ // Rounding.
+ EXPECT_EQ(10, ConvertUnit(999, 1000, 10));
+ EXPECT_EQ(10, ConvertUnit(950, 1000, 10));
+ EXPECT_EQ(9, ConvertUnit(949, 1000, 10));
+ EXPECT_EQ(10, ConvertUnit(1001, 1000, 10));
+ EXPECT_EQ(10, ConvertUnit(1049, 1000, 10));
+ EXPECT_EQ(11, ConvertUnit(1050, 1000, 10));
+ EXPECT_EQ(-10, ConvertUnit(-999, 1000, 10));
+ EXPECT_EQ(-10, ConvertUnit(-950, 1000, 10));
+ EXPECT_EQ(-9, ConvertUnit(-949, 1000, 10));
+ EXPECT_EQ(-10, ConvertUnit(-1001, 1000, 10));
+ EXPECT_EQ(-10, ConvertUnit(-1049, 1000, 10));
+ EXPECT_EQ(-11, ConvertUnit(-1050, 1000, 10));
+
+ EXPECT_EQ(0, ConvertUnit(2, 1000000000, 1));
+ EXPECT_EQ(2000000000, ConvertUnit(2, 1, 1000000000));
+ EXPECT_EQ(4000000000U,
+ static_cast<unsigned int>(ConvertUnit(2, 1, 2000000000)));
+
+ EXPECT_EQ(100, ConvertUnitDouble(100, 100, 100));
+ EXPECT_EQ(-100, ConvertUnitDouble(-100, 100, 100));
+ EXPECT_EQ(0, ConvertUnitDouble(0, 100, 100));
+ EXPECT_DOUBLE_EQ(0.000002, ConvertUnitDouble(2, 1000, 0.001));
+ EXPECT_EQ(2000000, ConvertUnitDouble(2, 0.001, 1000));
+
+ EXPECT_EQ(kHundrethsMMPerInch, ConvertMilliInchToHundredThousanthMeter(1000));
+ EXPECT_EQ(-kHundrethsMMPerInch,
+ ConvertMilliInchToHundredThousanthMeter(-1000));
+ EXPECT_EQ(0, ConvertMilliInchToHundredThousanthMeter(0));
+ EXPECT_EQ(1000, ConvertHundredThousanthMeterToMilliInch(kHundrethsMMPerInch));
+ EXPECT_EQ(-1000,
+ ConvertHundredThousanthMeterToMilliInch(-kHundrethsMMPerInch));
+ EXPECT_EQ(0, ConvertHundredThousanthMeterToMilliInch(0));
+
+ EXPECT_EQ(8.25, ConvertPixelsToPointDouble(11.0));
+ // Round down.
+ EXPECT_EQ(8, ConvertPixelsToPoint(11));
+ EXPECT_EQ(7.5, ConvertPixelsToPointDouble(10.0));
+ // Round up.
+ EXPECT_EQ(8, ConvertPixelsToPoint(10));
+ EXPECT_EQ(0, ConvertPixelsToPoint(0));
+}
+
+} // namespace printing