summaryrefslogtreecommitdiffstats
path: root/printing
diff options
context:
space:
mode:
authorsverrir@google.com <sverrir@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-07 21:31:39 +0000
committersverrir@google.com <sverrir@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-07 21:31:39 +0000
commit8ff1d42631e79e842669dc3051d91ed7db80f1dc (patch)
tree55c1987549e982bfbf60a33fad3b60b5113d7864 /printing
parent395f9295b3e370746846dbe6073a0d43ab7e4af5 (diff)
downloadchromium_src-8ff1d42631e79e842669dc3051d91ed7db80f1dc.zip
chromium_src-8ff1d42631e79e842669dc3051d91ed7db80f1dc.tar.gz
chromium_src-8ff1d42631e79e842669dc3051d91ed7db80f1dc.tar.bz2
Move printing related stuff to the root printing project from the browser project. This simplifies further refactoring and eases understanding of the printing part of Chrome.
Also renamed win_printing_context to printing_context_win (correct naming convention) and added stub implementations for _linux and mac. Now all but one file is compiling on all platforms. TEST=none (no functional change). BUG=none Review URL: http://codereview.chromium.org/149212 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@20086 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'printing')
-rw-r--r--printing/DEPS3
-rw-r--r--printing/emf_win.h6
-rw-r--r--printing/emf_win_unittest.cc2
-rw-r--r--printing/native_metafile.h10
-rw-r--r--printing/page_number.cc83
-rw-r--r--printing/page_number.h73
-rw-r--r--printing/page_number_unittest.cc30
-rw-r--r--printing/page_overlays.cc206
-rw-r--r--printing/page_overlays.h80
-rw-r--r--printing/page_overlays_unittest.cc92
-rw-r--r--printing/page_range.cc31
-rw-r--r--printing/page_range.h34
-rw-r--r--printing/page_range_unittest.cc38
-rw-r--r--printing/page_setup.cc126
-rw-r--r--printing/page_setup.h82
-rw-r--r--printing/page_setup_unittest.cc146
-rw-r--r--printing/print_settings.cc116
-rw-r--r--printing/print_settings.h107
-rw-r--r--printing/printed_document.cc366
-rw-r--r--printing/printed_document.h192
-rw-r--r--printing/printed_page.cc24
-rw-r--r--printing/printed_page.h50
-rw-r--r--printing/printed_pages_source.h29
-rw-r--r--printing/printing.gyp50
-rw-r--r--printing/printing_context.h152
-rw-r--r--printing/printing_context_linux.cc117
-rw-r--r--printing/printing_context_mac.cc117
-rw-r--r--printing/printing_context_win.cc607
-rw-r--r--printing/printing_context_win_unittest.cc28
-rw-r--r--printing/printing_test.h36
30 files changed, 3022 insertions, 11 deletions
diff --git a/printing/DEPS b/printing/DEPS
index 5cd0867..64b2aab 100644
--- a/printing/DEPS
+++ b/printing/DEPS
@@ -1,3 +1,6 @@
include_rules = [
+ "+app/gfx", # Font's are here.
+ "+app", # win_util::FormatSystemTime/Date.
"+base",
+ "+skia/ext",
]
diff --git a/printing/emf_win.h b/printing/emf_win.h
index 7be747a..4170e87 100644
--- a/printing/emf_win.h
+++ b/printing/emf_win.h
@@ -2,8 +2,8 @@
// 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__
+#ifndef PRINTING_EMF_WIN_H_
+#define PRINTING_EMF_WIN_H_
#include <windows.h>
#include <vector>
@@ -178,4 +178,4 @@ class Emf::Enumerator {
} // namespace printing
-#endif // PRINTING_EMF_WIN_H__
+#endif // PRINTING_EMF_WIN_H_
diff --git a/printing/emf_win_unittest.cc b/printing/emf_win_unittest.cc
index 9ff375a..7f1a1e6 100644
--- a/printing/emf_win_unittest.cc
+++ b/printing/emf_win_unittest.cc
@@ -64,7 +64,7 @@ TEST(EmfTest, DC) {
/*
// DEPS check fails even if include is in a multi line comment:
-// #include "chrome/browser/printing/win_printing_context.h"
+// #include "printing/printing_context.h"
// #include "chrome/common/chrome_paths.h"
// Disabled if no "UnitTest printer" exist. Useful to reproduce bug 1186598.
diff --git a/printing/native_metafile.h b/printing/native_metafile.h
index 6963089..284212c 100644
--- a/printing/native_metafile.h
+++ b/printing/native_metafile.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef PRINTING_NATIVE_METAFILE_H__
-#define PRINTING_NATIVE_METAFILE_H__
+#ifndef PRINTING_NATIVE_METAFILE_H_
+#define PRINTING_NATIVE_METAFILE_H_
// Define a metafile format for the current platform. We use this platform
// independent define so we can define interfaces in platform agnostic manner.
@@ -25,12 +25,16 @@ typedef Emf NativeMetafile;
#elif defined(OS_MACOSX)
// TODO(port): Printing using PDF?
+// The mock class is here so we can compile.
+class NativeMetafile {};
#elif defined(OS_LINUX)
// TODO(port): Printing using PostScript?
+// The mock class is here so we can compile.
+class NativeMetafile {};
#endif
-#endif // PRINTING_NATIVE_METAFILE_H__
+#endif // PRINTING_NATIVE_METAFILE_H_
diff --git a/printing/page_number.cc b/printing/page_number.cc
new file mode 100644
index 0000000..8e92870
--- /dev/null
+++ b/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/printing/page_number.h b/printing/page_number.h
new file mode 100644
index 0000000..528a41d
--- /dev/null
+++ b/printing/page_number.h
@@ -0,0 +1,73 @@
+// 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 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 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/printing/page_number_unittest.cc b/printing/page_number_unittest.cc
new file mode 100644
index 0000000..ece1e0f
--- /dev/null
+++ b/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/printing/page_overlays.cc b/printing/page_overlays.cc
new file mode 100644
index 0000000..946186d
--- /dev/null
+++ b/printing/page_overlays.cc
@@ -0,0 +1,206 @@
+// Copyright (c) 2006-2009 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_overlays.h"
+
+#include "app/gfx/text_elider.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "printing/printed_document.h"
+#include "printing/printed_page.h"
+
+namespace {
+
+// Replaces a subpart of a string by other value, and returns the position right
+// after the new value.
+size_t ReplaceKey(std::wstring* string,
+ size_t offset,
+ size_t old_string_len,
+ const std::wstring& new_string) {
+ string->replace(offset, old_string_len, new_string);
+ return offset + new_string.size();
+}
+
+} // namespace
+
+namespace printing {
+
+const wchar_t* const PageOverlays::kTitle = L"{title}";
+const wchar_t* const PageOverlays::kTime = L"{time}";
+const wchar_t* const PageOverlays::kDate = L"{date}";
+const wchar_t* const PageOverlays::kPage = L"{page}";
+const wchar_t* const PageOverlays::kPageCount = L"{pagecount}";
+const wchar_t* const PageOverlays::kPageOnTotal = L"{pageontotal}";
+const wchar_t* const PageOverlays::kUrl = L"{url}";
+
+PageOverlays::PageOverlays()
+ : top_left(kDate),
+ top_center(kTitle),
+ top_right(),
+ bottom_left(kUrl),
+ bottom_center(),
+ bottom_right(kPageOnTotal) {
+}
+
+bool PageOverlays::Equals(const PageOverlays& rhs) const {
+ return top_left == rhs.top_left &&
+ top_center == rhs.top_center &&
+ top_right == rhs.top_right &&
+ bottom_left == rhs.bottom_left &&
+ bottom_center == rhs.bottom_center &&
+ bottom_right == rhs.bottom_right;
+}
+
+const std::wstring& PageOverlays::GetOverlay(HorizontalPosition x,
+ VerticalPosition y) const {
+ switch (x) {
+ case LEFT:
+ switch (y) {
+ case TOP:
+ return top_left;
+ case BOTTOM:
+ return bottom_left;
+ }
+ break;
+ case CENTER:
+ switch (y) {
+ case TOP:
+ return top_center;
+ case BOTTOM:
+ return bottom_center;
+ }
+ break;
+ case RIGHT:
+ switch (y) {
+ case TOP:
+ return top_right;
+ case BOTTOM:
+ return bottom_right;
+ }
+ break;
+ }
+ NOTREACHED();
+ return EmptyWString();
+}
+
+void PageOverlays::SetOverlay(HorizontalPosition x, VerticalPosition y,
+ std::wstring& input) {
+ switch (x) {
+ case LEFT:
+ switch (y) {
+ case TOP:
+ top_left = input;
+ break;
+ case BOTTOM:
+ bottom_left = input;
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ break;
+ case CENTER:
+ switch (y) {
+ case TOP:
+ top_center = input;
+ break;
+ case BOTTOM:
+ bottom_center = input;
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ break;
+ case RIGHT:
+ switch (y) {
+ case TOP:
+ top_right = input;
+ break;
+ case BOTTOM:
+ bottom_right = input;
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+}
+
+//static
+std::wstring PageOverlays::ReplaceVariables(const std::wstring& input,
+ const PrintedDocument& document,
+ const PrintedPage& page) {
+ std::wstring output(input);
+ for (size_t offset = output.find(L'{', 0);
+ offset != std::wstring::npos;
+ offset = output.find(L'{', offset)) {
+
+ if (0 == output.compare(offset,
+ wcslen(kTitle),
+ kTitle)) {
+ offset = ReplaceKey(&output,
+ offset,
+ wcslen(kTitle),
+ document.name());
+ } else if (0 == output.compare(offset,
+ wcslen(kTime),
+ kTime)) {
+ offset = ReplaceKey(&output,
+ offset,
+ wcslen(kTime),
+ document.time());
+ } else if (0 == output.compare(offset,
+ wcslen(kDate),
+ kDate)) {
+ offset = ReplaceKey(&output,
+ offset,
+ wcslen(kDate),
+ document.date());
+ } else if (0 == output.compare(offset,
+ wcslen(kPage),
+ kPage)) {
+ offset = ReplaceKey(&output,
+ offset,
+ wcslen(kPage),
+ IntToWString(page.page_number()));
+ } else if (0 == output.compare(offset,
+ wcslen(kPageCount),
+ kPageCount)) {
+ offset = ReplaceKey(&output,
+ offset,
+ wcslen(kPageCount),
+ IntToWString(document.page_count()));
+ } else if (0 == output.compare(offset,
+ wcslen(kPageOnTotal),
+ kPageOnTotal)) {
+ std::wstring replacement;
+ replacement = IntToWString(page.page_number());
+ replacement += L"/";
+ replacement += IntToWString(document.page_count());
+ offset = ReplaceKey(&output,
+ offset,
+ wcslen(kPageOnTotal),
+ replacement);
+ } else if (0 == output.compare(offset,
+ wcslen(kUrl),
+ kUrl)) {
+ // TODO(maruel): http://b/1126373 gfx::ElideUrl(document.url(), ...)
+ offset = ReplaceKey(&output,
+ offset,
+ wcslen(kUrl),
+ UTF8ToWide(document.url().spec()));
+ } else {
+ // There is just a { in the string.
+ ++offset;
+ }
+ }
+ return output;
+}
+
+} // namespace printing
diff --git a/printing/page_overlays.h b/printing/page_overlays.h
new file mode 100644
index 0000000..f30aa26
--- /dev/null
+++ b/printing/page_overlays.h
@@ -0,0 +1,80 @@
+// Copyright (c) 2006-2009 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_OVERLAYS_H_
+#define PRINTING_PAGE_OVERLAYS_H_
+
+#include <string>
+
+namespace printing {
+
+class PrintedDocument;
+class PrintedPage;
+
+// Page's overlays, i.e. headers and footers. Contains the strings that will be
+// printed in the overlays, with actual values as variables. The variables are
+// replaced by their actual values with ReplaceVariables().
+class PageOverlays {
+ public:
+ // Position of the header/footer.
+ enum HorizontalPosition {
+ LEFT,
+ CENTER,
+ RIGHT,
+ };
+
+ // Position of the header/footer.
+ enum VerticalPosition {
+ TOP,
+ BOTTOM,
+ };
+
+ PageOverlays();
+
+ // Equality operator.
+ bool Equals(const PageOverlays& rhs) const;
+
+ // Returns the string of an overlay according to its x,y position.
+ const std::wstring& GetOverlay(HorizontalPosition x,
+ VerticalPosition y) const;
+
+ // Sets the string of an overlay according to its x,y position.
+ void SetOverlay(HorizontalPosition x, VerticalPosition y,
+ std::wstring& input);
+
+ // Replaces the variables in |input| with their actual values according to the
+ // properties of the current printed document and the current printed page.
+ static std::wstring ReplaceVariables(const std::wstring& input,
+ const PrintedDocument& document,
+ const PrintedPage& page);
+
+ // Keys that are dynamically replaced in the header and footer by their actual
+ // values.
+ // Web page's title.
+ static const wchar_t* const kTitle;
+ // Print job's start time.
+ static const wchar_t* const kTime;
+ // Print job's start date.
+ static const wchar_t* const kDate;
+ // Printed page's number.
+ static const wchar_t* const kPage;
+ // Print job's total page count.
+ static const wchar_t* const kPageCount;
+ // Printed page's number on total page count.
+ static const wchar_t* const kPageOnTotal;
+ // Web page's displayed url.
+ static const wchar_t* const kUrl;
+
+ // Actual headers and footers.
+ std::wstring top_left;
+ std::wstring top_center;
+ std::wstring top_right;
+ std::wstring bottom_left;
+ std::wstring bottom_center;
+ std::wstring bottom_right;
+};
+
+} // namespace printing
+
+#endif // PRINTING_PAGE_OVERLAYS_H_
diff --git a/printing/page_overlays_unittest.cc b/printing/page_overlays_unittest.cc
new file mode 100644
index 0000000..2d1bdac
--- /dev/null
+++ b/printing/page_overlays_unittest.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2006-2009 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/at_exit.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "printing/page_overlays.h"
+#include "printing/print_settings.h"
+#include "printing/printed_document.h"
+#include "printing/printed_page.h"
+#include "printing/printed_pages_source.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+base::AtExitManager global_at_exit_manager;
+
+class PageOverlaysTest : public testing::Test {
+ private:
+ MessageLoop message_loop_;
+};
+
+struct Keys {
+ const wchar_t* key;
+ const wchar_t* expected;
+};
+
+const Keys kOverlayKeys[] = {
+ printing::PageOverlays::kTitle, L"Foobar Document",
+ printing::PageOverlays::kTime, L"",
+ printing::PageOverlays::kDate, L"",
+ printing::PageOverlays::kPage, L"1",
+ printing::PageOverlays::kPageCount, L"2",
+ printing::PageOverlays::kPageOnTotal, L"1/2",
+ printing::PageOverlays::kUrl, L"http://www.perdu.com/",
+};
+
+class PagesSource : public printing::PrintedPagesSource {
+ public:
+ virtual std::wstring RenderSourceName() {
+ return L"Foobar Document";
+ }
+
+ virtual GURL RenderSourceUrl() {
+ return GURL(L"http://www.perdu.com");
+ }
+};
+
+} // namespace
+
+
+TEST_F(PageOverlaysTest, StringConversion) {
+ printing::PageOverlays overlays;
+ overlays.GetOverlay(printing::PageOverlays::LEFT,
+ printing::PageOverlays::BOTTOM);
+ printing::PrintSettings settings;
+ PagesSource source;
+ int cookie = 1;
+ scoped_refptr<printing::PrintedDocument> doc(
+ new printing::PrintedDocument(settings, &source, cookie));
+ doc->set_page_count(2);
+ gfx::Size page_size(100, 100);
+ scoped_refptr<printing::PrintedPage> page(
+ new printing::PrintedPage(1, NULL, page_size));
+
+ std::wstring input;
+ std::wstring out;
+ for (int i = 0; i < arraysize(kOverlayKeys); ++i) {
+ input = StringPrintf(L"foo%lsbar", kOverlayKeys[i].key);
+ out = printing::PageOverlays::ReplaceVariables(input, *doc.get(),
+ *page.get());
+ EXPECT_FALSE(out.empty());
+ if (wcslen(kOverlayKeys[i].expected) == 0)
+ continue;
+ EXPECT_EQ(StringPrintf(L"foo%lsbar", kOverlayKeys[i].expected), out) <<
+ kOverlayKeys[i].key;
+ }
+
+ // Check if SetOverlay really sets the page overlay.
+ overlays.SetOverlay(printing::PageOverlays::LEFT,
+ printing::PageOverlays::TOP,
+ UTF16ToWide(L"Page {page}"));
+ input = overlays.GetOverlay(printing::PageOverlays::LEFT,
+ printing::PageOverlays::TOP);
+ EXPECT_EQ(input, L"Page {page}");
+
+ // Replace the variables to see if the page number is correct.
+ out = printing::PageOverlays::ReplaceVariables(input, *doc.get(),
+ *page.get());
+ EXPECT_EQ(out, L"Page 1");
+}
diff --git a/printing/page_range.cc b/printing/page_range.cc
new file mode 100644
index 0000000..ffe572d
--- /dev/null
+++ b/printing/page_range.cc
@@ -0,0 +1,31 @@
+// 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_range.h"
+
+#include "base/stl_util-inl.h"
+
+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 SetToVector(pages);
+}
+
+/* static */
+int PageRange::GetTotalPages(const PageRanges& ranges) {
+ // Since ranges can overlap we need to merge them before counting
+ std::vector<int> pages = PageRange::GetPages(ranges);
+ return pages.size();
+}
+
+} // namespace printing
diff --git a/printing/page_range.h b/printing/page_range.h
new file mode 100644
index 0000000..1237d5d
--- /dev/null
+++ b/printing/page_range.h
@@ -0,0 +1,34 @@
+// 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 PRINTING_PAGE_RANGE_H_
+#define PRINTING_PAGE_RANGE_H_
+
+#include <vector>
+
+namespace printing {
+
+struct PageRange;
+
+typedef std::vector<PageRange> PageRanges;
+
+// Print range is inclusive. To select one page, set from == to.
+struct 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);
+
+ // Gets the total number of pages.
+ static int GetTotalPages(const PageRanges& ranges);
+};
+
+} // namespace printing
+
+#endif // PRINTING_PAGE_RANGE_H_
diff --git a/printing/page_range_unittest.cc b/printing/page_range_unittest.cc
new file mode 100644
index 0000000..2cb4f82
--- /dev/null
+++ b/printing/page_range_unittest.cc
@@ -0,0 +1,38 @@
+// 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_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]);
+ EXPECT_EQ(8, printing::PageRange::GetTotalPages(ranges));
+}
+
+TEST(PageRangeTest, Empty) {
+ printing::PageRanges ranges;
+ std::vector<int> pages(printing::PageRange::GetPages(ranges));
+ EXPECT_EQ(0U, pages.size());
+ EXPECT_EQ(0, printing::PageRange::GetTotalPages(ranges));
+}
diff --git a/printing/page_setup.cc b/printing/page_setup.cc
new file mode 100644
index 0000000..adc285b
--- /dev/null
+++ b/printing/page_setup.cc
@@ -0,0 +1,126 @@
+// 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_setup.h"
+
+#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() : text_height_(0) {
+}
+
+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;
+}
+
+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;
+
+ // Calculate the effective margins. The tricky part.
+ effective_margins_.header = std::max(requested_margins_.header,
+ printable_area_.y());
+ effective_margins_.footer = std::max(requested_margins_.footer,
+ physical_size.height() -
+ printable_area_.bottom());
+ effective_margins_.left = std::max(requested_margins_.left,
+ printable_area_.x());
+ effective_margins_.top = std::max(std::max(requested_margins_.top,
+ printable_area_.y()),
+ effective_margins_.header + text_height);
+ effective_margins_.right = std::max(requested_margins_.right,
+ physical_size.width() -
+ printable_area_.right());
+ effective_margins_.bottom = std::max(std::max(requested_margins_.bottom,
+ physical_size.height() -
+ printable_area_.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()));
+}
+
+void PageSetup::SetRequestedMargins(const PageMargins& requested_margins) {
+ requested_margins_ = requested_margins;
+ if (physical_size_.width() && physical_size_.height())
+ Init(physical_size_, printable_area_, text_height_);
+}
+
+} // namespace printing
diff --git a/printing/page_setup.h b/printing/page_setup.h
new file mode 100644
index 0000000..101b128
--- /dev/null
+++ b/printing/page_setup.h
@@ -0,0 +1,82 @@
+// 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 PRINTING_PAGE_SETUP_H_
+#define PRINTING_PAGE_SETUP_H_
+
+#include "base/gfx/rect.h"
+
+namespace printing {
+
+// Margins for a page setup.
+class 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 PageSetup {
+ public:
+ 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);
+
+ void SetRequestedMargins(const PageMargins& requested_margins);
+
+ 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 PageMargins& effective_margins() const {
+ return effective_margins_;
+ }
+
+ private:
+ // 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_;
+
+ // Space that must be kept free for the overlays.
+ int text_height_;
+};
+
+} // namespace printing
+
+#endif // PRINTING_PAGE_SETUP_H_
diff --git a/printing/page_setup_unittest.cc b/printing/page_setup_unittest.cc
new file mode 100644
index 0000000..e2c68b9
--- /dev/null
+++ b/printing/page_setup_unittest.cc
@@ -0,0 +1,146 @@
+// 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_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 <<
+ " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(overlay_area, setup.overlay_area()) << seed << " " << page_size <<
+ " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(content_area, setup.content_area()) << seed << " " << page_size <<
+ " " << printable_area << " " << kTextHeight;
+
+ EXPECT_EQ(effective_margins.header, setup.effective_margins().header) <<
+ seed << " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.footer, setup.effective_margins().footer) <<
+ seed << " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.left, setup.effective_margins().left) << seed <<
+ " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.top, setup.effective_margins().top) << seed <<
+ " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.right, setup.effective_margins().right) << seed <<
+ " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.bottom, setup.effective_margins().bottom) <<
+ seed << " " << page_size << " " << printable_area << " " << 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 <<
+ " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(overlay_area, setup.overlay_area()) << " " << page_size <<
+ " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(content_area, setup.content_area()) << " " << page_size <<
+ " " << printable_area << " " << kTextHeight;
+
+ EXPECT_EQ(effective_margins.header, setup.effective_margins().header) <<
+ " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.footer, setup.effective_margins().footer) <<
+ " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.left, setup.effective_margins().left) <<
+ " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.top, setup.effective_margins().top) <<
+ " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.right, setup.effective_margins().right) <<
+ " " << page_size << " " << printable_area << " " << kTextHeight;
+ EXPECT_EQ(effective_margins.bottom, setup.effective_margins().bottom) <<
+ " " << page_size << " " << printable_area << " " << kTextHeight;
+}
diff --git a/printing/print_settings.cc b/printing/print_settings.cc
new file mode 100644
index 0000000..b76821b
--- /dev/null
+++ b/printing/print_settings.cc
@@ -0,0 +1,116 @@
+// 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/print_settings.h"
+
+#include "base/atomic_sequence_num.h"
+#include "base/logging.h"
+#include "printing/units.h"
+
+namespace printing {
+
+// Global SequenceNumber used for generating unique cookie values.
+static base::AtomicSequenceNumber cookie_seq(base::LINKER_INITIALIZED);
+
+PrintSettings::PrintSettings()
+ : min_shrink(1.25),
+ max_shrink(2.0),
+ desired_dpi(72),
+ selection_only(false),
+ dpi_(0),
+ landscape_(false) {
+}
+
+void PrintSettings::Clear() {
+ ranges.clear();
+ min_shrink = 1.25;
+ max_shrink = 2.;
+ desired_dpi = 72;
+ selection_only = false;
+ printer_name_.clear();
+ device_name_.clear();
+ page_setup_pixels_.Clear();
+ dpi_ = 0;
+ landscape_ = false;
+}
+
+#ifdef WIN32
+void PrintSettings::Init(HDC hdc,
+ const DEVMODE& dev_mode,
+ const PageRanges& new_ranges,
+ const std::wstring& new_device_name,
+ bool print_selection_only) {
+ DCHECK(hdc);
+ printer_name_ = dev_mode.dmDeviceName;
+ device_name_ = new_device_name;
+ ranges = new_ranges;
+ landscape_ = dev_mode.dmOrientation == DMORIENT_LANDSCAPE;
+ selection_only = print_selection_only;
+
+ dpi_ = GetDeviceCaps(hdc, LOGPIXELSX);
+ // 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_pixels_.
+ gfx::Size physical_size_pixels(GetDeviceCaps(hdc, PHYSICALWIDTH),
+ GetDeviceCaps(hdc, PHYSICALHEIGHT));
+ gfx::Rect printable_area_pixels(GetDeviceCaps(hdc, PHYSICALOFFSETX),
+ GetDeviceCaps(hdc, PHYSICALOFFSETY),
+ GetDeviceCaps(hdc, HORZRES),
+ GetDeviceCaps(hdc, VERTRES));
+
+ SetPrinterPrintableArea(physical_size_pixels, printable_area_pixels);
+}
+#endif
+
+void PrintSettings::SetPrinterPrintableArea(
+ gfx::Size const& physical_size_pixels,
+ gfx::Rect const& printable_area_pixels) {
+
+ int margin_printer_units = ConvertUnit(500, kHundrethsMMPerInch, dpi_);
+
+ // Start by setting the user configuration
+ // Hard-code text_height = 0.5cm = ~1/5 of inch
+ page_setup_pixels_.Init(physical_size_pixels,
+ printable_area_pixels,
+ margin_printer_units);
+
+ // Now apply user configured settings.
+ PageMargins margins;
+ margins.header = margin_printer_units;
+ margins.footer = margin_printer_units;
+ margins.left = margin_printer_units;
+ margins.top = margin_printer_units;
+ margins.right = margin_printer_units;
+ margins.bottom = margin_printer_units;
+ page_setup_pixels_.SetRequestedMargins(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 &&
+ overlays.Equals(rhs.overlays) &&
+ device_name_ == rhs.device_name_ &&
+ page_setup_pixels_.Equals(rhs.page_setup_pixels_) &&
+ dpi_ == rhs.dpi_ &&
+ landscape_ == rhs.landscape_;
+}
+
+int PrintSettings::NewCookie() {
+ // A cookie of 0 is used to mark a document as unassigned, count from 1.
+ return cookie_seq.GetNext() + 1;
+}
+
+} // namespace printing
diff --git a/printing/print_settings.h b/printing/print_settings.h
new file mode 100644
index 0000000..d1929c3
--- /dev/null
+++ b/printing/print_settings.h
@@ -0,0 +1,107 @@
+// 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 PRINTING_PRINT_SETTINGS_H_
+#define PRINTING_PRINT_SETTINGS_H_
+
+#include "base/gfx/rect.h"
+#include "printing/page_overlays.h"
+#include "printing/page_range.h"
+#include "printing/page_setup.h"
+
+typedef struct HDC__* HDC;
+typedef struct _devicemodeW DEVMODE;
+
+namespace printing {
+
+// OS-independent print settings.
+class PrintSettings {
+ public:
+ PrintSettings();
+
+ // Reinitialize the settings to the default values.
+ void Clear();
+
+#ifdef WIN32
+ // Reads the settings from the selected device context. Calculates derived
+ // values like printable_area_.
+ void Init(HDC hdc,
+ const DEVMODE& dev_mode,
+ const PageRanges& new_ranges,
+ const std::wstring& new_device_name,
+ bool selection_only);
+#endif
+
+ // Set printer printable area in in pixels.
+ void SetPrinterPrintableArea(gfx::Size const& physical_size_pixels,
+ gfx::Rect const& printable_area_pixels);
+
+ // Equality operator.
+ // NOTE: printer_name is NOT tested for equality since it doesn't affect the
+ // output.
+ bool Equals(const PrintSettings& rhs) const;
+
+ const std::wstring& printer_name() const { return printer_name_; }
+ void set_device_name(const std::wstring& device_name) {
+ device_name_ = device_name;
+ }
+ const std::wstring& device_name() const { return device_name_; }
+ int dpi() const { return dpi_; }
+ const PageSetup& page_setup_pixels() const { return page_setup_pixels_; }
+
+ // 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;
+
+ // The various overlays (headers and footers).
+ PageOverlays overlays;
+
+ // Indicates if the user only wants to print the current selection.
+ bool selection_only;
+
+ // 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();
+
+ private:
+ //////////////////////////////////////////////////////////////////////////////
+ // Settings that can't be changed without side-effects.
+
+ // Printer name as shown to the user.
+ std::wstring printer_name_;
+
+ // Printer device name as opened by the OS.
+ std::wstring device_name_;
+
+ // Page setup in pixel units, dpi adjusted.
+ PageSetup page_setup_pixels_;
+
+ // Printer's device effective dots per inch in both axis.
+ int dpi_;
+
+ // Is the orientation landscape or portrait.
+ bool landscape_;
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINT_SETTINGS_H_
diff --git a/printing/printed_document.cc b/printing/printed_document.cc
new file mode 100644
index 0000000..517548b
--- /dev/null
+++ b/printing/printed_document.cc
@@ -0,0 +1,366 @@
+// 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/printed_document.h"
+
+#include <set>
+
+#include "app/gfx/font.h"
+#include "app/gfx/text_elider.h"
+#include "app/win_util.h"
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/singleton.h"
+#include "base/string_util.h"
+#include "base/time.h"
+#include "printing/page_number.h"
+#include "printing/page_overlays.h"
+#include "printing/printed_pages_source.h"
+#include "printing/printed_page.h"
+#include "printing/units.h"
+#include "skia/ext/platform_device.h"
+
+using base::Time;
+
+namespace {
+
+struct PrintDebugDumpPath {
+ PrintDebugDumpPath()
+ : enabled(false) {
+ }
+
+ bool enabled;
+ std::wstring debug_dump_path;
+};
+
+Singleton<PrintDebugDumpPath> g_debug_dump_info;
+
+} // 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,
+ NativeMetafile* metafile,
+ double shrink) {
+ // 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, immutable_.settings_.page_setup_pixels().physical_size()));
+ {
+ AutoLock lock(lock_);
+ mutable_.pages_[page_number] = page;
+ if (mutable_.shrink_factor == 0) {
+ mutable_.shrink_factor = shrink;
+ } else {
+ DCHECK_EQ(mutable_.shrink_factor, shrink);
+ }
+ }
+ DebugDump(*page);
+}
+
+bool PrintedDocument::GetPage(int page_number,
+ scoped_refptr<PrintedPage>* page) {
+ 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;
+}
+
+void PrintedDocument::RenderPrintedPage(const PrintedPage& page,
+ HDC context) const {
+#ifndef NDEBUG
+ {
+ // Make sure the page is from our list.
+ AutoLock lock(lock_);
+ DCHECK(&page == mutable_.pages_.find(page.page_number() - 1)->second.get());
+ }
+#endif
+
+ // 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::PlatformDevice::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.
+ XFORM xform = { 0 };
+ xform.eDx = static_cast<float>(
+ immutable_.settings_.page_setup_pixels().content_area().x());
+ xform.eDy = static_cast<float>(
+ immutable_.settings_.page_setup_pixels().content_area().y());
+ xform.eM11 = static_cast<float>(1. / mutable_.shrink_factor);
+ xform.eM22 = static_cast<float>(1. / mutable_.shrink_factor);
+ BOOL res = ModifyWorldTransform(context, &xform, MWT_LEFTMULTIPLY);
+ DCHECK_NE(res, 0);
+
+ if (!page.native_metafile()->SafePlayback(context)) {
+ NOTREACHED();
+ }
+
+ res = RestoreDC(context, saved_state);
+ DCHECK_NE(res, 0);
+ }
+
+ // Print the header and footer.
+ int base_font_size = gfx::Font().height();
+ int new_font_size = ConvertUnit(10,
+ immutable_.settings_.desired_dpi,
+ immutable_.settings_.dpi());
+ DCHECK_GT(new_font_size, base_font_size);
+ gfx::Font font(gfx::Font().DeriveFont(new_font_size - base_font_size));
+ HGDIOBJ old_font = SelectObject(context, font.hfont());
+ DCHECK(old_font != NULL);
+ // We don't want a white square around the text ever if overflowing.
+ SetBkMode(context, TRANSPARENT);
+ PrintHeaderFooter(context, page, PageOverlays::LEFT, PageOverlays::TOP,
+ font);
+ PrintHeaderFooter(context, page, PageOverlays::CENTER, PageOverlays::TOP,
+ font);
+ PrintHeaderFooter(context, page, PageOverlays::RIGHT, PageOverlays::TOP,
+ font);
+ PrintHeaderFooter(context, page, PageOverlays::LEFT, PageOverlays::BOTTOM,
+ font);
+ PrintHeaderFooter(context, page, PageOverlays::CENTER, PageOverlays::BOTTOM,
+ font);
+ PrintHeaderFooter(context, page, PageOverlays::RIGHT, PageOverlays::BOTTOM,
+ font);
+ int res = RestoreDC(context, saved_state);
+ DCHECK_NE(res, 0);
+}
+
+bool PrintedDocument::RenderPrintedPageNumber(int page_number, HDC context) {
+ scoped_refptr<PrintedPage> page;
+ if (!GetPage(page_number, &page))
+ return false;
+ RenderPrintedPage(*page.get(), context);
+ return true;
+}
+
+bool PrintedDocument::IsComplete() const {
+ 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) {
+ PrintedPages::const_iterator itr = mutable_.pages_.find(page.ToInt());
+ if (itr == mutable_.pages_.end() || !itr->second.get() ||
+ !itr->second->native_metafile())
+ return false;
+ }
+ return true;
+}
+
+void PrintedDocument::DisconnectSource() {
+ AutoLock lock(lock_);
+ mutable_.source_ = NULL;
+}
+
+size_t PrintedDocument::MemoryUsage() const {
+ std::vector<scoped_refptr<PrintedPage>> pages_copy;
+ {
+ 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);
+ }
+ }
+ }
+ size_t total = 0;
+ for (size_t i = 0; i < pages_copy.size(); ++i) {
+ total += pages_copy[i]->native_metafile()->GetDataSize();
+ }
+ return total;
+}
+
+void PrintedDocument::set_page_count(int max_page) {
+ 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 {
+ AutoLock lock(lock_);
+ return mutable_.page_count_;
+}
+
+int PrintedDocument::expected_page_count() const {
+ AutoLock lock(lock_);
+ return mutable_.expected_page_count_;
+}
+
+void PrintedDocument::PrintHeaderFooter(HDC context,
+ const PrintedPage& page,
+ PageOverlays::HorizontalPosition x,
+ PageOverlays::VerticalPosition y,
+ const gfx::Font& font) const {
+ const PrintSettings& settings = immutable_.settings_;
+ const std::wstring& line = settings.overlays.GetOverlay(x, y);
+ if (line.empty()) {
+ return;
+ }
+ std::wstring output(PageOverlays::ReplaceVariables(line, *this, page));
+ if (output.empty()) {
+ // May happens if document name or url is empty.
+ return;
+ }
+ const gfx::Size string_size(font.GetStringWidth(output), font.height());
+ gfx::Rect bounding;
+ bounding.set_height(string_size.height());
+ const gfx::Rect& overlay_area(settings.page_setup_pixels().overlay_area());
+ // Hard code .25 cm interstice between overlays. Make sure that some space is
+ // kept between each headers.
+ const int interstice = ConvertUnit(250, kHundrethsMMPerInch, settings.dpi());
+ const int max_width = overlay_area.width() / 3 - interstice;
+ const int actual_width = std::min(string_size.width(), max_width);
+ switch (x) {
+ case PageOverlays::LEFT:
+ bounding.set_x(overlay_area.x());
+ bounding.set_width(max_width);
+ break;
+ case PageOverlays::CENTER:
+ bounding.set_x(overlay_area.x() +
+ (overlay_area.width() - actual_width) / 2);
+ bounding.set_width(actual_width);
+ break;
+ case PageOverlays::RIGHT:
+ bounding.set_x(overlay_area.right() - actual_width);
+ bounding.set_width(actual_width);
+ break;
+ }
+
+ DCHECK_LE(bounding.right(), overlay_area.right());
+
+ switch (y) {
+ case PageOverlays::BOTTOM:
+ bounding.set_y(overlay_area.bottom() - string_size.height());
+ break;
+ case PageOverlays::TOP:
+ bounding.set_y(overlay_area.y());
+ break;
+ }
+
+ if (string_size.width() > bounding.width()) {
+ if (line == PageOverlays::kUrl) {
+ output = gfx::ElideUrl(url(), font, bounding.width(), std::wstring());
+ } else {
+ output = gfx::ElideText(output, font, bounding.width());
+ }
+ }
+
+ // Save the state (again) for the clipping region.
+ int saved_state = SaveDC(context);
+ DCHECK_NE(saved_state, 0);
+
+ int result = IntersectClipRect(context, bounding.x(), bounding.y(),
+ bounding.right() + 1, bounding.bottom() + 1);
+ DCHECK(result == SIMPLEREGION || result == COMPLEXREGION);
+ TextOut(context,
+ bounding.x(), bounding.y(),
+ output.c_str(),
+ static_cast<int>(output.size()));
+ int res = RestoreDC(context, saved_state);
+ DCHECK_NE(res, 0);
+}
+
+void PrintedDocument::DebugDump(const PrintedPage& page)
+{
+ if (!g_debug_dump_info->enabled)
+ return;
+
+ std::wstring filename;
+ filename += date();
+ filename += L"_";
+ filename += time();
+ filename += L"_";
+ filename += name();
+ filename += L"_";
+ filename += StringPrintf(L"%02d", page.page_number());
+ filename += L"_.emf";
+ file_util::ReplaceIllegalCharacters(&filename, '_');
+ std::wstring path(g_debug_dump_info->debug_dump_path);
+ file_util::AppendToPath(&path, filename);
+ page.native_metafile()->SaveTo(path);
+}
+
+void PrintedDocument::set_debug_dump_path(const std::wstring& debug_dump_path) {
+ g_debug_dump_info->enabled = !debug_dump_path.empty();
+ g_debug_dump_info->debug_dump_path = debug_dump_path;
+}
+
+const std::wstring& PrintedDocument::debug_dump_path() {
+ return g_debug_dump_info->debug_dump_path;
+}
+
+PrintedDocument::Mutable::Mutable(PrintedPagesSource* source)
+ : source_(source),
+ expected_page_count_(0),
+ page_count_(0),
+ shrink_factor(0) {
+}
+
+PrintedDocument::Immutable::Immutable(const PrintSettings& settings,
+ PrintedPagesSource* source,
+ int cookie)
+ : settings_(settings),
+ source_message_loop_(MessageLoop::current()),
+ name_(source->RenderSourceName()),
+ url_(source->RenderSourceUrl()),
+ cookie_(cookie) {
+ // Setup the document's date.
+#ifdef WIN32
+ // On Windows, use the native time formatting for printing.
+ SYSTEMTIME systemtime;
+ GetLocalTime(&systemtime);
+ date_ = win_util::FormatSystemDate(systemtime, std::wstring());
+ time_ = win_util::FormatSystemTime(systemtime, std::wstring());
+#else
+ Time now = Time::Now();
+ date_ = TimeFormat::ShortDateNumeric(now);
+ time_ = TimeFormat::TimeOfDay(now);
+#endif // WIN32
+}
+
+} // namespace printing
diff --git a/printing/printed_document.h b/printing/printed_document.h
new file mode 100644
index 0000000..cedaddd
--- /dev/null
+++ b/printing/printed_document.h
@@ -0,0 +1,192 @@
+// 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 PRINTING_PRINTED_DOCUMENT_H_
+#define PRINTING_PRINTED_DOCUMENT_H_
+
+#include <map>
+
+#include "base/lock.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "googleurl/src/gurl.h"
+#include "printing/print_settings.h"
+#include "printing/native_metafile.h"
+
+class MessageLoop;
+
+namespace gfx {
+class Font;
+}
+
+namespace printing {
+
+class PrintedPage;
+class PrintedPagesSource;
+
+// 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 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);
+ ~PrintedDocument();
+
+ // Sets a page's data. 0-based. Takes metafile ownership.
+ // Note: locks for a short amount of time.
+ void SetPage(int page_number, NativeMetafile* metafile, double shrink);
+
+ // 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.
+ void RenderPrintedPage(const PrintedPage& page, HDC context) const;
+
+ // Draws the page in the context. 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 RenderPrintedPageNumber(int page_number, HDC context);
+
+ // 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.
+ size_t 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 std::wstring& name() const {
+ return immutable_.name_;
+ }
+ const GURL& url() const { return immutable_.url_; }
+ const std::wstring& date() const { return immutable_.date_; }
+ const std::wstring& time() const { return immutable_.time_; }
+ const 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 std::wstring& debug_dump_path);
+
+ static const std::wstring& debug_dump_path();
+
+ private:
+ // 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 {
+ Mutable(PrintedPagesSource* source);
+
+ // 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_;
+
+ // Shrink done in comparison to desired_dpi.
+ double shrink_factor;
+ };
+
+ // 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);
+
+ // Print settings used to generate this document. Immutable.
+ PrintSettings settings_;
+
+ // Native thread for the render source.
+ MessageLoop* source_message_loop_;
+
+ // Document name. Immutable.
+ std::wstring name_;
+
+ // URL that generated this document. Immutable.
+ GURL url_;
+
+ // The date on which this job started. Immutable.
+ std::wstring date_;
+
+ // The time at which this job started. Immutable.
+ std::wstring time_;
+
+ // 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_;
+ };
+
+ // Prints the headers and footers for one page in the specified context
+ // according to the current settings.
+ void PrintHeaderFooter(HDC context,
+ const PrintedPage& page,
+ PageOverlays::HorizontalPosition x,
+ PageOverlays::VerticalPosition y,
+ const gfx::Font& font) const;
+
+ 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 Lock lock_;
+
+ // All the mutable members.
+ Mutable mutable_;
+
+ // All the immutable members.
+ const Immutable immutable_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PrintedDocument);
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINTED_DOCUMENT_H_
diff --git a/printing/printed_page.cc b/printing/printed_page.cc
new file mode 100644
index 0000000..441690a
--- /dev/null
+++ b/printing/printed_page.cc
@@ -0,0 +1,24 @@
+// 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/printed_page.h"
+
+namespace printing {
+
+PrintedPage::PrintedPage(int page_number,
+ NativeMetafile* native_metafile,
+ const gfx::Size& page_size)
+ : page_number_(page_number),
+ native_metafile_(native_metafile),
+ page_size_(page_size) {
+}
+
+PrintedPage::~PrintedPage() {
+}
+
+const NativeMetafile* PrintedPage::native_metafile() const {
+ return native_metafile_.get();
+}
+
+} // namespace printing
diff --git a/printing/printed_page.h b/printing/printed_page.h
new file mode 100644
index 0000000..3c954ac
--- /dev/null
+++ b/printing/printed_page.h
@@ -0,0 +1,50 @@
+// 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 PRINTING_PRINTED_PAGE_H_
+#define PRINTING_PRINTED_PAGE_H_
+
+#include "base/gfx/rect.h"
+#include "base/gfx/size.h"
+#include "base/ref_counted.h"
+#include "base/scoped_ptr.h"
+#include "printing/native_metafile.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 PrintedPage : public base::RefCountedThreadSafe<PrintedPage> {
+ public:
+ PrintedPage(int page_number,
+ NativeMetafile* native_metafile,
+ const gfx::Size& page_size);
+ ~PrintedPage();
+
+ // Getters
+ int page_number() const { return page_number_; }
+ const NativeMetafile* native_metafile() const;
+ const gfx::Size& page_size() const { return page_size_; }
+
+ private:
+ // Page number inside the printed document.
+ const int page_number_;
+
+ // Actual paint data.
+ const scoped_ptr<NativeMetafile> native_metafile_;
+
+ // The physical page size. To support multiple page formats inside on print
+ // job.
+ const gfx::Size page_size_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PrintedPage);
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINTED_PAGE_H_
diff --git a/printing/printed_pages_source.h b/printing/printed_pages_source.h
new file mode 100644
index 0000000..4ae6549
--- /dev/null
+++ b/printing/printed_pages_source.h
@@ -0,0 +1,29 @@
+// 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 PRINTING_PRINTED_PAGES_SOURCE_H_
+#define PRINTING_PRINTED_PAGES_SOURCE_H_
+
+#include <string>
+
+class GURL;
+class MessageLoop;
+
+namespace printing {
+
+class PrintedDocument;
+
+// Source of printed pages.
+class PrintedPagesSource {
+ public:
+ // Returns the document title.
+ virtual std::wstring RenderSourceName() = 0;
+
+ // Returns the URL's source of the document if applicable.
+ virtual GURL RenderSourceUrl() = 0;
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINTED_PAGES_SOURCE_H_
diff --git a/printing/printing.gyp b/printing/printing.gyp
index 899bded..e0f1d38 100644
--- a/printing/printing.gyp
+++ b/printing/printing.gyp
@@ -14,9 +14,13 @@
'target_name': 'printing',
'type': '<(library)',
'dependencies': [
+ '../app/app.gyp:app_base', # Only required for Font support
'../base/base.gyp:base',
'../base/base.gyp:base_gfx',
-
+ '../build/temp_gyp/googleurl.gyp:googleurl',
+ '../skia/skia.gyp:skia',
+ '../third_party/icu38/icu38.gyp:icui18n',
+ '../third_party/icu38/icu38.gyp:icuuc',
],
'msvs_guid': '9E5416B9-B91B-4029-93F4-102C1AD5CAF4',
'include_dirs': [
@@ -26,6 +30,25 @@
'emf_win.cc',
'emf_win.h',
'native_metafile.h',
+ 'page_number.cc',
+ 'page_number.h',
+ 'page_overlays.cc',
+ 'page_overlays.h',
+ 'page_range.cc',
+ 'page_range.h',
+ 'page_setup.cc',
+ 'page_setup.h',
+ 'print_settings.cc',
+ 'print_settings.h',
+ 'printed_document.cc',
+ 'printed_document.h',
+ 'printed_page.cc',
+ 'printed_page.h',
+ 'printed_pages_source.h',
+ 'printing_context.h',
+ 'printing_context_linux.cc',
+ 'printing_context_mac.cc',
+ 'printing_context_win.cc',
'units.cc',
'units.h',
],
@@ -38,7 +61,12 @@
['OS!="linux"', {'sources/': [['exclude', '_linux\\.cc$']]}],
['OS!="mac"', {'sources/': [['exclude', '_mac\\.(cc|mm?)$']]}],
['OS!="win"', {
- 'sources/': [['exclude', '_win\\.cc$']]
+ 'sources/': [
+ ['exclude', '_win\\.cc$'],
+ ['exclude',
+ 'printed_document.cc',
+ ]
+ ]
}, { # else: OS=="win"
'sources/': [['exclude', '_posix\\.cc$']]
}],
@@ -55,17 +83,31 @@
],
'sources': [
'emf_win_unittest.cc',
+ 'printing_test.h',
+ 'page_number_unittest.cc',
+ 'page_overlays_unittest.cc',
+ 'page_range_unittest.cc',
+ 'page_setup_unittest.cc',
+ 'printing_context_win_unittest.cc',
'units_unittest.cc',
],
'conditions': [
['OS!="linux"', {'sources/': [['exclude', '_linux_unittest\\.cc$']]}],
['OS!="mac"', {'sources/': [['exclude', '_mac_unittest\\.(cc|mm?)$']]}],
['OS!="win"', {
- 'sources/': [['exclude', '_win_unittest\\.cc$']]
+ 'sources/': [
+ ['exclude', '_win_unittest\\.cc$'],
+
+ # Most of the printing functionailty is Windows only for now.
+ ['exclude', '.*'],
+ ['include', 'page_range_unittest.cc'],
+ ['include', 'page_setup_unittest.cc'],
+ ['include', 'units_unittest.cc'],
+ ]
}, { # else: OS=="win"
'sources/': [['exclude', '_posix_unittest\\.cc$']]
}],
],
},
],
-}
+} \ No newline at end of file
diff --git a/printing/printing_context.h b/printing/printing_context.h
new file mode 100644
index 0000000..06ad9af
--- /dev/null
+++ b/printing/printing_context.h
@@ -0,0 +1,152 @@
+// 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 PRINTING_PRINTING_CONTEXT_H_
+#define PRINTING_PRINTING_CONTEXT_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <ocidl.h>
+#include <commdlg.h>
+#endif
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "printing/print_settings.h"
+
+namespace printing {
+
+// Describe the user selected printing context for Windows. This includes the
+// OS-dependent UI to ask the user about the print settings. This class directly
+// talk to the printer and manages the document and pages breaks.
+class PrintingContext {
+ public:
+ // Tri-state result for user behavior-dependent functions.
+ enum Result {
+ OK,
+ CANCEL,
+ FAILED,
+ };
+
+ PrintingContext();
+ ~PrintingContext();
+
+#if defined(OS_WIN)
+ // Asks the user what printer and format should be used to print. Updates the
+ // context with the select device settings.
+ Result AskUserForSettings(HWND window, int max_pages, bool has_selection);
+#endif
+
+ // Selects the user's default printer and format. Updates the context with the
+ // default device settings.
+ Result UseDefaultSettings();
+
+ // Initializes with predefined settings.
+ Result InitWithSettings(const PrintSettings& settings);
+
+ // Reinitializes the settings to uninitialized for object reuse.
+ void ResetSettings();
+
+ // 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.
+ Result NewDocument(const std::wstring& document_name);
+
+ // Starts a new page.
+ Result NewPage();
+
+ // Closes the printed page.
+ Result PageDone();
+
+ // Closes the printing job. After this call the object is ready to start a new
+ // document.
+ Result DocumentDone();
+
+ // Cancels printing. Can be used in a multi-threaded context. Takes effect
+ // immediately.
+ void Cancel();
+
+ // Dismiss the Print... dialog box if shown.
+ void DismissDialog();
+
+#if defined(OS_WIN)
+ HDC context() {
+ return hdc_;
+ }
+#endif
+
+ const PrintSettings& settings() const {
+ return settings_;
+ }
+
+ private:
+ // Class that manages the PrintDlgEx() callbacks. This is meant to be a
+ // temporary object used during the Print... dialog display.
+ class CallbackHandler;
+
+ // Does bookkeeping when an error occurs.
+ PrintingContext::Result OnError();
+
+#if defined(OS_WIN)
+ // 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. hdc_ is allocated with
+ // this call.
+ bool GetPrinterSettings(HANDLE printer,
+ const std::wstring& device_name);
+
+ // Allocates the HDC for a specific DEVMODE.
+ bool AllocateContext(const std::wstring& printer_name,
+ const DEVMODE* dev_mode);
+
+ // Parses the result of a PRINTDLGEX result.
+ Result ParseDialogResultEx(const PRINTDLGEX& dialog_options);
+ Result ParseDialogResult(const PRINTDLG& dialog_options);
+
+ // The selected printer context.
+ HDC hdc_;
+#endif
+
+ // Complete print context settings.
+ PrintSettings settings_;
+
+#ifndef NDEBUG
+ // Current page number in the print job.
+ int page_number_;
+#endif
+
+#if defined(OS_WIN)
+ // The dialog box for the time it is shown.
+ volatile HWND dialog_box_;
+#endif
+
+ // 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_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PrintingContext);
+};
+
+} // namespace printing
+
+#endif // PRINTING_PRINTING_CONTEXT_H_
diff --git a/printing/printing_context_linux.cc b/printing/printing_context_linux.cc
new file mode 100644
index 0000000..fcc51f7
--- /dev/null
+++ b/printing/printing_context_linux.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2009 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"
+
+namespace printing {
+
+PrintingContext::PrintingContext()
+ :
+#ifndef NDEBUG
+ page_number_(-1),
+#endif
+ dialog_box_dismissed_(false),
+ in_print_job_(false),
+ abort_printing_(false) {
+}
+
+PrintingContext::~PrintingContext() {
+ ResetSettings();
+}
+
+
+PrintingContext::Result PrintingContext::UseDefaultSettings() {
+ DCHECK(!in_print_job_);
+
+ NOTIMPLEMENTED();
+
+ return FAILED;
+}
+
+PrintingContext::Result PrintingContext::InitWithSettings(
+ const PrintSettings& settings) {
+ DCHECK(!in_print_job_);
+ settings_ = settings;
+
+ NOTIMPLEMENTED();
+
+ return FAILED;
+}
+
+void PrintingContext::ResetSettings() {
+#ifndef NDEBUG
+ page_number_ = -1;
+#endif
+ dialog_box_dismissed_ = false;
+ abort_printing_ = false;
+ in_print_job_ = false;
+}
+
+PrintingContext::Result PrintingContext::NewDocument(
+ const std::wstring& document_name) {
+ DCHECK(!in_print_job_);
+
+ NOTIMPLEMENTED();
+
+#ifndef NDEBUG
+ page_number_ = 0;
+#endif
+
+ return FAILED;
+}
+
+PrintingContext::Result PrintingContext::NewPage() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ NOTIMPLEMENTED();
+
+#ifndef NDEBUG
+ ++page_number_;
+#endif
+
+ return FAILED;
+}
+
+PrintingContext::Result PrintingContext::PageDone() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ NOTIMPLEMENTED();
+
+ return FAILED;
+}
+
+PrintingContext::Result PrintingContext::DocumentDone() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ NOTIMPLEMENTED();
+
+ ResetSettings();
+ return FAILED;
+}
+
+void PrintingContext::Cancel() {
+ abort_printing_ = true;
+ in_print_job_ = false;
+
+ NOTIMPLEMENTED();
+}
+
+void PrintingContext::DismissDialog() {
+ NOTIMPLEMENTED();
+}
+
+PrintingContext::Result PrintingContext::OnError() {
+ ResetSettings();
+ return abort_printing_ ? CANCEL : FAILED;
+}
+
+} // namespace printing
diff --git a/printing/printing_context_mac.cc b/printing/printing_context_mac.cc
new file mode 100644
index 0000000..fcc51f7
--- /dev/null
+++ b/printing/printing_context_mac.cc
@@ -0,0 +1,117 @@
+// Copyright (c) 2009 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"
+
+namespace printing {
+
+PrintingContext::PrintingContext()
+ :
+#ifndef NDEBUG
+ page_number_(-1),
+#endif
+ dialog_box_dismissed_(false),
+ in_print_job_(false),
+ abort_printing_(false) {
+}
+
+PrintingContext::~PrintingContext() {
+ ResetSettings();
+}
+
+
+PrintingContext::Result PrintingContext::UseDefaultSettings() {
+ DCHECK(!in_print_job_);
+
+ NOTIMPLEMENTED();
+
+ return FAILED;
+}
+
+PrintingContext::Result PrintingContext::InitWithSettings(
+ const PrintSettings& settings) {
+ DCHECK(!in_print_job_);
+ settings_ = settings;
+
+ NOTIMPLEMENTED();
+
+ return FAILED;
+}
+
+void PrintingContext::ResetSettings() {
+#ifndef NDEBUG
+ page_number_ = -1;
+#endif
+ dialog_box_dismissed_ = false;
+ abort_printing_ = false;
+ in_print_job_ = false;
+}
+
+PrintingContext::Result PrintingContext::NewDocument(
+ const std::wstring& document_name) {
+ DCHECK(!in_print_job_);
+
+ NOTIMPLEMENTED();
+
+#ifndef NDEBUG
+ page_number_ = 0;
+#endif
+
+ return FAILED;
+}
+
+PrintingContext::Result PrintingContext::NewPage() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ NOTIMPLEMENTED();
+
+#ifndef NDEBUG
+ ++page_number_;
+#endif
+
+ return FAILED;
+}
+
+PrintingContext::Result PrintingContext::PageDone() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ NOTIMPLEMENTED();
+
+ return FAILED;
+}
+
+PrintingContext::Result PrintingContext::DocumentDone() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ NOTIMPLEMENTED();
+
+ ResetSettings();
+ return FAILED;
+}
+
+void PrintingContext::Cancel() {
+ abort_printing_ = true;
+ in_print_job_ = false;
+
+ NOTIMPLEMENTED();
+}
+
+void PrintingContext::DismissDialog() {
+ NOTIMPLEMENTED();
+}
+
+PrintingContext::Result PrintingContext::OnError() {
+ ResetSettings();
+ return abort_printing_ ? CANCEL : FAILED;
+}
+
+} // namespace printing
diff --git a/printing/printing_context_win.cc b/printing/printing_context_win.cc
new file mode 100644
index 0000000..4e506a5
--- /dev/null
+++ b/printing/printing_context_win.cc
@@ -0,0 +1,607 @@
+// 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/printing_context.h"
+
+#include <winspool.h>
+
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/time.h"
+#include "base/time_format.h"
+#include "printing/printed_document.h"
+#include "skia/ext/platform_device_win.h"
+
+using base::Time;
+
+namespace {
+
+// Retrieves the content of a GetPrinter call.
+void GetPrinterHelper(HANDLE printer, int level, scoped_array<uint8>* buffer) {
+ DWORD buf_size = 0;
+ GetPrinter(printer, level, NULL, 0, &buf_size);
+ if (buf_size) {
+ buffer->reset(new uint8[buf_size]);
+ memset(buffer->get(), 0, buf_size);
+ if (!GetPrinter(printer, level, buffer->get(), buf_size, &buf_size)) {
+ buffer->reset();
+ }
+ }
+}
+
+} // namespace
+
+namespace printing {
+
+class PrintingContext::CallbackHandler
+ : public IPrintDialogCallback,
+ public IObjectWithSite {
+ public:
+ CallbackHandler(PrintingContext& 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:
+ PrintingContext& owner_;
+ HWND owner_hwnd_;
+ IPrintDialogServices* services_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CallbackHandler);
+};
+
+PrintingContext::PrintingContext()
+ : hdc_(NULL),
+#ifndef NDEBUG
+ page_number_(-1),
+#endif
+ dialog_box_(NULL),
+ dialog_box_dismissed_(false),
+ in_print_job_(false),
+ abort_printing_(false) {
+}
+
+PrintingContext::~PrintingContext() {
+ ResetSettings();
+}
+
+PrintingContext::Result PrintingContext::AskUserForSettings(
+ HWND window,
+ int max_pages,
+ bool has_selection) {
+ DCHECK(window);
+ DCHECK(!in_print_job_);
+ dialog_box_dismissed_ = false;
+ // 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;
+ }
+
+ {
+ if (PrintDlgEx(&dialog_options) != S_OK) {
+ ResetSettings();
+ return FAILED;
+ }
+ }
+ // TODO(maruel): Support PD_PRINTTOFILE.
+ return ParseDialogResultEx(dialog_options);
+}
+
+PrintingContext::Result PrintingContext::UseDefaultSettings() {
+ DCHECK(!in_print_job_);
+
+ PRINTDLG dialog_options = { sizeof(PRINTDLG) };
+ dialog_options.Flags = PD_RETURNDC | PD_RETURNDEFAULT;
+ if (PrintDlg(&dialog_options) == 0) {
+ ResetSettings();
+ return FAILED;
+ }
+ return ParseDialogResult(dialog_options);
+}
+
+PrintingContext::Result PrintingContext::InitWithSettings(
+ const PrintSettings& settings) {
+ DCHECK(!in_print_job_);
+ settings_ = settings;
+ // TODO(maruel): settings_->ToDEVMODE()
+ HANDLE printer;
+ if (!OpenPrinter(const_cast<wchar_t*>(settings_.device_name().c_str()),
+ &printer,
+ NULL))
+ return FAILED;
+
+ Result status = OK;
+
+ if (!GetPrinterSettings(printer, settings_.device_name()))
+ status = FAILED;
+
+ // Close the printer after retrieving the context.
+ ClosePrinter(printer);
+
+ if (status != OK)
+ ResetSettings();
+ return status;
+}
+
+void PrintingContext::ResetSettings() {
+ if (hdc_ != NULL) {
+ DeleteDC(hdc_);
+ hdc_ = NULL;
+ }
+ settings_.Clear();
+ in_print_job_ = false;
+
+#ifndef NDEBUG
+ page_number_ = -1;
+#endif
+}
+
+PrintingContext::Result PrintingContext::NewDocument(
+ const std::wstring& document_name) {
+ DCHECK(!in_print_job_);
+ if (!hdc_)
+ 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(hdc_, &AbortProc))
+ return OnError();
+
+ DOCINFO di = { sizeof(DOCINFO) };
+ di.lpszDocName = document_name.c_str();
+
+ // Is there a debug dump directory specified? If so, force to print to a file.
+ std::wstring 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 += document_name;
+ filename += L"_";
+ filename += L"buffer.prn";
+ file_util::ReplaceIllegalCharacters(&filename, '_');
+ file_util::AppendToPath(&debug_dump_path, filename);
+ di.lpszOutput = debug_dump_path.c_str();
+ }
+
+ DCHECK_EQ(MessageLoop::current()->NestableTasksAllowed(), false);
+ // 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(hdc_, &di) <= 0)
+ return OnError();
+
+#ifndef NDEBUG
+ page_number_ = 0;
+#endif
+ return OK;
+}
+
+PrintingContext::Result PrintingContext::NewPage() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ // Inform the driver that the application is about to begin sending data.
+ if (StartPage(hdc_) <= 0)
+ return OnError();
+
+#ifndef NDEBUG
+ ++page_number_;
+#endif
+
+ return OK;
+}
+
+PrintingContext::Result PrintingContext::PageDone() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ if (EndPage(hdc_) <= 0)
+ return OnError();
+ return OK;
+}
+
+PrintingContext::Result PrintingContext::DocumentDone() {
+ if (abort_printing_)
+ return CANCEL;
+ DCHECK(in_print_job_);
+
+ // Inform the driver that document has ended.
+ if (EndDoc(hdc_) <= 0)
+ return OnError();
+
+ ResetSettings();
+ return OK;
+}
+
+void PrintingContext::Cancel() {
+ abort_printing_ = true;
+ in_print_job_ = false;
+ if (hdc_)
+ CancelDC(hdc_);
+ DismissDialog();
+}
+
+void PrintingContext::DismissDialog() {
+ if (dialog_box_) {
+ DestroyWindow(dialog_box_);
+ dialog_box_dismissed_ = true;
+ }
+}
+
+PrintingContext::Result PrintingContext::OnError() {
+ // This will close hdc_ and clear settings_.
+ ResetSettings();
+ return abort_printing_ ? CANCEL : FAILED;
+}
+
+// static
+BOOL PrintingContext::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 PrintingContext::InitializeSettings(const DEVMODE& dev_mode,
+ const std::wstring& new_device_name,
+ const PRINTPAGERANGE* ranges,
+ int number_ranges,
+ bool selection_only) {
+ skia::PlatformDevice::InitializeDC(hdc_);
+ DCHECK(GetDeviceCaps(hdc_, CLIPCAPS));
+ DCHECK(GetDeviceCaps(hdc_, RASTERCAPS) & RC_STRETCHDIB);
+ DCHECK(GetDeviceCaps(hdc_, RASTERCAPS) & RC_BITMAP64);
+ // Some printers don't advertise these.
+ // DCHECK(GetDeviceCaps(hdc_, RASTERCAPS) & RC_SCALING);
+ // DCHECK(GetDeviceCaps(hdc_, SHADEBLENDCAPS) & SB_CONST_ALPHA);
+ // DCHECK(GetDeviceCaps(hdc_, SHADEBLENDCAPS) & SB_PIXEL_ALPHA);
+
+ // StretchDIBits() support is needed for printing.
+ if (!(GetDeviceCaps(hdc_, RASTERCAPS) & RC_STRETCHDIB) ||
+ !(GetDeviceCaps(hdc_, RASTERCAPS) & RC_BITMAP64)) {
+ NOTREACHED();
+ ResetSettings();
+ return false;
+ }
+
+ DCHECK(!in_print_job_);
+ DCHECK(hdc_);
+ 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);
+ }
+ }
+ settings_.Init(hdc_,
+ dev_mode,
+ ranges_vector,
+ new_device_name,
+ selection_only);
+ return true;
+}
+
+bool PrintingContext::GetPrinterSettings(HANDLE printer,
+ const std::wstring& device_name) {
+ DCHECK(!in_print_job_);
+ scoped_array<uint8> buffer;
+
+ // A PRINTER_INFO_9 structure specifying the per-user default printer
+ // settings.
+ GetPrinterHelper(printer, 9, &buffer);
+ if (buffer.get()) {
+ PRINTER_INFO_9* info_9 = reinterpret_cast<PRINTER_INFO_9*>(buffer.get());
+ if (info_9->pDevMode != NULL) {
+ if (!AllocateContext(device_name, info_9->pDevMode)) {
+ ResetSettings();
+ return false;
+ }
+ return InitializeSettings(*info_9->pDevMode, device_name, NULL, 0, false);
+ }
+ buffer.reset();
+ }
+
+ // A PRINTER_INFO_8 structure specifying the global default printer settings.
+ GetPrinterHelper(printer, 8, &buffer);
+ if (buffer.get()) {
+ PRINTER_INFO_8* info_8 = reinterpret_cast<PRINTER_INFO_8*>(buffer.get());
+ if (info_8->pDevMode != NULL) {
+ if (!AllocateContext(device_name, info_8->pDevMode)) {
+ ResetSettings();
+ return false;
+ }
+ return InitializeSettings(*info_8->pDevMode, device_name, NULL, 0, false);
+ }
+ buffer.reset();
+ }
+
+ // A PRINTER_INFO_2 structure specifying the driver's default printer
+ // settings.
+ GetPrinterHelper(printer, 2, &buffer);
+ if (buffer.get()) {
+ PRINTER_INFO_2* info_2 = reinterpret_cast<PRINTER_INFO_2*>(buffer.get());
+ if (info_2->pDevMode != NULL) {
+ if (!AllocateContext(device_name, info_2->pDevMode)) {
+ ResetSettings();
+ return false;
+ }
+ return InitializeSettings(*info_2->pDevMode, device_name, NULL, 0, false);
+ }
+ buffer.reset();
+ }
+ // Failed to retrieve the printer settings.
+ ResetSettings();
+ return false;
+}
+
+bool PrintingContext::AllocateContext(const std::wstring& printer_name,
+ const DEVMODE* dev_mode) {
+ hdc_ = CreateDC(L"WINSPOOL", printer_name.c_str(), NULL, dev_mode);
+ DCHECK(hdc_);
+ return hdc_ != NULL;
+}
+
+PrintingContext::Result PrintingContext::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()) {
+ hdc_ = 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,
+ dialog_options.lpPageRanges,
+ dialog_options.nPageRanges,
+ print_selection_only);
+ }
+
+ if (!success && dialog_options.hDC) {
+ DeleteDC(dialog_options.hDC);
+ hdc_ = 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 hdc_ ? OK : FAILED;
+ case PD_RESULT_APPLY:
+ return hdc_ ? CANCEL : FAILED;
+ case PD_RESULT_CANCEL:
+ return CANCEL;
+ default:
+ return FAILED;
+ }
+}
+
+PrintingContext::Result PrintingContext::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()) {
+ hdc_ = dialog_options.hDC;
+ success = InitializeSettings(*dev_mode, device_name, NULL, 0, false);
+ }
+
+ if (!success && dialog_options.hDC) {
+ DeleteDC(dialog_options.hDC);
+ hdc_ = 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 hdc_ ? OK : FAILED;
+}
+
+} // namespace printing
diff --git a/printing/printing_context_win_unittest.cc b/printing/printing_context_win_unittest.cc
new file mode 100644
index 0000000..9e19e3b
--- /dev/null
+++ b/printing/printing_context_win_unittest.cc
@@ -0,0 +1,28 @@
+// 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/printing_context.h"
+
+#include "printing/printing_test.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> {
+};
+
+TEST_F(PrintingContextTest, Base) {
+ printing::PrintSettings settings;
+
+ settings.set_device_name(GetDefaultPrinter());
+ // Initialize it.
+ printing::PrintingContext context;
+ EXPECT_EQ(context.InitWithSettings(settings), printing::PrintingContext::OK);
+
+ // 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));
+}
diff --git a/printing/printing_test.h b/printing/printing_test.h
new file mode 100644
index 0000000..3237813
--- /dev/null
+++ b/printing/printing_test.h
@@ -0,0 +1,36 @@
+// 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>
+
+// Disable the whole test case when executing on a computer that has no printer
+// installed.
+// Note: Parent should be testing::Test or UITest.
+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__