blob: 849e1257a52fca4dd473d2093701e41d906751f3 [file] [log] [blame]
[email protected]89eff96a2011-03-17 23:22:061// Copyright (c) 2011 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
[email protected]0e0fca32009-07-06 15:25:505#include "printing/emf_win.h"
initial.commit09911bf2008-07-26 23:55:296
[email protected]e6cddc572010-09-29 21:39:457#include "base/file_path.h"
initial.commit09911bf2008-07-26 23:55:298#include "base/logging.h"
[email protected]3b63f8f42011-03-28 01:54:159#include "base/memory/scoped_ptr.h"
[email protected]835d7c82010-10-14 04:38:3810#include "base/metrics/histogram.h"
[email protected]38bba4f2010-03-12 05:29:0711#include "base/time.h"
[email protected]d2fdcf02011-03-21 22:16:4312#include "skia/ext/vector_platform_device_win.h"
[email protected]38bba4f2010-03-12 05:29:0713#include "third_party/skia/include/core/SkBitmap.h"
[email protected]08397d52011-02-05 01:53:3814#include "ui/gfx/codec/jpeg_codec.h"
15#include "ui/gfx/codec/png_codec.h"
16#include "ui/gfx/gdi_util.h"
[email protected]edc531f92011-03-18 17:52:2317#include "ui/gfx/point.h"
[email protected]08397d52011-02-05 01:53:3818#include "ui/gfx/rect.h"
[email protected]edc531f92011-03-18 17:52:2319#include "ui/gfx/size.h"
initial.commit09911bf2008-07-26 23:55:2920
[email protected]2aa8e182010-07-12 16:25:0421namespace {
22const int kCustomGdiCommentSignature = 0xdeadbabe;
23struct PageBreakRecord {
24 int signature;
25 enum PageBreakType {
26 START_PAGE,
27 END_PAGE,
28 } type;
29 explicit PageBreakRecord(PageBreakType type_in)
30 : signature(kCustomGdiCommentSignature), type(type_in) {
31 }
32 bool IsValid() const {
33 return (signature == kCustomGdiCommentSignature) &&
34 (type >= START_PAGE) && (type <= END_PAGE);
35 }
36};
37}
38
[email protected]0e0fca32009-07-06 15:25:5039namespace printing {
initial.commit09911bf2008-07-26 23:55:2940
[email protected]38bba4f2010-03-12 05:29:0741bool DIBFormatNativelySupported(HDC dc, uint32 escape, const BYTE* bits,
42 int size) {
43 BOOL supported = FALSE;
44 if (ExtEscape(dc, QUERYESCSUPPORT, sizeof(escape),
45 reinterpret_cast<LPCSTR>(&escape), 0, 0) > 0) {
46 ExtEscape(dc, escape, size, reinterpret_cast<LPCSTR>(bits),
47 sizeof(supported), reinterpret_cast<LPSTR>(&supported));
48 }
49 return !!supported;
50}
51
[email protected]830cf742011-04-01 16:06:2552Emf::Emf() : emf_(NULL), hdc_(NULL), page_count_(0) {
initial.commit09911bf2008-07-26 23:55:2953}
54
55Emf::~Emf() {
[email protected]481edc52011-03-22 06:36:5856 DCHECK(!hdc_);
57 if (emf_)
58 DeleteEnhMetaFile(emf_);
59}
60
61bool Emf::InitToFile(const FilePath& metafile_path) {
initial.commit09911bf2008-07-26 23:55:2962 DCHECK(!emf_ && !hdc_);
[email protected]481edc52011-03-22 06:36:5863 hdc_ = CreateEnhMetaFile(NULL, metafile_path.value().c_str(), NULL, NULL);
64 DCHECK(hdc_);
65 return hdc_ != NULL;
66}
67
68bool Emf::InitFromFile(const FilePath& metafile_path) {
69 DCHECK(!emf_ && !hdc_);
70 emf_ = GetEnhMetaFile(metafile_path.value().c_str());
71 DCHECK(emf_);
72 return emf_ != NULL;
73}
74
75bool Emf::Init() {
76 DCHECK(!emf_ && !hdc_);
77 hdc_ = CreateEnhMetaFile(NULL, NULL, NULL, NULL);
78 DCHECK(hdc_);
79 return hdc_ != NULL;
initial.commit09911bf2008-07-26 23:55:2980}
81
[email protected]89eff96a2011-03-17 23:22:0682bool Emf::InitFromData(const void* src_buffer, uint32 src_buffer_size) {
[email protected]e8980a52010-10-07 20:11:2483 DCHECK(!emf_ && !hdc_);
84 emf_ = SetEnhMetaFileBits(src_buffer_size,
85 reinterpret_cast<const BYTE*>(src_buffer));
[email protected]e8980a52010-10-07 20:11:2486 return emf_ != NULL;
87}
88
[email protected]cdd19f52011-03-19 01:04:5789bool Emf::FinishDocument() {
initial.commit09911bf2008-07-26 23:55:2990 DCHECK(!emf_ && hdc_);
91 emf_ = CloseEnhMetaFile(hdc_);
92 DCHECK(emf_);
93 hdc_ = NULL;
94 return emf_ != NULL;
95}
96
initial.commit09911bf2008-07-26 23:55:2997bool Emf::Playback(HDC hdc, const RECT* rect) const {
98 DCHECK(emf_ && !hdc_);
99 RECT bounds;
100 if (!rect) {
101 // Get the natural bounds of the EMF buffer.
[email protected]8f17cd3e2011-03-16 01:39:42102 bounds = GetPageBounds(1).ToRECT();
initial.commit09911bf2008-07-26 23:55:29103 rect = &bounds;
104 }
105 return PlayEnhMetaFile(hdc, emf_, rect) != 0;
106}
107
108bool Emf::SafePlayback(HDC context) const {
109 DCHECK(emf_ && !hdc_);
110 XFORM base_matrix;
111 if (!GetWorldTransform(context, &base_matrix)) {
112 NOTREACHED();
113 return false;
114 }
initial.commit09911bf2008-07-26 23:55:29115 return EnumEnhMetaFile(context,
116 emf_,
117 &Emf::SafePlaybackProc,
118 reinterpret_cast<void*>(&base_matrix),
[email protected]8f17cd3e2011-03-16 01:39:42119 &GetPageBounds(1).ToRECT()) != 0;
initial.commit09911bf2008-07-26 23:55:29120}
121
[email protected]8f17cd3e2011-03-16 01:39:42122gfx::Rect Emf::GetPageBounds(unsigned int page_number) const {
initial.commit09911bf2008-07-26 23:55:29123 DCHECK(emf_ && !hdc_);
[email protected]8f17cd3e2011-03-16 01:39:42124 DCHECK_EQ(1U, page_number);
initial.commit09911bf2008-07-26 23:55:29125 ENHMETAHEADER header;
126 if (GetEnhMetaFileHeader(emf_, sizeof(header), &header) != sizeof(header)) {
127 NOTREACHED();
128 return gfx::Rect();
129 }
130 if (header.rclBounds.left == 0 &&
131 header.rclBounds.top == 0 &&
132 header.rclBounds.right == -1 &&
133 header.rclBounds.bottom == -1) {
134 // A freshly created EMF buffer that has no drawing operation has invalid
135 // bounds. Instead of having an (0,0) size, it has a (-1,-1) size. Detect
136 // this special case and returns an empty Rect instead of an invalid one.
137 return gfx::Rect();
138 }
139 return gfx::Rect(header.rclBounds.left,
140 header.rclBounds.top,
141 header.rclBounds.right - header.rclBounds.left,
142 header.rclBounds.bottom - header.rclBounds.top);
143}
144
[email protected]b5ab3982010-02-16 23:58:27145uint32 Emf::GetDataSize() const {
initial.commit09911bf2008-07-26 23:55:29146 DCHECK(emf_ && !hdc_);
147 return GetEnhMetaFileBits(emf_, 0, NULL);
148}
149
[email protected]b5ab3982010-02-16 23:58:27150bool Emf::GetData(void* buffer, uint32 size) const {
initial.commit09911bf2008-07-26 23:55:29151 DCHECK(emf_ && !hdc_);
152 DCHECK(buffer && size);
[email protected]b5ab3982010-02-16 23:58:27153 uint32 size2 =
154 GetEnhMetaFileBits(emf_, size, reinterpret_cast<BYTE*>(buffer));
initial.commit09911bf2008-07-26 23:55:29155 DCHECK(size2 == size);
156 return size2 == size && size2 != 0;
157}
158
[email protected]96fddd82011-03-24 23:37:45159bool Emf::GetDataAsVector(std::vector<uint8>* buffer) const {
[email protected]b5ab3982010-02-16 23:58:27160 uint32 size = GetDataSize();
initial.commit09911bf2008-07-26 23:55:29161 if (!size)
162 return false;
163
164 buffer->resize(size);
165 if (!GetData(&buffer->front(), size))
166 return false;
167 return true;
168}
169
[email protected]8f17cd3e2011-03-16 01:39:42170bool Emf::SaveTo(const FilePath& file_path) const {
171 HANDLE file = CreateFile(file_path.value().c_str(), GENERIC_WRITE,
initial.commit09911bf2008-07-26 23:55:29172 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
173 CREATE_ALWAYS, 0, NULL);
174 if (file == INVALID_HANDLE_VALUE)
175 return false;
176
177 bool success = false;
178 std::vector<uint8> buffer;
[email protected]96fddd82011-03-24 23:37:45179 if (GetDataAsVector(&buffer)) {
initial.commit09911bf2008-07-26 23:55:29180 DWORD written = 0;
181 if (WriteFile(file, &*buffer.begin(), static_cast<DWORD>(buffer.size()),
182 &written, NULL) &&
183 written == buffer.size()) {
184 success = true;
185 }
186 }
187 CloseHandle(file);
188 return success;
189}
190
191int CALLBACK Emf::SafePlaybackProc(HDC hdc,
192 HANDLETABLE* handle_table,
193 const ENHMETARECORD* record,
194 int objects_count,
195 LPARAM param) {
196 const XFORM* base_matrix = reinterpret_cast<const XFORM*>(param);
197 EnumerationContext context;
198 context.handle_table = handle_table;
199 context.objects_count = objects_count;
200 context.hdc = hdc;
201 Record record_instance(&context, record);
202 bool success = record_instance.SafePlayback(base_matrix);
203 DCHECK(success);
204 return 1;
205}
206
initial.commit09911bf2008-07-26 23:55:29207Emf::Record::Record(const EnumerationContext* context,
208 const ENHMETARECORD* record)
209 : record_(record),
210 context_(context) {
211 DCHECK(record_);
212}
213
214bool Emf::Record::Play() const {
215 return 0 != PlayEnhMetaFileRecord(context_->hdc,
216 context_->handle_table,
217 record_,
218 context_->objects_count);
219}
220
221bool Emf::Record::SafePlayback(const XFORM* base_matrix) const {
222 // For EMF field description, see [MS-EMF] Enhanced Metafile Format
223 // Specification.
224 //
225 // This is the second major EMF breakage I get; the first one being
226 // SetDCBrushColor/SetDCPenColor/DC_PEN/DC_BRUSH being silently ignored.
227 //
228 // This function is the guts of the fix for bug 1186598. Some printer drivers
229 // somehow choke on certain EMF records, but calling the corresponding
230 // function directly on the printer HDC is fine. Still, playing the EMF record
231 // fails. Go figure.
232 //
233 // The main issue is that SetLayout is totally unsupported on these printers
234 // (HP 4500/4700). I used to call SetLayout and I stopped. I found out this is
235 // not sufficient because GDI32!PlayEnhMetaFile internally calls SetLayout(!)
236 // Damn.
237 //
238 // So I resorted to manually parse the EMF records and play them one by one.
239 // The issue with this method compared to using PlayEnhMetaFile to play back
240 // an EMF buffer is that the later silently fixes the matrix to take in
241 // account the matrix currently loaded at the time of the call.
242 // The matrix magic is done transparently when using PlayEnhMetaFile but since
243 // I'm processing one field at a time, I need to do the fixup myself. Note
244 // that PlayEnhMetaFileRecord doesn't fix the matrix correctly even when
245 // called inside an EnumEnhMetaFile loop. Go figure (bis).
246 //
247 // So when I see a EMR_SETWORLDTRANSFORM and EMR_MODIFYWORLDTRANSFORM, I need
248 // to fix the matrix according to the matrix previously loaded before playing
249 // back the buffer. Otherwise, the previously loaded matrix would be ignored
250 // and the EMF buffer would always be played back at its native resolution.
251 // Duh.
252 //
253 // I also use this opportunity to skip over eventual EMR_SETLAYOUT record that
254 // could remain.
255 //
[email protected]38bba4f2010-03-12 05:29:07256 // Another tweak we make is for JPEGs/PNGs in calls to StretchDIBits.
257 // (Our Pepper plugin code uses a JPEG). If the printer does not support
258 // JPEGs/PNGs natively we decompress the JPEG/PNG and then set it to the
259 // device.
260 // TODO(sanjeevr): We should also add JPEG/PNG support for SetSIBitsToDevice
261 //
[email protected]2aa8e182010-07-12 16:25:04262 // We also process any custom EMR_GDICOMMENT records which are our
263 // placeholders for StartPage and EndPage.
initial.commit09911bf2008-07-26 23:55:29264 // Note: I should probably care about view ports and clipping, eventually.
265 bool res;
266 switch (record()->iType) {
[email protected]38bba4f2010-03-12 05:29:07267 case EMR_STRETCHDIBITS: {
268 const EMRSTRETCHDIBITS * sdib_record =
269 reinterpret_cast<const EMRSTRETCHDIBITS*>(record());
270 const BYTE* record_start = reinterpret_cast<const BYTE *>(record());
271 const BITMAPINFOHEADER *bmih =
272 reinterpret_cast<const BITMAPINFOHEADER *>(record_start +
273 sdib_record->offBmiSrc);
274 const BYTE* bits = record_start + sdib_record->offBitsSrc;
275 bool play_normally = true;
276 res = false;
277 HDC hdc = context_->hdc;
278 scoped_ptr<SkBitmap> bitmap;
279 if (bmih->biCompression == BI_JPEG) {
280 if (!DIBFormatNativelySupported(hdc, CHECKJPEGFORMAT, bits,
281 bmih->biSizeImage)) {
282 play_normally = false;
283 base::TimeTicks start_time = base::TimeTicks::Now();
284 bitmap.reset(gfx::JPEGCodec::Decode(bits, bmih->biSizeImage));
285 UMA_HISTOGRAM_TIMES("Printing.JPEGDecompressTime",
286 base::TimeTicks::Now() - start_time);
287 }
288 } else if (bmih->biCompression == BI_PNG) {
289 if (!DIBFormatNativelySupported(hdc, CHECKPNGFORMAT, bits,
290 bmih->biSizeImage)) {
291 play_normally = false;
292 bitmap.reset(new SkBitmap());
293 base::TimeTicks start_time = base::TimeTicks::Now();
294 gfx::PNGCodec::Decode(bits, bmih->biSizeImage, bitmap.get());
295 UMA_HISTOGRAM_TIMES("Printing.PNGDecompressTime",
296 base::TimeTicks::Now() - start_time);
297 }
298 }
299 if (!play_normally) {
300 DCHECK(bitmap.get());
301 if (bitmap.get()) {
302 SkAutoLockPixels lock(*bitmap.get());
303 DCHECK_EQ(bitmap->getConfig(), SkBitmap::kARGB_8888_Config);
304 const uint32_t* pixels =
305 static_cast<const uint32_t*>(bitmap->getPixels());
306 if (pixels == NULL) {
307 NOTREACHED();
308 return false;
309 }
310 BITMAPINFOHEADER bmi = {0};
311 gfx::CreateBitmapHeader(bitmap->width(), bitmap->height(), &bmi);
312 res = (0 != StretchDIBits(hdc, sdib_record->xDest, sdib_record->yDest,
313 sdib_record->cxDest,
314 sdib_record->cyDest, sdib_record->xSrc,
315 sdib_record->ySrc,
316 sdib_record->cxSrc, sdib_record->cySrc,
317 pixels,
318 reinterpret_cast<const BITMAPINFO *>(&bmi),
319 sdib_record->iUsageSrc,
320 sdib_record->dwRop));
321 }
322 } else {
323 res = Play();
324 }
325 break;
326 }
initial.commit09911bf2008-07-26 23:55:29327 case EMR_SETWORLDTRANSFORM: {
328 DCHECK_EQ(record()->nSize, sizeof(DWORD) * 2 + sizeof(XFORM));
329 const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm);
330 HDC hdc = context_->hdc;
331 if (base_matrix) {
332 res = 0 != SetWorldTransform(hdc, base_matrix) &&
333 ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
334 } else {
335 res = 0 != SetWorldTransform(hdc, xform);
336 }
337 break;
338 }
339 case EMR_MODIFYWORLDTRANSFORM: {
340 DCHECK_EQ(record()->nSize,
341 sizeof(DWORD) * 2 + sizeof(XFORM) + sizeof(DWORD));
342 const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm);
343 const DWORD* option = reinterpret_cast<const DWORD*>(xform + 1);
344 HDC hdc = context_->hdc;
345 switch (*option) {
346 case MWT_IDENTITY:
347 if (base_matrix) {
348 res = 0 != SetWorldTransform(hdc, base_matrix);
349 } else {
350 res = 0 != ModifyWorldTransform(hdc, xform, MWT_IDENTITY);
351 }
352 break;
353 case MWT_LEFTMULTIPLY:
354 case MWT_RIGHTMULTIPLY:
355 res = 0 != ModifyWorldTransform(hdc, xform, *option);
356 break;
357 case 4: // MWT_SET
358 if (base_matrix) {
359 res = 0 != SetWorldTransform(hdc, base_matrix) &&
360 ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
361 } else {
362 res = 0 != SetWorldTransform(hdc, xform);
363 }
364 break;
365 default:
366 res = false;
367 break;
368 }
369 break;
370 }
371 case EMR_SETLAYOUT:
372 // Ignore it.
373 res = true;
374 break;
[email protected]2aa8e182010-07-12 16:25:04375 case EMR_GDICOMMENT: {
376 const EMRGDICOMMENT* comment_record =
377 reinterpret_cast<const EMRGDICOMMENT*>(record());
378 if (comment_record->cbData == sizeof(PageBreakRecord)) {
379 const PageBreakRecord* page_break_record =
380 reinterpret_cast<const PageBreakRecord*>(comment_record->Data);
381 if (page_break_record && page_break_record->IsValid()) {
382 if (page_break_record->type == PageBreakRecord::START_PAGE) {
383 res = !!::StartPage(context_->hdc);
384 } else if (page_break_record->type == PageBreakRecord::END_PAGE) {
385 res = !!::EndPage(context_->hdc);
386 } else {
387 res = false;
388 NOTREACHED();
389 }
390 } else {
391 res = Play();
392 }
[email protected]b2b0fce2011-01-12 16:34:40393 } else {
394 res = true;
[email protected]2aa8e182010-07-12 16:25:04395 }
396 break;
397 }
initial.commit09911bf2008-07-26 23:55:29398 default: {
399 res = Play();
400 break;
401 }
402 }
403 return res;
404}
405
[email protected]d2fdcf02011-03-21 22:16:43406skia::PlatformDevice* Emf::StartPageForVectorCanvas(
407 const gfx::Size& page_size, const gfx::Point& content_origin,
408 const float& scale_factor) {
409 if (!StartPage(page_size, content_origin, scale_factor))
410 return NULL;
411
412 return skia::VectorPlatformDeviceFactory::CreateDevice(page_size.width(),
413 page_size.height(),
414 true, hdc_);
415}
416
[email protected]edc531f92011-03-18 17:52:23417bool Emf::StartPage(const gfx::Size& /*page_size*/,
418 const gfx::Point& /*content_origin*/,
419 const float& scale_factor) {
[email protected]d2fdcf02011-03-21 22:16:43420 DCHECK_EQ(1.0f, scale_factor); // We don't support scaling here.
[email protected]2aa8e182010-07-12 16:25:04421 DCHECK(hdc_);
422 if (!hdc_)
423 return false;
[email protected]830cf742011-04-01 16:06:25424 page_count_++;
[email protected]2aa8e182010-07-12 16:25:04425 PageBreakRecord record(PageBreakRecord::START_PAGE);
426 return !!GdiComment(hdc_, sizeof(record),
427 reinterpret_cast<const BYTE *>(&record));
428}
429
[email protected]8f17cd3e2011-03-16 01:39:42430bool Emf::FinishPage() {
[email protected]2aa8e182010-07-12 16:25:04431 DCHECK(hdc_);
432 if (!hdc_)
433 return false;
434 PageBreakRecord record(PageBreakRecord::END_PAGE);
435 return !!GdiComment(hdc_, sizeof(record),
436 reinterpret_cast<const BYTE *>(&record));
437}
438
initial.commit09911bf2008-07-26 23:55:29439Emf::Enumerator::Enumerator(const Emf& emf, HDC context, const RECT* rect) {
440 context_.handle_table = NULL;
441 context_.objects_count = 0;
442 context_.hdc = NULL;
443 items_.clear();
444 if (!EnumEnhMetaFile(context,
445 emf.emf(),
446 &Emf::Enumerator::EnhMetaFileProc,
447 reinterpret_cast<void*>(this),
448 rect)) {
449 NOTREACHED();
450 items_.clear();
451 }
452 DCHECK_EQ(context_.hdc, context);
453}
454
455Emf::Enumerator::const_iterator Emf::Enumerator::begin() const {
456 return items_.begin();
457}
458
459Emf::Enumerator::const_iterator Emf::Enumerator::end() const {
460 return items_.end();
461}
462
463int CALLBACK Emf::Enumerator::EnhMetaFileProc(HDC hdc,
464 HANDLETABLE* handle_table,
465 const ENHMETARECORD* record,
466 int objects_count,
467 LPARAM param) {
468 Enumerator& emf = *reinterpret_cast<Enumerator*>(param);
469 if (!emf.context_.handle_table) {
470 DCHECK(!emf.context_.handle_table);
471 DCHECK(!emf.context_.objects_count);
472 emf.context_.handle_table = handle_table;
473 emf.context_.objects_count = objects_count;
474 emf.context_.hdc = hdc;
475 } else {
476 DCHECK_EQ(emf.context_.handle_table, handle_table);
477 DCHECK_EQ(emf.context_.objects_count, objects_count);
478 DCHECK_EQ(emf.context_.hdc, hdc);
479 }
480 emf.items_.push_back(Record(&emf.context_, record));
481 return 1;
482}
483
[email protected]0e0fca32009-07-06 15:25:50484} // namespace printing