Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / local_discovery / pwg_raster_converter.cc
blob75c4d5bd13645f491b2546d6ce0d945dc0c7734e
1 // Copyright 2013 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 "chrome/browser/local_discovery/pwg_raster_converter.h"
7 #include <algorithm>
9 #include "base/bind_helpers.h"
10 #include "base/cancelable_callback.h"
11 #include "base/files/file.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/location.h"
15 #include "base/logging.h"
16 #include "base/single_thread_task_runner.h"
17 #include "chrome/common/chrome_utility_messages.h"
18 #include "chrome/common/chrome_utility_printing_messages.h"
19 #include "chrome/grit/generated_resources.h"
20 #include "components/cloud_devices/common/cloud_device_description.h"
21 #include "components/cloud_devices/common/printer_description.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/child_process_data.h"
24 #include "content/public/browser/utility_process_host.h"
25 #include "content/public/browser/utility_process_host_client.h"
26 #include "printing/pdf_render_settings.h"
27 #include "printing/pwg_raster_settings.h"
28 #include "printing/units.h"
29 #include "ui/base/l10n/l10n_util.h"
30 #include "ui/gfx/geometry/rect.h"
31 #include "ui/gfx/geometry/size.h"
33 namespace local_discovery {
35 namespace {
37 using content::BrowserThread;
39 class FileHandlers {
40 public:
41 FileHandlers() {}
43 ~FileHandlers() {
44 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
47 void Init(base::RefCountedMemory* data);
48 bool IsValid();
50 base::FilePath GetPwgPath() const {
51 return temp_dir_.path().AppendASCII("output.pwg");
54 base::FilePath GetPdfPath() const {
55 return temp_dir_.path().AppendASCII("input.pdf");
58 IPC::PlatformFileForTransit GetPdfForProcess(base::ProcessHandle process) {
59 DCHECK(pdf_file_.IsValid());
60 IPC::PlatformFileForTransit transit =
61 IPC::TakeFileHandleForProcess(pdf_file_.Pass(), process);
62 return transit;
65 IPC::PlatformFileForTransit GetPwgForProcess(base::ProcessHandle process) {
66 DCHECK(pwg_file_.IsValid());
67 IPC::PlatformFileForTransit transit =
68 IPC::TakeFileHandleForProcess(pwg_file_.Pass(), process);
69 return transit;
72 private:
73 base::ScopedTempDir temp_dir_;
74 base::File pdf_file_;
75 base::File pwg_file_;
78 void FileHandlers::Init(base::RefCountedMemory* data) {
79 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
81 if (!temp_dir_.CreateUniqueTempDir()) {
82 return;
85 if (static_cast<int>(data->size()) !=
86 base::WriteFile(GetPdfPath(), data->front_as<char>(), data->size())) {
87 return;
90 // Reopen in read only mode.
91 pdf_file_.Initialize(GetPdfPath(),
92 base::File::FLAG_OPEN | base::File::FLAG_READ);
93 pwg_file_.Initialize(GetPwgPath(),
94 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
97 bool FileHandlers::IsValid() {
98 return pdf_file_.IsValid() && pwg_file_.IsValid();
101 // Converts PDF into PWG raster.
102 // Class uses 3 threads: UI, IO and FILE.
103 // Internal workflow is following:
104 // 1. Create instance on the UI thread. (files_, settings_,)
105 // 2. Create file on the FILE thread.
106 // 3. Start utility process and start conversion on the IO thread.
107 // 4. Run result callback on the UI thread.
108 // 5. Instance is destroyed from any thread that has the last reference.
109 // 6. FileHandlers destroyed on the FILE thread.
110 // This step posts |FileHandlers| to be destroyed on the FILE thread.
111 // All these steps work sequentially, so no data should be accessed
112 // simultaneously by several threads.
113 class PwgUtilityProcessHostClient : public content::UtilityProcessHostClient {
114 public:
115 explicit PwgUtilityProcessHostClient(
116 const printing::PdfRenderSettings& settings,
117 const printing::PwgRasterSettings& bitmap_settings);
119 void Convert(base::RefCountedMemory* data,
120 const PWGRasterConverter::ResultCallback& callback);
122 // UtilityProcessHostClient implementation.
123 void OnProcessCrashed(int exit_code) override;
124 bool OnMessageReceived(const IPC::Message& message) override;
126 private:
127 ~PwgUtilityProcessHostClient() override;
129 // Message handlers.
130 void OnProcessStarted();
131 void OnSucceeded();
132 void OnFailed();
134 void RunCallback(bool success);
136 void StartProcessOnIOThread();
138 void RunCallbackOnUIThread(bool success);
139 void OnFilesReadyOnUIThread();
141 scoped_ptr<FileHandlers, BrowserThread::DeleteOnFileThread> files_;
142 printing::PdfRenderSettings settings_;
143 printing::PwgRasterSettings bitmap_settings_;
144 PWGRasterConverter::ResultCallback callback_;
145 base::WeakPtr<content::UtilityProcessHost> utility_process_host_;
147 DISALLOW_COPY_AND_ASSIGN(PwgUtilityProcessHostClient);
150 PwgUtilityProcessHostClient::PwgUtilityProcessHostClient(
151 const printing::PdfRenderSettings& settings,
152 const printing::PwgRasterSettings& bitmap_settings)
153 : settings_(settings), bitmap_settings_(bitmap_settings) {}
155 PwgUtilityProcessHostClient::~PwgUtilityProcessHostClient() {
158 void PwgUtilityProcessHostClient::Convert(
159 base::RefCountedMemory* data,
160 const PWGRasterConverter::ResultCallback& callback) {
161 DCHECK_CURRENTLY_ON(BrowserThread::UI);
162 callback_ = callback;
163 CHECK(!files_);
164 files_.reset(new FileHandlers());
165 BrowserThread::PostTaskAndReply(
166 BrowserThread::FILE, FROM_HERE,
167 base::Bind(&FileHandlers::Init, base::Unretained(files_.get()),
168 make_scoped_refptr(data)),
169 base::Bind(&PwgUtilityProcessHostClient::OnFilesReadyOnUIThread, this));
172 void PwgUtilityProcessHostClient::OnProcessCrashed(int exit_code) {
173 OnFailed();
176 bool PwgUtilityProcessHostClient::OnMessageReceived(
177 const IPC::Message& message) {
178 bool handled = true;
179 IPC_BEGIN_MESSAGE_MAP(PwgUtilityProcessHostClient, message)
180 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted, OnProcessStarted)
181 IPC_MESSAGE_HANDLER(
182 ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Succeeded, OnSucceeded)
183 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed,
184 OnFailed)
185 IPC_MESSAGE_UNHANDLED(handled = false)
186 IPC_END_MESSAGE_MAP()
187 return handled;
190 void PwgUtilityProcessHostClient::OnProcessStarted() {
191 DCHECK_CURRENTLY_ON(BrowserThread::IO);
192 if (!utility_process_host_) {
193 RunCallbackOnUIThread(false);
194 return;
197 base::ProcessHandle process = utility_process_host_->GetData().handle;
198 utility_process_host_->Send(new ChromeUtilityMsg_RenderPDFPagesToPWGRaster(
199 files_->GetPdfForProcess(process),
200 settings_,
201 bitmap_settings_,
202 files_->GetPwgForProcess(process)));
203 utility_process_host_.reset();
206 void PwgUtilityProcessHostClient::OnSucceeded() {
207 DCHECK_CURRENTLY_ON(BrowserThread::IO);
208 RunCallback(true);
211 void PwgUtilityProcessHostClient::OnFailed() {
212 DCHECK_CURRENTLY_ON(BrowserThread::IO);
213 RunCallback(false);
216 void PwgUtilityProcessHostClient::OnFilesReadyOnUIThread() {
217 DCHECK_CURRENTLY_ON(BrowserThread::UI);
218 if (!files_->IsValid()) {
219 RunCallbackOnUIThread(false);
220 return;
222 BrowserThread::PostTask(
223 BrowserThread::IO, FROM_HERE,
224 base::Bind(&PwgUtilityProcessHostClient::StartProcessOnIOThread, this));
227 void PwgUtilityProcessHostClient::StartProcessOnIOThread() {
228 DCHECK_CURRENTLY_ON(BrowserThread::IO);
229 utility_process_host_ =
230 content::UtilityProcessHost::Create(
231 this, base::MessageLoop::current()->task_runner())->AsWeakPtr();
232 utility_process_host_->SetName(l10n_util::GetStringUTF16(
233 IDS_UTILITY_PROCESS_PWG_RASTER_CONVERTOR_NAME));
234 utility_process_host_->Send(new ChromeUtilityMsg_StartupPing);
237 void PwgUtilityProcessHostClient::RunCallback(bool success) {
238 BrowserThread::PostTask(
239 BrowserThread::UI, FROM_HERE,
240 base::Bind(&PwgUtilityProcessHostClient::RunCallbackOnUIThread, this,
241 success));
244 void PwgUtilityProcessHostClient::RunCallbackOnUIThread(bool success) {
245 DCHECK_CURRENTLY_ON(BrowserThread::UI);
246 if (!callback_.is_null()) {
247 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
248 base::Bind(callback_, success,
249 files_->GetPwgPath()));
250 callback_.Reset();
254 class PWGRasterConverterImpl : public PWGRasterConverter {
255 public:
256 PWGRasterConverterImpl();
258 ~PWGRasterConverterImpl() override;
260 void Start(base::RefCountedMemory* data,
261 const printing::PdfRenderSettings& conversion_settings,
262 const printing::PwgRasterSettings& bitmap_settings,
263 const ResultCallback& callback) override;
265 private:
266 scoped_refptr<PwgUtilityProcessHostClient> utility_client_;
267 base::CancelableCallback<ResultCallback::RunType> callback_;
269 DISALLOW_COPY_AND_ASSIGN(PWGRasterConverterImpl);
272 PWGRasterConverterImpl::PWGRasterConverterImpl() {
275 PWGRasterConverterImpl::~PWGRasterConverterImpl() {
278 void PWGRasterConverterImpl::Start(
279 base::RefCountedMemory* data,
280 const printing::PdfRenderSettings& conversion_settings,
281 const printing::PwgRasterSettings& bitmap_settings,
282 const ResultCallback& callback) {
283 // Rebind cancelable callback to avoid calling callback if
284 // PWGRasterConverterImpl is destroyed.
285 callback_.Reset(callback);
286 utility_client_ =
287 new PwgUtilityProcessHostClient(conversion_settings, bitmap_settings);
288 utility_client_->Convert(data, callback_.callback());
291 } // namespace
293 // static
294 scoped_ptr<PWGRasterConverter> PWGRasterConverter::CreateDefault() {
295 return scoped_ptr<PWGRasterConverter>(new PWGRasterConverterImpl());
298 // static
299 printing::PdfRenderSettings PWGRasterConverter::GetConversionSettings(
300 const cloud_devices::CloudDeviceDescription& printer_capabilities,
301 const gfx::Size& page_size) {
302 int dpi = printing::kDefaultPdfDpi;
303 cloud_devices::printer::DpiCapability dpis;
304 if (dpis.LoadFrom(printer_capabilities))
305 dpi = std::max(dpis.GetDefault().horizontal, dpis.GetDefault().vertical);
307 double scale = dpi;
308 scale /= printing::kPointsPerInch;
310 // Make vertical rectangle to optimize streaming to printer. Fix orientation
311 // by autorotate.
312 gfx::Rect area(std::min(page_size.width(), page_size.height()) * scale,
313 std::max(page_size.width(), page_size.height()) * scale);
314 return printing::PdfRenderSettings(area, dpi, true /* autorotate */);
317 // static
318 printing::PwgRasterSettings PWGRasterConverter::GetBitmapSettings(
319 const cloud_devices::CloudDeviceDescription& printer_capabilities,
320 const cloud_devices::CloudDeviceDescription& ticket) {
321 printing::PwgRasterSettings result;
322 cloud_devices::printer::PwgRasterConfigCapability raster_capability;
323 // If the raster capability fails to load, raster_capability will contain
324 // the default value.
325 raster_capability.LoadFrom(printer_capabilities);
327 cloud_devices::printer::DuplexTicketItem duplex_item;
328 cloud_devices::printer::DuplexType duplex_value =
329 cloud_devices::printer::NO_DUPLEX;
331 cloud_devices::printer::DocumentSheetBack document_sheet_back =
332 raster_capability.value().document_sheet_back;
334 if (duplex_item.LoadFrom(ticket)) {
335 duplex_value = duplex_item.value();
338 result.odd_page_transform = printing::TRANSFORM_NORMAL;
339 switch (duplex_value) {
340 case cloud_devices::printer::NO_DUPLEX:
341 result.odd_page_transform = printing::TRANSFORM_NORMAL;
342 break;
343 case cloud_devices::printer::LONG_EDGE:
344 if (document_sheet_back == cloud_devices::printer::ROTATED) {
345 result.odd_page_transform = printing::TRANSFORM_ROTATE_180;
346 } else if (document_sheet_back == cloud_devices::printer::FLIPPED) {
347 result.odd_page_transform = printing::TRANSFORM_FLIP_VERTICAL;
349 break;
350 case cloud_devices::printer::SHORT_EDGE:
351 if (document_sheet_back == cloud_devices::printer::MANUAL_TUMBLE) {
352 result.odd_page_transform = printing::TRANSFORM_ROTATE_180;
353 } else if (document_sheet_back == cloud_devices::printer::FLIPPED) {
354 result.odd_page_transform = printing::TRANSFORM_FLIP_HORIZONTAL;
358 result.rotate_all_pages = raster_capability.value().rotate_all_pages;
360 result.reverse_page_order = raster_capability.value().reverse_order_streaming;
361 return result;
364 } // namespace local_discovery