1 // Copyright 2011 The Chromium Authors
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/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/functional/bind.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/thread_pool.h"
26 #include "base/time/time.h"
27 #include "base/values.h"
28 #include "build/build_config.h"
29 #include "printing/metafile.h"
30 #include "printing/page_number.h"
31 #include "printing/print_settings_conversion.h"
32 #include "printing/printing_context.h"
33 #include "printing/units.h"
34 #include "ui/gfx/font.h"
35 #include "ui/gfx/text_elider.h"
38 #include "printing/printed_page_win.h"
45 base::LazyInstance<base::FilePath>::Leaky g_debug_dump_info =
46 LAZY_INSTANCE_INITIALIZER;
49 void DebugDumpPageTask(const std::u16string& doc_name,
50 const PrintedPage* page) {
51 DCHECK(PrintedDocument::HasDebugDumpPath());
53 static constexpr base::FilePath::CharType kExtension[] =
54 FILE_PATH_LITERAL(".emf");
56 std::u16string name = doc_name;
57 name += base::ASCIIToUTF16(base::StringPrintf("_%04d", page->page_number()));
58 base::FilePath path = PrintedDocument::CreateDebugDumpPath(name, kExtension);
60 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
61 page->metafile()->SaveTo(&file);
63 #endif // BUILDFLAG(IS_WIN)
65 void DebugDumpTask(const std::u16string& doc_name,
66 const MetafilePlayer* metafile) {
67 DCHECK(PrintedDocument::HasDebugDumpPath());
69 static constexpr base::FilePath::CharType kExtension[] =
70 FILE_PATH_LITERAL(".pdf");
72 std::u16string name = doc_name;
73 base::FilePath path = PrintedDocument::CreateDebugDumpPath(name, kExtension);
75 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
76 #if BUILDFLAG(IS_ANDROID)
77 metafile->SaveToFileDescriptor(file.GetPlatformFile());
79 metafile->SaveTo(&file);
80 #endif // BUILDFLAG(IS_ANDROID)
83 void DebugDumpDataTask(const std::u16string& doc_name,
84 const base::FilePath::StringType& extension,
85 const base::RefCountedMemory* data) {
87 PrintedDocument::CreateDebugDumpPath(doc_name, extension);
90 base::WriteFile(path, *data);
93 void DebugDumpSettings(const std::u16string& doc_name,
94 const PrintSettings& settings) {
95 base::Value job_settings(PrintSettingsToJobSettingsDebug(settings));
96 std::string settings_str;
97 base::JSONWriter::WriteWithOptions(
98 job_settings, base::JSONWriter::OPTIONS_PRETTY_PRINT, &settings_str);
99 scoped_refptr<base::RefCountedMemory> data =
100 base::MakeRefCounted<base::RefCountedString>(std::move(settings_str));
101 base::ThreadPool::PostTask(
102 FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
103 base::BindOnce(&DebugDumpDataTask, doc_name, FILE_PATH_LITERAL(".json"),
104 base::RetainedRef(data)));
109 PrintedDocument::PrintedDocument(std::unique_ptr<PrintSettings> settings,
110 const std::u16string& name,
112 : immutable_(std::move(settings), name, cookie) {
113 // If there is a range, set the number of page
114 for (const PageRange& range : immutable_.settings_->ranges())
115 mutable_.expected_page_count_ += range.to - range.from + 1;
117 if (HasDebugDumpPath())
118 DebugDumpSettings(name, *immutable_.settings_);
121 PrintedDocument::~PrintedDocument() = default;
123 #if BUILDFLAG(IS_WIN)
124 void PrintedDocument::SetConvertingPdf() {
125 base::AutoLock lock(lock_);
126 mutable_.converting_pdf_ = true;
129 void PrintedDocument::SetPage(uint32_t page_number,
130 std::unique_ptr<MetafilePlayer> metafile,
132 const gfx::Size& page_size,
133 const gfx::Rect& page_content_rect) {
134 // Notice the page_number + 1, the reason is that this is the value that will
135 // be shown. Users dislike 0-based counting.
136 auto page = base::MakeRefCounted<PrintedPage>(
137 page_number + 1, std::move(metafile), page_size, page_content_rect);
138 page->set_shrink_factor(shrink);
140 base::AutoLock lock(lock_);
141 mutable_.pages_[page_number] = page;
144 if (HasDebugDumpPath()) {
145 base::ThreadPool::PostTask(
146 FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
147 base::BindOnce(&DebugDumpPageTask, name(), base::RetainedRef(page)));
151 scoped_refptr<PrintedPage> PrintedDocument::GetPage(uint32_t page_number) {
152 scoped_refptr<PrintedPage> page;
154 base::AutoLock lock(lock_);
155 PrintedPages::const_iterator it = mutable_.pages_.find(page_number);
156 if (it != mutable_.pages_.end())
162 void PrintedDocument::RemovePage(const PrintedPage* page) {
163 base::AutoLock lock(lock_);
164 PrintedPages::const_iterator it =
165 mutable_.pages_.find(page->page_number() - 1);
166 DCHECK_EQ(page, it->second.get());
167 mutable_.pages_.erase(it);
169 #endif // BUILDFLAG(IS_WIN)
171 void PrintedDocument::SetDocument(std::unique_ptr<MetafilePlayer> metafile) {
173 base::AutoLock lock(lock_);
174 mutable_.metafile_ = std::move(metafile);
177 if (HasDebugDumpPath()) {
178 base::ThreadPool::PostTask(
179 FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
180 base::BindOnce(&DebugDumpTask, name(), mutable_.metafile_.get()));
184 const MetafilePlayer* PrintedDocument::GetMetafile() {
185 return mutable_.metafile_.get();
188 mojom::ResultCode PrintedDocument::RenderPrintedDocument(
189 PrintingContext* context) {
190 base::AutoLock lock(lock_);
191 mojom::ResultCode result = context->PrintDocument(
192 *GetMetafile(), *immutable_.settings_, mutable_.expected_page_count_);
193 if (result != mojom::ResultCode::kSuccess)
196 // Beware of any asynchronous aborts of the print job that happened during
198 if (context->PrintingAborted())
199 return mojom::ResultCode::kCanceled;
201 return mojom::ResultCode::kSuccess;
204 bool PrintedDocument::IsComplete() const {
205 base::AutoLock lock(lock_);
206 if (!mutable_.page_count_)
208 #if BUILDFLAG(IS_WIN)
209 if (mutable_.converting_pdf_)
212 PageNumber page(immutable_.settings_->ranges(), mutable_.page_count_);
213 if (page == PageNumber::npos())
216 for (; page != PageNumber::npos(); ++page) {
217 PrintedPages::const_iterator it = mutable_.pages_.find(page.ToUint());
218 if (it == mutable_.pages_.end() || !it->second.get() ||
219 !it->second->metafile()) {
225 return !!mutable_.metafile_;
229 void PrintedDocument::set_page_count(uint32_t max_page) {
230 base::AutoLock lock(lock_);
231 DCHECK_EQ(0u, mutable_.page_count_);
232 mutable_.page_count_ = max_page;
233 if (immutable_.settings_->ranges().empty()) {
234 mutable_.expected_page_count_ = max_page;
236 // If there is a range, don't bother since expected_page_count_ is already
238 DCHECK_NE(mutable_.expected_page_count_, 0u);
242 uint32_t PrintedDocument::page_count() const {
243 base::AutoLock lock(lock_);
244 return mutable_.page_count_;
247 uint32_t PrintedDocument::expected_page_count() const {
248 base::AutoLock lock(lock_);
249 return mutable_.expected_page_count_;
253 void PrintedDocument::SetDebugDumpPath(const base::FilePath& debug_dump_path) {
254 DCHECK(!debug_dump_path.empty());
255 g_debug_dump_info.Get() = debug_dump_path;
259 bool PrintedDocument::HasDebugDumpPath() {
260 return g_debug_dump_info.IsCreated();
264 base::FilePath PrintedDocument::CreateDebugDumpPath(
265 const std::u16string& document_name,
266 const base::FilePath::StringType& extension) {
267 DCHECK(HasDebugDumpPath());
269 // Create a filename.
270 std::u16string filename;
271 base::Time now(base::Time::Now());
272 filename = base::TimeFormatShortDateAndTime(now);
274 filename += document_name;
275 base::FilePath::StringType system_filename;
276 #if BUILDFLAG(IS_WIN)
277 system_filename = base::UTF16ToWide(filename);
278 #else // BUILDFLAG(IS_WIN)
279 system_filename = base::UTF16ToUTF8(filename);
280 #endif // BUILDFLAG(IS_WIN)
281 base::i18n::ReplaceIllegalCharactersInPath(&system_filename, '_');
282 const auto& dump_path = g_debug_dump_info.Get();
283 DCHECK(!dump_path.empty());
284 return dump_path.Append(system_filename).AddExtension(extension);
287 void PrintedDocument::DebugDumpData(
288 const base::RefCountedMemory* data,
289 const base::FilePath::StringType& extension) {
290 DCHECK(HasDebugDumpPath());
291 base::ThreadPool::PostTask(
292 FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
293 base::BindOnce(&DebugDumpDataTask, name(), extension,
294 base::RetainedRef(data)));
297 PrintedDocument::Mutable::Mutable() = default;
299 PrintedDocument::Mutable::~Mutable() = default;
301 PrintedDocument::Immutable::Immutable(std::unique_ptr<PrintSettings> settings,
302 const std::u16string& name,
304 : settings_(std::move(settings)), name_(name), cookie_(cookie) {}
306 PrintedDocument::Immutable::~Immutable() = default;
308 } // namespace printing