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"
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
{
37 using content::BrowserThread
;
44 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
47 void Init(base::RefCountedMemory
* data
);
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
);
65 IPC::PlatformFileForTransit
GetPwgForProcess(base::ProcessHandle process
) {
66 DCHECK(pwg_file_
.IsValid());
67 IPC::PlatformFileForTransit transit
=
68 IPC::TakeFileHandleForProcess(pwg_file_
.Pass(), process
);
73 base::ScopedTempDir temp_dir_
;
78 void FileHandlers::Init(base::RefCountedMemory
* data
) {
79 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
81 if (!temp_dir_
.CreateUniqueTempDir()) {
85 if (static_cast<int>(data
->size()) !=
86 base::WriteFile(GetPdfPath(), data
->front_as
<char>(), data
->size())) {
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
{
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
;
127 ~PwgUtilityProcessHostClient() override
;
130 void OnProcessStarted();
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
;
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
) {
176 bool PwgUtilityProcessHostClient::OnMessageReceived(
177 const IPC::Message
& message
) {
179 IPC_BEGIN_MESSAGE_MAP(PwgUtilityProcessHostClient
, message
)
180 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted
, OnProcessStarted
)
182 ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Succeeded
, OnSucceeded
)
183 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed
,
185 IPC_MESSAGE_UNHANDLED(handled
= false)
186 IPC_END_MESSAGE_MAP()
190 void PwgUtilityProcessHostClient::OnProcessStarted() {
191 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
192 if (!utility_process_host_
) {
193 RunCallbackOnUIThread(false);
197 base::ProcessHandle process
= utility_process_host_
->GetData().handle
;
198 utility_process_host_
->Send(new ChromeUtilityMsg_RenderPDFPagesToPWGRaster(
199 files_
->GetPdfForProcess(process
),
202 files_
->GetPwgForProcess(process
)));
203 utility_process_host_
.reset();
206 void PwgUtilityProcessHostClient::OnSucceeded() {
207 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
211 void PwgUtilityProcessHostClient::OnFailed() {
212 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
216 void PwgUtilityProcessHostClient::OnFilesReadyOnUIThread() {
217 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
218 if (!files_
->IsValid()) {
219 RunCallbackOnUIThread(false);
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,
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()));
254 class PWGRasterConverterImpl
: public PWGRasterConverter
{
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
;
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
);
287 new PwgUtilityProcessHostClient(conversion_settings
, bitmap_settings
);
288 utility_client_
->Convert(data
, callback_
.callback());
294 scoped_ptr
<PWGRasterConverter
> PWGRasterConverter::CreateDefault() {
295 return scoped_ptr
<PWGRasterConverter
>(new PWGRasterConverterImpl());
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
);
308 scale
/= printing::kPointsPerInch
;
310 // Make vertical rectangle to optimize streaming to printer. Fix orientation
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 */);
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
;
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
;
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
;
364 } // namespace local_discovery