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.
4
5#include "printing/printed_document.h"
6
7#include <algorithm>
8#include <set>
9#include <string>
10#include <vector>
11
12#include "base/bind.h"
13#include "base/files/file_path.h"
14#include "base/files/file_util.h"
15#include "base/i18n/file_util_icu.h"
16#include "base/i18n/time_formatting.h"
17#include "base/json/json_writer.h"
18#include "base/lazy_instance.h"
19#include "base/memory/ref_counted_memory.h"
20#include "base/message_loop/message_loop.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/time/time.h"
26#include "base/values.h"
27#include "printing/page_number.h"
28#include "printing/print_settings_conversion.h"
29#include "printing/printed_page.h"
30#include "printing/printed_pages_source.h"
31#include "printing/units.h"
32#include "skia/ext/platform_device.h"
33#include "ui/gfx/font.h"
34#include "ui/gfx/text_elider.h"
35
36namespace printing {
37
38namespace {
39
40base::LazyInstance<base::FilePath> g_debug_dump_info =
41    LAZY_INSTANCE_INITIALIZER;
42
43void DebugDumpPageTask(const base::string16& doc_name,
44                       const PrintedPage* page) {
45  if (g_debug_dump_info.Get().empty())
46    return;
47
48  base::string16 filename = doc_name;
49  filename +=
50      base::ASCIIToUTF16(base::StringPrintf("_%04d", page->page_number()));
51  base::FilePath file_path =
52#if defined(OS_WIN)
53      PrintedDocument::CreateDebugDumpPath(filename, FILE_PATH_LITERAL(".emf"));
54#else   // OS_WIN
55      PrintedDocument::CreateDebugDumpPath(filename, FILE_PATH_LITERAL(".pdf"));
56#endif  // OS_WIN
57  base::File file(file_path,
58                  base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
59  page->metafile()->SaveTo(&file);
60}
61
62void DebugDumpDataTask(const base::string16& doc_name,
63                       const base::FilePath::StringType& extension,
64                       const base::RefCountedMemory* data) {
65  base::FilePath path =
66      PrintedDocument::CreateDebugDumpPath(doc_name, extension);
67  if (path.empty())
68    return;
69  base::WriteFile(path,
70                  reinterpret_cast<const char*>(data->front()),
71                  base::checked_cast<int>(data->size()));
72}
73
74void DebugDumpSettings(const base::string16& doc_name,
75                       const PrintSettings& settings,
76                       base::TaskRunner* blocking_runner) {
77  base::DictionaryValue job_settings;
78  PrintSettingsToJobSettingsDebug(settings, &job_settings);
79  std::string settings_str;
80  base::JSONWriter::WriteWithOptions(
81      &job_settings, base::JSONWriter::OPTIONS_PRETTY_PRINT, &settings_str);
82  scoped_refptr<base::RefCountedMemory> data =
83      base::RefCountedString::TakeString(&settings_str);
84  blocking_runner->PostTask(
85      FROM_HERE,
86      base::Bind(
87          &DebugDumpDataTask, doc_name, FILE_PATH_LITERAL(".json"), data));
88}
89
90}  // namespace
91
92PrintedDocument::PrintedDocument(const PrintSettings& settings,
93                                 PrintedPagesSource* source,
94                                 int cookie,
95                                 base::TaskRunner* blocking_runner)
96    : mutable_(source), immutable_(settings, source, cookie, blocking_runner) {
97  // Records the expected page count if a range is setup.
98  if (!settings.ranges().empty()) {
99    // If there is a range, set the number of page
100    for (unsigned i = 0; i < settings.ranges().size(); ++i) {
101      const PageRange& range = settings.ranges()[i];
102      mutable_.expected_page_count_ += range.to - range.from + 1;
103    }
104  }
105
106  if (!g_debug_dump_info.Get().empty())
107    DebugDumpSettings(name(), settings, blocking_runner);
108}
109
110PrintedDocument::~PrintedDocument() {
111}
112
113void PrintedDocument::SetPage(int page_number,
114                              scoped_ptr<MetafilePlayer> metafile,
115#if defined(OS_WIN)
116                              double shrink,
117#endif  // OS_WIN
118                              const gfx::Size& paper_size,
119                              const gfx::Rect& page_rect) {
120  // Notice the page_number + 1, the reason is that this is the value that will
121  // be shown. Users dislike 0-based counting.
122  scoped_refptr<PrintedPage> page(
123      new PrintedPage(page_number + 1, metafile.Pass(), paper_size, page_rect));
124#if defined(OS_WIN)
125  page->set_shrink_factor(shrink);
126#endif  // OS_WIN
127  {
128    base::AutoLock lock(lock_);
129    mutable_.pages_[page_number] = page;
130
131#if defined(OS_POSIX) && !defined(OS_MACOSX)
132    if (page_number < mutable_.first_page)
133      mutable_.first_page = page_number;
134#endif
135  }
136
137  if (!g_debug_dump_info.Get().empty()) {
138    immutable_.blocking_runner_->PostTask(
139        FROM_HERE, base::Bind(&DebugDumpPageTask, name(), page));
140  }
141}
142
143scoped_refptr<PrintedPage> PrintedDocument::GetPage(int page_number) {
144  scoped_refptr<PrintedPage> page;
145  {
146    base::AutoLock lock(lock_);
147    PrintedPages::const_iterator itr = mutable_.pages_.find(page_number);
148    if (itr != mutable_.pages_.end())
149      page = itr->second;
150  }
151  return page;
152}
153
154bool PrintedDocument::IsComplete() const {
155  base::AutoLock lock(lock_);
156  if (!mutable_.page_count_)
157    return false;
158  PageNumber page(immutable_.settings_, mutable_.page_count_);
159  if (page == PageNumber::npos())
160    return false;
161
162  for (; page != PageNumber::npos(); ++page) {
163#if defined(OS_WIN) || defined(OS_MACOSX)
164    const bool metafile_must_be_valid = true;
165#elif defined(OS_POSIX)
166    const bool metafile_must_be_valid = (page.ToInt() == mutable_.first_page);
167#endif
168    PrintedPages::const_iterator itr = mutable_.pages_.find(page.ToInt());
169    if (itr == mutable_.pages_.end() || !itr->second.get())
170      return false;
171    if (metafile_must_be_valid && !itr->second->metafile())
172      return false;
173  }
174  return true;
175}
176
177void PrintedDocument::DisconnectSource() {
178  base::AutoLock lock(lock_);
179  mutable_.source_ = NULL;
180}
181
182void PrintedDocument::set_page_count(int max_page) {
183  base::AutoLock lock(lock_);
184  DCHECK_EQ(0, mutable_.page_count_);
185  mutable_.page_count_ = max_page;
186  if (immutable_.settings_.ranges().empty()) {
187    mutable_.expected_page_count_ = max_page;
188  } else {
189    // If there is a range, don't bother since expected_page_count_ is already
190    // initialized.
191    DCHECK_NE(mutable_.expected_page_count_, 0);
192  }
193}
194
195int PrintedDocument::page_count() const {
196  base::AutoLock lock(lock_);
197  return mutable_.page_count_;
198}
199
200int PrintedDocument::expected_page_count() const {
201  base::AutoLock lock(lock_);
202  return mutable_.expected_page_count_;
203}
204
205void PrintedDocument::set_debug_dump_path(
206    const base::FilePath& debug_dump_path) {
207  g_debug_dump_info.Get() = debug_dump_path;
208}
209
210base::FilePath PrintedDocument::CreateDebugDumpPath(
211    const base::string16& document_name,
212    const base::FilePath::StringType& extension) {
213  if (g_debug_dump_info.Get().empty())
214    return base::FilePath();
215  // Create a filename.
216  base::string16 filename;
217  base::Time now(base::Time::Now());
218  filename = base::TimeFormatShortDateAndTime(now);
219  filename += base::ASCIIToUTF16("_");
220  filename += document_name;
221  base::FilePath::StringType system_filename;
222#if defined(OS_WIN)
223  system_filename = filename;
224#else   // OS_WIN
225  system_filename = base::UTF16ToUTF8(filename);
226#endif  // OS_WIN
227  base::i18n::ReplaceIllegalCharactersInPath(&system_filename, '_');
228  return g_debug_dump_info.Get().Append(system_filename).AddExtension(
229      extension);
230}
231
232void PrintedDocument::DebugDumpData(
233    const base::RefCountedMemory* data,
234    const base::FilePath::StringType& extension) {
235  if (g_debug_dump_info.Get().empty())
236    return;
237  immutable_.blocking_runner_->PostTask(
238      FROM_HERE, base::Bind(&DebugDumpDataTask, name(), extension, data));
239}
240
241PrintedDocument::Mutable::Mutable(PrintedPagesSource* source)
242    : source_(source),
243      expected_page_count_(0),
244      page_count_(0) {
245#if defined(OS_POSIX) && !defined(OS_MACOSX)
246  first_page = INT_MAX;
247#endif
248}
249
250PrintedDocument::Mutable::~Mutable() {
251}
252
253PrintedDocument::Immutable::Immutable(const PrintSettings& settings,
254                                      PrintedPagesSource* source,
255                                      int cookie,
256                                      base::TaskRunner* blocking_runner)
257    : settings_(settings),
258      name_(source->RenderSourceName()),
259      cookie_(cookie),
260      blocking_runner_(blocking_runner) {
261}
262
263PrintedDocument::Immutable::~Immutable() {
264}
265
266#if defined(OS_CHROMEOS) || defined(OS_ANDROID)
267// This function is not used on aura linux/chromeos or android.
268void PrintedDocument::RenderPrintedPage(const PrintedPage& page,
269                                        PrintingContext* context) const {
270}
271#endif
272
273}  // namespace printing
274