summaryrefslogtreecommitdiffstats
path: root/printing/printed_document.cc
diff options
context:
space:
mode:
Diffstat (limited to 'printing/printed_document.cc')
-rw-r--r--printing/printed_document.cc366
1 files changed, 366 insertions, 0 deletions
diff --git a/printing/printed_document.cc b/printing/printed_document.cc
new file mode 100644
index 0000000..517548b
--- /dev/null
+++ b/printing/printed_document.cc
@@ -0,0 +1,366 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/printed_document.h"
+
+#include <set>
+
+#include "app/gfx/font.h"
+#include "app/gfx/text_elider.h"
+#include "app/win_util.h"
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/singleton.h"
+#include "base/string_util.h"
+#include "base/time.h"
+#include "printing/page_number.h"
+#include "printing/page_overlays.h"
+#include "printing/printed_pages_source.h"
+#include "printing/printed_page.h"
+#include "printing/units.h"
+#include "skia/ext/platform_device.h"
+
+using base::Time;
+
+namespace {
+
+struct PrintDebugDumpPath {
+ PrintDebugDumpPath()
+ : enabled(false) {
+ }
+
+ bool enabled;
+ std::wstring debug_dump_path;
+};
+
+Singleton<PrintDebugDumpPath> g_debug_dump_info;
+
+} // namespace
+
+namespace printing {
+
+PrintedDocument::PrintedDocument(const PrintSettings& settings,
+ PrintedPagesSource* source,
+ int cookie)
+ : mutable_(source),
+ immutable_(settings, source, cookie) {
+
+ // Records the expected page count if a range is setup.
+ if (!settings.ranges.empty()) {
+ // If there is a range, set the number of page
+ for (unsigned i = 0; i < settings.ranges.size(); ++i) {
+ const PageRange& range = settings.ranges[i];
+ mutable_.expected_page_count_ += range.to - range.from + 1;
+ }
+ }
+}
+
+PrintedDocument::~PrintedDocument() {
+}
+
+void PrintedDocument::SetPage(int page_number,
+ NativeMetafile* metafile,
+ double shrink) {
+ // Notice the page_number + 1, the reason is that this is the value that will
+ // be shown. Users dislike 0-based counting.
+ scoped_refptr<PrintedPage> page(
+ new PrintedPage(page_number + 1,
+ metafile, immutable_.settings_.page_setup_pixels().physical_size()));
+ {
+ AutoLock lock(lock_);
+ mutable_.pages_[page_number] = page;
+ if (mutable_.shrink_factor == 0) {
+ mutable_.shrink_factor = shrink;
+ } else {
+ DCHECK_EQ(mutable_.shrink_factor, shrink);
+ }
+ }
+ DebugDump(*page);
+}
+
+bool PrintedDocument::GetPage(int page_number,
+ scoped_refptr<PrintedPage>* page) {
+ AutoLock lock(lock_);
+ PrintedPages::const_iterator itr = mutable_.pages_.find(page_number);
+ if (itr != mutable_.pages_.end()) {
+ if (itr->second.get()) {
+ *page = itr->second;
+ return true;
+ }
+ }
+ return false;
+}
+
+void PrintedDocument::RenderPrintedPage(const PrintedPage& page,
+ HDC context) const {
+#ifndef NDEBUG
+ {
+ // Make sure the page is from our list.
+ AutoLock lock(lock_);
+ DCHECK(&page == mutable_.pages_.find(page.page_number() - 1)->second.get());
+ }
+#endif
+
+ // Save the state to make sure the context this function call does not modify
+ // the device context.
+ int saved_state = SaveDC(context);
+ DCHECK_NE(saved_state, 0);
+ skia::PlatformDevice::InitializeDC(context);
+ {
+ // Save the state (again) to apply the necessary world transformation.
+ int saved_state = SaveDC(context);
+ DCHECK_NE(saved_state, 0);
+
+ // Setup the matrix to translate and scale to the right place. Take in
+ // account the actual shrinking factor.
+ XFORM xform = { 0 };
+ xform.eDx = static_cast<float>(
+ immutable_.settings_.page_setup_pixels().content_area().x());
+ xform.eDy = static_cast<float>(
+ immutable_.settings_.page_setup_pixels().content_area().y());
+ xform.eM11 = static_cast<float>(1. / mutable_.shrink_factor);
+ xform.eM22 = static_cast<float>(1. / mutable_.shrink_factor);
+ BOOL res = ModifyWorldTransform(context, &xform, MWT_LEFTMULTIPLY);
+ DCHECK_NE(res, 0);
+
+ if (!page.native_metafile()->SafePlayback(context)) {
+ NOTREACHED();
+ }
+
+ res = RestoreDC(context, saved_state);
+ DCHECK_NE(res, 0);
+ }
+
+ // Print the header and footer.
+ int base_font_size = gfx::Font().height();
+ int new_font_size = ConvertUnit(10,
+ immutable_.settings_.desired_dpi,
+ immutable_.settings_.dpi());
+ DCHECK_GT(new_font_size, base_font_size);
+ gfx::Font font(gfx::Font().DeriveFont(new_font_size - base_font_size));
+ HGDIOBJ old_font = SelectObject(context, font.hfont());
+ DCHECK(old_font != NULL);
+ // We don't want a white square around the text ever if overflowing.
+ SetBkMode(context, TRANSPARENT);
+ PrintHeaderFooter(context, page, PageOverlays::LEFT, PageOverlays::TOP,
+ font);
+ PrintHeaderFooter(context, page, PageOverlays::CENTER, PageOverlays::TOP,
+ font);
+ PrintHeaderFooter(context, page, PageOverlays::RIGHT, PageOverlays::TOP,
+ font);
+ PrintHeaderFooter(context, page, PageOverlays::LEFT, PageOverlays::BOTTOM,
+ font);
+ PrintHeaderFooter(context, page, PageOverlays::CENTER, PageOverlays::BOTTOM,
+ font);
+ PrintHeaderFooter(context, page, PageOverlays::RIGHT, PageOverlays::BOTTOM,
+ font);
+ int res = RestoreDC(context, saved_state);
+ DCHECK_NE(res, 0);
+}
+
+bool PrintedDocument::RenderPrintedPageNumber(int page_number, HDC context) {
+ scoped_refptr<PrintedPage> page;
+ if (!GetPage(page_number, &page))
+ return false;
+ RenderPrintedPage(*page.get(), context);
+ return true;
+}
+
+bool PrintedDocument::IsComplete() const {
+ AutoLock lock(lock_);
+ if (!mutable_.page_count_)
+ return false;
+ PageNumber page(immutable_.settings_, mutable_.page_count_);
+ if (page == PageNumber::npos())
+ return false;
+ for (; page != PageNumber::npos(); ++page) {
+ PrintedPages::const_iterator itr = mutable_.pages_.find(page.ToInt());
+ if (itr == mutable_.pages_.end() || !itr->second.get() ||
+ !itr->second->native_metafile())
+ return false;
+ }
+ return true;
+}
+
+void PrintedDocument::DisconnectSource() {
+ AutoLock lock(lock_);
+ mutable_.source_ = NULL;
+}
+
+size_t PrintedDocument::MemoryUsage() const {
+ std::vector<scoped_refptr<PrintedPage>> pages_copy;
+ {
+ AutoLock lock(lock_);
+ pages_copy.reserve(mutable_.pages_.size());
+ PrintedPages::const_iterator end = mutable_.pages_.end();
+ for (PrintedPages::const_iterator itr = mutable_.pages_.begin();
+ itr != end; ++itr) {
+ if (itr->second.get()) {
+ pages_copy.push_back(itr->second);
+ }
+ }
+ }
+ size_t total = 0;
+ for (size_t i = 0; i < pages_copy.size(); ++i) {
+ total += pages_copy[i]->native_metafile()->GetDataSize();
+ }
+ return total;
+}
+
+void PrintedDocument::set_page_count(int max_page) {
+ AutoLock lock(lock_);
+ DCHECK_EQ(0, mutable_.page_count_);
+ mutable_.page_count_ = max_page;
+ if (immutable_.settings_.ranges.empty()) {
+ mutable_.expected_page_count_ = max_page;
+ } else {
+ // If there is a range, don't bother since expected_page_count_ is already
+ // initialized.
+ DCHECK_NE(mutable_.expected_page_count_, 0);
+ }
+}
+
+int PrintedDocument::page_count() const {
+ AutoLock lock(lock_);
+ return mutable_.page_count_;
+}
+
+int PrintedDocument::expected_page_count() const {
+ AutoLock lock(lock_);
+ return mutable_.expected_page_count_;
+}
+
+void PrintedDocument::PrintHeaderFooter(HDC context,
+ const PrintedPage& page,
+ PageOverlays::HorizontalPosition x,
+ PageOverlays::VerticalPosition y,
+ const gfx::Font& font) const {
+ const PrintSettings& settings = immutable_.settings_;
+ const std::wstring& line = settings.overlays.GetOverlay(x, y);
+ if (line.empty()) {
+ return;
+ }
+ std::wstring output(PageOverlays::ReplaceVariables(line, *this, page));
+ if (output.empty()) {
+ // May happens if document name or url is empty.
+ return;
+ }
+ const gfx::Size string_size(font.GetStringWidth(output), font.height());
+ gfx::Rect bounding;
+ bounding.set_height(string_size.height());
+ const gfx::Rect& overlay_area(settings.page_setup_pixels().overlay_area());
+ // Hard code .25 cm interstice between overlays. Make sure that some space is
+ // kept between each headers.
+ const int interstice = ConvertUnit(250, kHundrethsMMPerInch, settings.dpi());
+ const int max_width = overlay_area.width() / 3 - interstice;
+ const int actual_width = std::min(string_size.width(), max_width);
+ switch (x) {
+ case PageOverlays::LEFT:
+ bounding.set_x(overlay_area.x());
+ bounding.set_width(max_width);
+ break;
+ case PageOverlays::CENTER:
+ bounding.set_x(overlay_area.x() +
+ (overlay_area.width() - actual_width) / 2);
+ bounding.set_width(actual_width);
+ break;
+ case PageOverlays::RIGHT:
+ bounding.set_x(overlay_area.right() - actual_width);
+ bounding.set_width(actual_width);
+ break;
+ }
+
+ DCHECK_LE(bounding.right(), overlay_area.right());
+
+ switch (y) {
+ case PageOverlays::BOTTOM:
+ bounding.set_y(overlay_area.bottom() - string_size.height());
+ break;
+ case PageOverlays::TOP:
+ bounding.set_y(overlay_area.y());
+ break;
+ }
+
+ if (string_size.width() > bounding.width()) {
+ if (line == PageOverlays::kUrl) {
+ output = gfx::ElideUrl(url(), font, bounding.width(), std::wstring());
+ } else {
+ output = gfx::ElideText(output, font, bounding.width());
+ }
+ }
+
+ // Save the state (again) for the clipping region.
+ int saved_state = SaveDC(context);
+ DCHECK_NE(saved_state, 0);
+
+ int result = IntersectClipRect(context, bounding.x(), bounding.y(),
+ bounding.right() + 1, bounding.bottom() + 1);
+ DCHECK(result == SIMPLEREGION || result == COMPLEXREGION);
+ TextOut(context,
+ bounding.x(), bounding.y(),
+ output.c_str(),
+ static_cast<int>(output.size()));
+ int res = RestoreDC(context, saved_state);
+ DCHECK_NE(res, 0);
+}
+
+void PrintedDocument::DebugDump(const PrintedPage& page)
+{
+ if (!g_debug_dump_info->enabled)
+ return;
+
+ std::wstring filename;
+ filename += date();
+ filename += L"_";
+ filename += time();
+ filename += L"_";
+ filename += name();
+ filename += L"_";
+ filename += StringPrintf(L"%02d", page.page_number());
+ filename += L"_.emf";
+ file_util::ReplaceIllegalCharacters(&filename, '_');
+ std::wstring path(g_debug_dump_info->debug_dump_path);
+ file_util::AppendToPath(&path, filename);
+ page.native_metafile()->SaveTo(path);
+}
+
+void PrintedDocument::set_debug_dump_path(const std::wstring& debug_dump_path) {
+ g_debug_dump_info->enabled = !debug_dump_path.empty();
+ g_debug_dump_info->debug_dump_path = debug_dump_path;
+}
+
+const std::wstring& PrintedDocument::debug_dump_path() {
+ return g_debug_dump_info->debug_dump_path;
+}
+
+PrintedDocument::Mutable::Mutable(PrintedPagesSource* source)
+ : source_(source),
+ expected_page_count_(0),
+ page_count_(0),
+ shrink_factor(0) {
+}
+
+PrintedDocument::Immutable::Immutable(const PrintSettings& settings,
+ PrintedPagesSource* source,
+ int cookie)
+ : settings_(settings),
+ source_message_loop_(MessageLoop::current()),
+ name_(source->RenderSourceName()),
+ url_(source->RenderSourceUrl()),
+ cookie_(cookie) {
+ // Setup the document's date.
+#ifdef WIN32
+ // On Windows, use the native time formatting for printing.
+ SYSTEMTIME systemtime;
+ GetLocalTime(&systemtime);
+ date_ = win_util::FormatSystemDate(systemtime, std::wstring());
+ time_ = win_util::FormatSystemTime(systemtime, std::wstring());
+#else
+ Time now = Time::Now();
+ date_ = TimeFormat::ShortDateNumeric(now);
+ time_ = TimeFormat::TimeOfDay(now);
+#endif // WIN32
+}
+
+} // namespace printing