ae14df6577
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
367 linhas
12 KiB
C++
367 linhas
12 KiB
C++
// 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
|