blob: dc4f0f1e7c78d8e116faa494f37d354b0e5175e2 [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "printing/printed_document.h"
#include <algorithm>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/i18n/file_util_icu.h"
#include "base/i18n/time_formatting.h"
#include "base/json/json_writer.h"
#include "base/lazy_instance.h"
#include "base/memory/ref_counted_memory.h"
#include "base/message_loop/message_loop.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "printing/page_number.h"
#include "printing/print_settings_conversion.h"
#include "printing/printed_page.h"
#include "printing/printed_pages_source.h"
#include "printing/units.h"
#include "ui/gfx/font.h"
#include "ui/gfx/text_elider.h"
namespace printing {
namespace {
base::LazyInstance<base::FilePath>::Leaky g_debug_dump_info =
LAZY_INSTANCE_INITIALIZER;
void DebugDumpPageTask(const base::string16& doc_name,
const PrintedPage* page) {
if (g_debug_dump_info.Get().empty())
return;
base::string16 filename = doc_name;
filename +=
base::ASCIIToUTF16(base::StringPrintf("_%04d", page->page_number()));
base::FilePath file_path =
#if defined(OS_WIN)
PrintedDocument::CreateDebugDumpPath(filename, FILE_PATH_LITERAL(".emf"));
#else // OS_WIN
PrintedDocument::CreateDebugDumpPath(filename, FILE_PATH_LITERAL(".pdf"));
#endif // OS_WIN
base::File file(file_path,
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
page->metafile()->SaveTo(&file);
}
void DebugDumpDataTask(const base::string16& doc_name,
const base::FilePath::StringType& extension,
const base::RefCountedMemory* data) {
base::FilePath path =
PrintedDocument::CreateDebugDumpPath(doc_name, extension);
if (path.empty())
return;
base::WriteFile(path,
reinterpret_cast<const char*>(data->front()),
base::checked_cast<int>(data->size()));
}
void DebugDumpSettings(const base::string16& doc_name,
const PrintSettings& settings,
base::TaskRunner* blocking_runner) {
base::DictionaryValue job_settings;
PrintSettingsToJobSettingsDebug(settings, &job_settings);
std::string settings_str;
base::JSONWriter::WriteWithOptions(
job_settings, base::JSONWriter::OPTIONS_PRETTY_PRINT, &settings_str);
scoped_refptr<base::RefCountedMemory> data =
base::RefCountedString::TakeString(&settings_str);
blocking_runner->PostTask(FROM_HERE, base::Bind(&DebugDumpDataTask, doc_name,
FILE_PATH_LITERAL(".json"),
base::RetainedRef(data)));
}
} // namespace
PrintedDocument::PrintedDocument(const PrintSettings& settings,
PrintedPagesSource* source,
int cookie,
base::TaskRunner* blocking_runner)
: mutable_(source), immutable_(settings, source, cookie, blocking_runner) {
// 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;
}
}
if (!g_debug_dump_info.Get().empty())
DebugDumpSettings(name(), settings, blocking_runner);
}
PrintedDocument::~PrintedDocument() {
}
void PrintedDocument::SetPage(int page_number,
std::unique_ptr<MetafilePlayer> metafile,
#if defined(OS_WIN)
float shrink,
#endif // OS_WIN
const gfx::Size& paper_size,
const gfx::Rect& page_rect) {
// Notice the page_number + 1, the reason is that this is the value that will
// be shown. Users dislike 0-based counting.
scoped_refptr<PrintedPage> page(new PrintedPage(
page_number + 1, std::move(metafile), paper_size, page_rect));
#if defined(OS_WIN)
page->set_shrink_factor(shrink);
#endif // OS_WIN
{
base::AutoLock lock(lock_);
mutable_.pages_[page_number] = page;
#if defined(OS_POSIX) && !defined(OS_MACOSX)
if (page_number < mutable_.first_page)
mutable_.first_page = page_number;
#endif
}
if (!g_debug_dump_info.Get().empty()) {
immutable_.blocking_runner_->PostTask(
FROM_HERE,
base::Bind(&DebugDumpPageTask, name(), base::RetainedRef(page)));
}
}
scoped_refptr<PrintedPage> PrintedDocument::GetPage(int page_number) {
scoped_refptr<PrintedPage> page;
{
base::AutoLock lock(lock_);
PrintedPages::const_iterator itr = mutable_.pages_.find(page_number);
if (itr != mutable_.pages_.end())
page = itr->second;
}
return page;
}
bool PrintedDocument::IsComplete() const {
base::AutoLock lock(lock_);
if (!mutable_.page_count_)
return false;
PageNumber page(immutable_.settings_, mutable_.page_count_);
if (page == PageNumber::npos())
return false;
for (; page != PageNumber::npos(); ++page) {
#if defined(OS_WIN) || defined(OS_MACOSX)
const bool metafile_must_be_valid = true;
#elif defined(OS_POSIX)
const bool metafile_must_be_valid = (page.ToInt() == mutable_.first_page);
#endif
PrintedPages::const_iterator itr = mutable_.pages_.find(page.ToInt());
if (itr == mutable_.pages_.end() || !itr->second.get())
return false;
if (metafile_must_be_valid && !itr->second->metafile())
return false;
}
return true;
}
void PrintedDocument::DisconnectSource() {
base::AutoLock lock(lock_);
mutable_.source_ = NULL;
}
void PrintedDocument::set_page_count(int max_page) {
base::AutoLock lock(lock_);
DCHECK_EQ(0, mutable_.page_count_);
mutable_.page_count_ = max_page;
if (immutable_.settings_.ranges().empty()) {
mutable_.expected_page_count_ = max_page;
} else {
// If there is a range, don't bother since expected_page_count_ is already
// initialized.
DCHECK_NE(mutable_.expected_page_count_, 0);
}
}
int PrintedDocument::page_count() const {
base::AutoLock lock(lock_);
return mutable_.page_count_;
}
int PrintedDocument::expected_page_count() const {
base::AutoLock lock(lock_);
return mutable_.expected_page_count_;
}
void PrintedDocument::set_debug_dump_path(
const base::FilePath& debug_dump_path) {
g_debug_dump_info.Get() = debug_dump_path;
}
base::FilePath PrintedDocument::CreateDebugDumpPath(
const base::string16& document_name,
const base::FilePath::StringType& extension) {
if (g_debug_dump_info.Get().empty())
return base::FilePath();
// Create a filename.
base::string16 filename;
base::Time now(base::Time::Now());
filename = base::TimeFormatShortDateAndTime(now);
filename += base::ASCIIToUTF16("_");
filename += document_name;
base::FilePath::StringType system_filename;
#if defined(OS_WIN)
system_filename = filename;
#else // OS_WIN
system_filename = base::UTF16ToUTF8(filename);
#endif // OS_WIN
base::i18n::ReplaceIllegalCharactersInPath(&system_filename, '_');
return g_debug_dump_info.Get().Append(system_filename).AddExtension(
extension);
}
void PrintedDocument::DebugDumpData(
const base::RefCountedMemory* data,
const base::FilePath::StringType& extension) {
if (g_debug_dump_info.Get().empty())
return;
immutable_.blocking_runner_->PostTask(
FROM_HERE, base::Bind(&DebugDumpDataTask, name(), extension, data));
}
PrintedDocument::Mutable::Mutable(PrintedPagesSource* source)
: source_(source),
expected_page_count_(0),
page_count_(0) {
#if defined(OS_POSIX) && !defined(OS_MACOSX)
first_page = INT_MAX;
#endif
}
PrintedDocument::Mutable::~Mutable() {
}
PrintedDocument::Immutable::Immutable(const PrintSettings& settings,
PrintedPagesSource* source,
int cookie,
base::TaskRunner* blocking_runner)
: settings_(settings),
name_(source->RenderSourceName()),
cookie_(cookie),
blocking_runner_(blocking_runner) {
}
PrintedDocument::Immutable::~Immutable() {
}
#if defined(OS_ANDROID)
// This function is not used on android.
void PrintedDocument::RenderPrintedPage(const PrintedPage& page,
PrintingContext* context) const {
NOTREACHED();
}
#endif
} // namespace printing