1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "printing/printed_document.h"
13 #include "base/bind.h"
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h"
16 #include "base/i18n/file_util_icu.h"
17 #include "base/i18n/time_formatting.h"
18 #include "base/json/json_writer.h"
19 #include "base/lazy_instance.h"
20 #include "base/memory/ref_counted_memory.h"
21 #include "base/numerics/safe_conversions.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "base/task/post_task.h"
26 #include "base/time/time.h"
27 #include "base/values.h"
28 #include "printing/metafile.h"
29 #include "printing/page_number.h"
30 #include "printing/print_settings_conversion.h"
31 #include "printing/units.h"
32 #include "ui/gfx/font.h"
33 #include "ui/gfx/text_elider.h"
36 #include "printing/printed_page_win.h"
43 base::LazyInstance<base::FilePath>::Leaky g_debug_dump_info =
44 LAZY_INSTANCE_INITIALIZER;
47 void DebugDumpPageTask(const base::string16& doc_name,
48 const PrintedPage* page) {
49 DCHECK(PrintedDocument::HasDebugDumpPath());
51 static constexpr base::FilePath::CharType kExtension[] =
52 FILE_PATH_LITERAL(".emf");
54 base::string16 name = doc_name;
55 name += base::ASCIIToUTF16(base::StringPrintf("_%04d", page->page_number()));
56 base::FilePath path = PrintedDocument::CreateDebugDumpPath(name, kExtension);
58 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
59 page->metafile()->SaveTo(&file);
62 void DebugDumpTask(const base::string16& doc_name,
63 const MetafilePlayer* metafile) {
64 DCHECK(PrintedDocument::HasDebugDumpPath());
66 static constexpr base::FilePath::CharType kExtension[] =
67 FILE_PATH_LITERAL(".pdf");
69 base::string16 name = doc_name;
70 base::FilePath path = PrintedDocument::CreateDebugDumpPath(name, kExtension);
72 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
73 metafile->SaveTo(&file);
77 void DebugDumpDataTask(const base::string16& doc_name,
78 const base::FilePath::StringType& extension,
79 const base::RefCountedMemory* data) {
81 PrintedDocument::CreateDebugDumpPath(doc_name, extension);
85 reinterpret_cast<const char*>(data->front()),
86 base::checked_cast<int>(data->size()));
89 void DebugDumpSettings(const base::string16& doc_name,
90 const PrintSettings& settings) {
91 base::DictionaryValue job_settings;
92 PrintSettingsToJobSettingsDebug(settings, &job_settings);
93 std::string settings_str;
94 base::JSONWriter::WriteWithOptions(
95 job_settings, base::JSONWriter::OPTIONS_PRETTY_PRINT, &settings_str);
96 scoped_refptr<base::RefCountedMemory> data =
97 base::RefCountedString::TakeString(&settings_str);
98 base::PostTaskWithTraits(
99 FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
100 base::BindOnce(&DebugDumpDataTask, doc_name, FILE_PATH_LITERAL(".json"),
101 base::RetainedRef(data)));
106 PrintedDocument::PrintedDocument(const PrintSettings& settings,
107 const base::string16& name,
109 : immutable_(settings, name, cookie) {
110 // If there is a range, set the number of page
111 for (const PageRange& range : settings.ranges())
112 mutable_.expected_page_count_ += range.to - range.from + 1;
114 if (HasDebugDumpPath())
115 DebugDumpSettings(name, settings);
118 PrintedDocument::~PrintedDocument() = default;
121 void PrintedDocument::SetConvertingPdf() {
122 base::AutoLock lock(lock_);
123 mutable_.converting_pdf_ = true;
126 void PrintedDocument::SetPage(int page_number,
127 std::unique_ptr<MetafilePlayer> metafile,
129 const gfx::Size& page_size,
130 const gfx::Rect& page_content_rect) {
131 // Notice the page_number + 1, the reason is that this is the value that will
132 // be shown. Users dislike 0-based counting.
133 auto page = base::MakeRefCounted<PrintedPage>(
134 page_number + 1, std::move(metafile), page_size, page_content_rect);
135 page->set_shrink_factor(shrink);
137 base::AutoLock lock(lock_);
138 mutable_.pages_[page_number] = page;
141 if (HasDebugDumpPath()) {
142 base::PostTaskWithTraits(
143 FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
144 base::BindOnce(&DebugDumpPageTask, name(), base::RetainedRef(page)));
148 scoped_refptr<PrintedPage> PrintedDocument::GetPage(int page_number) {
149 scoped_refptr<PrintedPage> page;
151 base::AutoLock lock(lock_);
152 PrintedPages::const_iterator it = mutable_.pages_.find(page_number);
153 if (it != mutable_.pages_.end())
160 void PrintedDocument::SetDocument(std::unique_ptr<MetafilePlayer> metafile,
161 const gfx::Size& page_size,
162 const gfx::Rect& page_content_rect) {
164 base::AutoLock lock(lock_);
165 mutable_.metafile_ = std::move(metafile);
166 #if defined(OS_MACOSX)
167 mutable_.page_size_ = page_size;
168 mutable_.page_content_rect_ = page_content_rect;
172 if (HasDebugDumpPath()) {
173 base::PostTaskWithTraits(
174 FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
175 base::BindOnce(&DebugDumpTask, name(), mutable_.metafile_.get()));
179 const MetafilePlayer* PrintedDocument::GetMetafile() {
180 return mutable_.metafile_.get();
185 bool PrintedDocument::IsComplete() const {
186 base::AutoLock lock(lock_);
187 if (!mutable_.page_count_)
190 if (mutable_.converting_pdf_)
193 PageNumber page(immutable_.settings_, mutable_.page_count_);
194 if (page == PageNumber::npos())
197 for (; page != PageNumber::npos(); ++page) {
198 PrintedPages::const_iterator it = mutable_.pages_.find(page.ToInt());
199 if (it == mutable_.pages_.end() || !it->second.get() ||
200 !it->second->metafile()) {
206 return !!mutable_.metafile_;
210 void PrintedDocument::set_page_count(int max_page) {
211 base::AutoLock lock(lock_);
212 DCHECK_EQ(0, mutable_.page_count_);
213 mutable_.page_count_ = max_page;
214 if (immutable_.settings_.ranges().empty()) {
215 mutable_.expected_page_count_ = max_page;
217 // If there is a range, don't bother since expected_page_count_ is already
219 DCHECK_NE(mutable_.expected_page_count_, 0);
223 int PrintedDocument::page_count() const {
224 base::AutoLock lock(lock_);
225 return mutable_.page_count_;
228 int PrintedDocument::expected_page_count() const {
229 base::AutoLock lock(lock_);
230 return mutable_.expected_page_count_;
234 void PrintedDocument::SetDebugDumpPath(const base::FilePath& debug_dump_path) {
235 DCHECK(!debug_dump_path.empty());
236 g_debug_dump_info.Get() = debug_dump_path;
240 bool PrintedDocument::HasDebugDumpPath() {
241 return g_debug_dump_info.IsCreated();
245 base::FilePath PrintedDocument::CreateDebugDumpPath(
246 const base::string16& document_name,
247 const base::FilePath::StringType& extension) {
248 DCHECK(HasDebugDumpPath());
250 // Create a filename.
251 base::string16 filename;
252 base::Time now(base::Time::Now());
253 filename = base::TimeFormatShortDateAndTime(now);
254 filename += base::ASCIIToUTF16("_");
255 filename += document_name;
256 base::FilePath::StringType system_filename;
258 system_filename = filename;
260 system_filename = base::UTF16ToUTF8(filename);
262 base::i18n::ReplaceIllegalCharactersInPath(&system_filename, '_');
263 const auto& dump_path = g_debug_dump_info.Get();
264 DCHECK(!dump_path.empty());
265 return dump_path.Append(system_filename).AddExtension(extension);
268 void PrintedDocument::DebugDumpData(
269 const base::RefCountedMemory* data,
270 const base::FilePath::StringType& extension) {
271 DCHECK(HasDebugDumpPath());
272 base::PostTaskWithTraits(FROM_HERE,
273 {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
274 base::BindOnce(&DebugDumpDataTask, name(), extension,
275 base::RetainedRef(data)));
278 #if defined(OS_WIN) || defined(OS_MACOSX)
279 gfx::Rect PrintedDocument::GetCenteredPageContentRect(
280 const gfx::Size& paper_size,
281 const gfx::Size& page_size,
282 const gfx::Rect& page_content_rect) const {
283 gfx::Rect content_rect = page_content_rect;
284 if (paper_size.width() > page_size.width()) {
285 int diff = paper_size.width() - page_size.width();
286 content_rect.set_x(content_rect.x() + diff / 2);
288 if (paper_size.height() > page_size.height()) {
289 int diff = paper_size.height() - page_size.height();
290 content_rect.set_y(content_rect.y() + diff / 2);
296 PrintedDocument::Mutable::Mutable() = default;
298 PrintedDocument::Mutable::~Mutable() = default;
300 PrintedDocument::Immutable::Immutable(const PrintSettings& settings,
301 const base::string16& name,
303 : settings_(settings), name_(name), cookie_(cookie) {}
305 PrintedDocument::Immutable::~Immutable() = default;
307 } // namespace printing