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/logging.h"
15 #include "chrome/common/chrome_utility_messages.h"
16 #include "chrome/common/chrome_utility_printing_messages.h"
17 #include "components/cloud_devices/common/cloud_device_description.h"
18 #include "components/cloud_devices/common/printer_description.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/browser/child_process_data.h"
21 #include "content/public/browser/utility_process_host.h"
22 #include "content/public/browser/utility_process_host_client.h"
23 #include "printing/pdf_render_settings.h"
24 #include "printing/pwg_raster_settings.h"
25 #include "printing/units.h"
26 #include "ui/gfx/geometry/rect.h"
27 #include "ui/gfx/geometry/size.h"
29 namespace local_discovery
{
33 using content::BrowserThread
;
40 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
43 void Init(base::RefCountedMemory
* data
);
46 base::FilePath
GetPwgPath() const {
47 return temp_dir_
.path().AppendASCII("output.pwg");
50 base::FilePath
GetPdfPath() const {
51 return temp_dir_
.path().AppendASCII("input.pdf");
54 IPC::PlatformFileForTransit
GetPdfForProcess(base::ProcessHandle process
) {
55 DCHECK(pdf_file_
.IsValid());
56 IPC::PlatformFileForTransit transit
=
57 IPC::TakeFileHandleForProcess(pdf_file_
.Pass(), process
);
61 IPC::PlatformFileForTransit
GetPwgForProcess(base::ProcessHandle process
) {
62 DCHECK(pwg_file_
.IsValid());
63 IPC::PlatformFileForTransit transit
=
64 IPC::TakeFileHandleForProcess(pwg_file_
.Pass(), process
);
69 base::ScopedTempDir temp_dir_
;
74 void FileHandlers::Init(base::RefCountedMemory
* data
) {
75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
77 if (!temp_dir_
.CreateUniqueTempDir()) {
81 if (static_cast<int>(data
->size()) !=
82 base::WriteFile(GetPdfPath(), data
->front_as
<char>(), data
->size())) {
86 // Reopen in read only mode.
87 pdf_file_
.Initialize(GetPdfPath(),
88 base::File::FLAG_OPEN
| base::File::FLAG_READ
);
89 pwg_file_
.Initialize(GetPwgPath(),
90 base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
);
93 bool FileHandlers::IsValid() {
94 return pdf_file_
.IsValid() && pwg_file_
.IsValid();
97 // Converts PDF into PWG raster.
98 // Class uses 3 threads: UI, IO and FILE.
99 // Internal workflow is following:
100 // 1. Create instance on the UI thread. (files_, settings_,)
101 // 2. Create file on the FILE thread.
102 // 3. Start utility process and start conversion on the IO thread.
103 // 4. Run result callback on the UI thread.
104 // 5. Instance is destroyed from any thread that has the last reference.
105 // 6. FileHandlers destroyed on the FILE thread.
106 // This step posts |FileHandlers| to be destroyed on the FILE thread.
107 // All these steps work sequentially, so no data should be accessed
108 // simultaneously by several threads.
109 class PwgUtilityProcessHostClient
: public content::UtilityProcessHostClient
{
111 explicit PwgUtilityProcessHostClient(
112 const printing::PdfRenderSettings
& settings
,
113 const printing::PwgRasterSettings
& bitmap_settings
);
115 void Convert(base::RefCountedMemory
* data
,
116 const PWGRasterConverter::ResultCallback
& callback
);
118 // UtilityProcessHostClient implementation.
119 void OnProcessCrashed(int exit_code
) override
;
120 bool OnMessageReceived(const IPC::Message
& message
) override
;
123 ~PwgUtilityProcessHostClient() override
;
126 void OnProcessStarted();
130 void RunCallback(bool success
);
132 void StartProcessOnIOThread();
134 void RunCallbackOnUIThread(bool success
);
135 void OnFilesReadyOnUIThread();
137 scoped_ptr
<FileHandlers
, BrowserThread::DeleteOnFileThread
> files_
;
138 printing::PdfRenderSettings settings_
;
139 printing::PwgRasterSettings bitmap_settings_
;
140 PWGRasterConverter::ResultCallback callback_
;
141 base::WeakPtr
<content::UtilityProcessHost
> utility_process_host_
;
143 DISALLOW_COPY_AND_ASSIGN(PwgUtilityProcessHostClient
);
146 PwgUtilityProcessHostClient::PwgUtilityProcessHostClient(
147 const printing::PdfRenderSettings
& settings
,
148 const printing::PwgRasterSettings
& bitmap_settings
)
149 : settings_(settings
), bitmap_settings_(bitmap_settings
) {}
151 PwgUtilityProcessHostClient::~PwgUtilityProcessHostClient() {
154 void PwgUtilityProcessHostClient::Convert(
155 base::RefCountedMemory
* data
,
156 const PWGRasterConverter::ResultCallback
& callback
) {
157 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
158 callback_
= callback
;
160 files_
.reset(new FileHandlers());
161 BrowserThread::PostTaskAndReply(
162 BrowserThread::FILE, FROM_HERE
,
163 base::Bind(&FileHandlers::Init
, base::Unretained(files_
.get()),
164 make_scoped_refptr(data
)),
165 base::Bind(&PwgUtilityProcessHostClient::OnFilesReadyOnUIThread
, this));
168 void PwgUtilityProcessHostClient::OnProcessCrashed(int exit_code
) {
172 bool PwgUtilityProcessHostClient::OnMessageReceived(
173 const IPC::Message
& message
) {
175 IPC_BEGIN_MESSAGE_MAP(PwgUtilityProcessHostClient
, message
)
176 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_ProcessStarted
, OnProcessStarted
)
178 ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Succeeded
, OnSucceeded
)
179 IPC_MESSAGE_HANDLER(ChromeUtilityHostMsg_RenderPDFPagesToPWGRaster_Failed
,
181 IPC_MESSAGE_UNHANDLED(handled
= false)
182 IPC_END_MESSAGE_MAP()
186 void PwgUtilityProcessHostClient::OnProcessStarted() {
187 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
188 if (!utility_process_host_
) {
189 RunCallbackOnUIThread(false);
193 base::ProcessHandle process
= utility_process_host_
->GetData().handle
;
194 utility_process_host_
->Send(new ChromeUtilityMsg_RenderPDFPagesToPWGRaster(
195 files_
->GetPdfForProcess(process
),
198 files_
->GetPwgForProcess(process
)));
199 utility_process_host_
.reset();
202 void PwgUtilityProcessHostClient::OnSucceeded() {
203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
207 void PwgUtilityProcessHostClient::OnFailed() {
208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
212 void PwgUtilityProcessHostClient::OnFilesReadyOnUIThread() {
213 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
214 if (!files_
->IsValid()) {
215 RunCallbackOnUIThread(false);
218 BrowserThread::PostTask(
219 BrowserThread::IO
, FROM_HERE
,
220 base::Bind(&PwgUtilityProcessHostClient::StartProcessOnIOThread
, this));
223 void PwgUtilityProcessHostClient::StartProcessOnIOThread() {
224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
225 utility_process_host_
=
226 content::UtilityProcessHost::Create(
228 base::MessageLoop::current()->message_loop_proxy())->AsWeakPtr();
229 utility_process_host_
->Send(new ChromeUtilityMsg_StartupPing
);
232 void PwgUtilityProcessHostClient::RunCallback(bool success
) {
233 BrowserThread::PostTask(
234 BrowserThread::UI
, FROM_HERE
,
235 base::Bind(&PwgUtilityProcessHostClient::RunCallbackOnUIThread
, this,
239 void PwgUtilityProcessHostClient::RunCallbackOnUIThread(bool success
) {
240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
241 if (!callback_
.is_null()) {
242 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
243 base::Bind(callback_
, success
,
244 files_
->GetPwgPath()));
249 class PWGRasterConverterImpl
: public PWGRasterConverter
{
251 PWGRasterConverterImpl();
253 ~PWGRasterConverterImpl() override
;
255 void Start(base::RefCountedMemory
* data
,
256 const printing::PdfRenderSettings
& conversion_settings
,
257 const printing::PwgRasterSettings
& bitmap_settings
,
258 const ResultCallback
& callback
) override
;
261 scoped_refptr
<PwgUtilityProcessHostClient
> utility_client_
;
262 base::CancelableCallback
<ResultCallback::RunType
> callback_
;
264 DISALLOW_COPY_AND_ASSIGN(PWGRasterConverterImpl
);
267 PWGRasterConverterImpl::PWGRasterConverterImpl() {
270 PWGRasterConverterImpl::~PWGRasterConverterImpl() {
273 void PWGRasterConverterImpl::Start(
274 base::RefCountedMemory
* data
,
275 const printing::PdfRenderSettings
& conversion_settings
,
276 const printing::PwgRasterSettings
& bitmap_settings
,
277 const ResultCallback
& callback
) {
278 // Rebind cancelable callback to avoid calling callback if
279 // PWGRasterConverterImpl is destroyed.
280 callback_
.Reset(callback
);
282 new PwgUtilityProcessHostClient(conversion_settings
, bitmap_settings
);
283 utility_client_
->Convert(data
, callback_
.callback());
289 scoped_ptr
<PWGRasterConverter
> PWGRasterConverter::CreateDefault() {
290 return scoped_ptr
<PWGRasterConverter
>(new PWGRasterConverterImpl());
294 printing::PdfRenderSettings
PWGRasterConverter::GetConversionSettings(
295 const cloud_devices::CloudDeviceDescription
& printer_capabilities
,
296 const gfx::Size
& page_size
) {
297 int dpi
= printing::kDefaultPdfDpi
;
298 cloud_devices::printer::DpiCapability dpis
;
299 if (dpis
.LoadFrom(printer_capabilities
))
300 dpi
= std::max(dpis
.GetDefault().horizontal
, dpis
.GetDefault().vertical
);
303 scale
/= printing::kPointsPerInch
;
305 // Make vertical rectangle to optimize streaming to printer. Fix orientation
307 gfx::Rect
area(std::min(page_size
.width(), page_size
.height()) * scale
,
308 std::max(page_size
.width(), page_size
.height()) * scale
);
309 return printing::PdfRenderSettings(area
, dpi
, true /* autorotate */);
313 printing::PwgRasterSettings
PWGRasterConverter::GetBitmapSettings(
314 const cloud_devices::CloudDeviceDescription
& printer_capabilities
,
315 const cloud_devices::CloudDeviceDescription
& ticket
) {
316 printing::PwgRasterSettings result
;
317 cloud_devices::printer::PwgRasterConfigCapability raster_capability
;
318 // If the raster capability fails to load, raster_capability will contain
319 // the default value.
320 raster_capability
.LoadFrom(printer_capabilities
);
322 cloud_devices::printer::DuplexTicketItem duplex_item
;
323 cloud_devices::printer::DuplexType duplex_value
=
324 cloud_devices::printer::NO_DUPLEX
;
326 cloud_devices::printer::DocumentSheetBack document_sheet_back
=
327 raster_capability
.value().document_sheet_back
;
329 if (duplex_item
.LoadFrom(ticket
)) {
330 duplex_value
= duplex_item
.value();
333 result
.odd_page_transform
= printing::TRANSFORM_NORMAL
;
334 switch (duplex_value
) {
335 case cloud_devices::printer::NO_DUPLEX
:
336 result
.odd_page_transform
= printing::TRANSFORM_NORMAL
;
338 case cloud_devices::printer::LONG_EDGE
:
339 if (document_sheet_back
== cloud_devices::printer::ROTATED
) {
340 result
.odd_page_transform
= printing::TRANSFORM_ROTATE_180
;
341 } else if (document_sheet_back
== cloud_devices::printer::FLIPPED
) {
342 result
.odd_page_transform
= printing::TRANSFORM_FLIP_VERTICAL
;
345 case cloud_devices::printer::SHORT_EDGE
:
346 if (document_sheet_back
== cloud_devices::printer::MANUAL_TUMBLE
) {
347 result
.odd_page_transform
= printing::TRANSFORM_ROTATE_180
;
348 } else if (document_sheet_back
== cloud_devices::printer::FLIPPED
) {
349 result
.odd_page_transform
= printing::TRANSFORM_FLIP_HORIZONTAL
;
353 result
.rotate_all_pages
= raster_capability
.value().rotate_all_pages
;
355 result
.reverse_page_order
= raster_capability
.value().reverse_order_streaming
;
359 } // namespace local_discovery