From 8ff1d42631e79e842669dc3051d91ed7db80f1dc Mon Sep 17 00:00:00 2001 From: "sverrir@google.com" Date: Tue, 7 Jul 2009 21:31:39 +0000 Subject: 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 --- printing/DEPS | 3 + printing/emf_win.h | 6 +- printing/emf_win_unittest.cc | 2 +- printing/native_metafile.h | 10 +- printing/page_number.cc | 83 ++++ printing/page_number.h | 73 ++++ printing/page_number_unittest.cc | 30 ++ printing/page_overlays.cc | 206 ++++++++++ printing/page_overlays.h | 80 ++++ printing/page_overlays_unittest.cc | 92 +++++ printing/page_range.cc | 31 ++ printing/page_range.h | 34 ++ printing/page_range_unittest.cc | 38 ++ printing/page_setup.cc | 126 +++++++ printing/page_setup.h | 82 ++++ printing/page_setup_unittest.cc | 146 +++++++ printing/print_settings.cc | 116 ++++++ printing/print_settings.h | 107 ++++++ printing/printed_document.cc | 366 ++++++++++++++++++ printing/printed_document.h | 192 ++++++++++ printing/printed_page.cc | 24 ++ printing/printed_page.h | 50 +++ printing/printed_pages_source.h | 29 ++ printing/printing.gyp | 50 ++- printing/printing_context.h | 152 ++++++++ printing/printing_context_linux.cc | 117 ++++++ printing/printing_context_mac.cc | 117 ++++++ printing/printing_context_win.cc | 607 ++++++++++++++++++++++++++++++ printing/printing_context_win_unittest.cc | 28 ++ printing/printing_test.h | 36 ++ 30 files changed, 3022 insertions(+), 11 deletions(-) create mode 100644 printing/page_number.cc create mode 100644 printing/page_number.h create mode 100644 printing/page_number_unittest.cc create mode 100644 printing/page_overlays.cc create mode 100644 printing/page_overlays.h create mode 100644 printing/page_overlays_unittest.cc create mode 100644 printing/page_range.cc create mode 100644 printing/page_range.h create mode 100644 printing/page_range_unittest.cc create mode 100644 printing/page_setup.cc create mode 100644 printing/page_setup.h create mode 100644 printing/page_setup_unittest.cc create mode 100644 printing/print_settings.cc create mode 100644 printing/print_settings.h create mode 100644 printing/printed_document.cc create mode 100644 printing/printed_document.h create mode 100644 printing/printed_page.cc create mode 100644 printing/printed_page.h create mode 100644 printing/printed_pages_source.h create mode 100644 printing/printing_context.h create mode 100644 printing/printing_context_linux.cc create mode 100644 printing/printing_context_mac.cc create mode 100644 printing/printing_context_win.cc create mode 100644 printing/printing_context_win_unittest.cc create mode 100644 printing/printing_test.h (limited to 'printing') 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 #include @@ -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 + +#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( + std::numeric_limits::max())); + if (++page_range_index_ == static_cast(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 + +#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 +inline typename std::basic_ostream& operator<<( + typename std::basic_ostream& 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 + +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 doc( + new printing::PrintedDocument(settings, &source, cookie)); + doc->set_page_count(2); + gfx::Size page_size(100, 100); + scoped_refptr 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 PageRange::GetPages(const PageRanges& ranges) { + std::set 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 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 + +namespace printing { + +struct PageRange; + +typedef std::vector 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 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 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 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 +#include + +#include "testing/gtest/include/gtest/gtest.h" + +TEST(PageSetupTest, Random) { + time_t seed = time(NULL); + int kMax = 10; + srand(static_cast(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 + +#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 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 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* 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( + immutable_.settings_.page_setup_pixels().content_area().x()); + xform.eDy = static_cast( + immutable_.settings_.page_setup_pixels().content_area().y()); + xform.eM11 = static_cast(1. / mutable_.shrink_factor); + xform.eM22 = static_cast(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 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> 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(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 + +#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 { + 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* 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 > 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 { + 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 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 + +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 +#include +#endif + +#include + +#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 + +#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* 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(static_cast(this)); + } + + // IUnknown + virtual HRESULT WINAPI QueryInterface(REFIID riid, void**object) { + if (riid == IID_IUnknown) { + *object = ToIUnknown(); + } else if (riid == IID_IPrintDialogCallback) { + *object = static_cast(this); + } else if (riid == IID_IObjectWithSite) { + *object = static_cast(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(&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(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 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(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(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(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(GlobalLock(dialog_options.hDevMode)); + DCHECK(dev_mode); + } + + std::wstring device_name; + if (dialog_options.hDevNames) { + DEVNAMES* dev_names = + reinterpret_cast(GlobalLock(dialog_options.hDevNames)); + DCHECK(dev_names); + if (dev_names) { + device_name = + reinterpret_cast( + reinterpret_cast(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(GlobalLock(dialog_options.hDevMode)); + DCHECK(dev_mode); + } + + std::wstring device_name; + if (dialog_options.hDevNames) { + DEVNAMES* dev_names = + reinterpret_cast(GlobalLock(dialog_options.hDevNames)); + DCHECK(dev_names); + if (dev_names) { + device_name = + reinterpret_cast( + reinterpret_cast(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 { +}; + +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 +#include + +// Disable the whole test case when executing on a computer that has no printer +// installed. +// Note: Parent should be testing::Test or UITest. +template +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__ -- cgit v1.1