1// Copyright (c) 2012 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/emf_win.h"
6
7#include "base/files/file_path.h"
8#include "base/logging.h"
9#include "base/memory/scoped_ptr.h"
10#include "base/win/scoped_gdi_object.h"
11#include "base/win/scoped_hdc.h"
12#include "base/win/scoped_select_object.h"
13#include "skia/ext/vector_platform_device_emf_win.h"
14#include "third_party/skia/include/core/SkBitmap.h"
15#include "ui/gfx/codec/jpeg_codec.h"
16#include "ui/gfx/codec/png_codec.h"
17#include "ui/gfx/gdi_util.h"
18#include "ui/gfx/rect.h"
19#include "ui/gfx/size.h"
20
21namespace {
22
23const int kCustomGdiCommentSignature = 0xdeadbabe;
24struct PageBreakRecord {
25  int signature;
26  enum PageBreakType {
27    START_PAGE,
28    END_PAGE,
29  } type;
30  explicit PageBreakRecord(PageBreakType type_in)
31      : signature(kCustomGdiCommentSignature), type(type_in) {
32  }
33  bool IsValid() const {
34    return (signature == kCustomGdiCommentSignature) &&
35           (type >= START_PAGE) && (type <= END_PAGE);
36  }
37};
38
39int CALLBACK IsAlphaBlendUsedEnumProc(HDC,
40                                      HANDLETABLE*,
41                                      const ENHMETARECORD *record,
42                                      int,
43                                      LPARAM data) {
44  bool* result = reinterpret_cast<bool*>(data);
45  if (!result)
46    return 0;
47  switch (record->iType) {
48    case EMR_ALPHABLEND: {
49      *result = true;
50      return 0;
51      break;
52    }
53  }
54  return 1;
55}
56
57int CALLBACK RasterizeAlphaBlendProc(HDC metafile_dc,
58                                     HANDLETABLE* handle_table,
59                                     const ENHMETARECORD *record,
60                                     int num_objects,
61                                     LPARAM data) {
62    HDC bitmap_dc = *reinterpret_cast<HDC*>(data);
63    // Play this command to the bitmap DC.
64    ::PlayEnhMetaFileRecord(bitmap_dc, handle_table, record, num_objects);
65    switch (record->iType) {
66    case EMR_ALPHABLEND: {
67      const EMRALPHABLEND* alpha_blend =
68          reinterpret_cast<const EMRALPHABLEND*>(record);
69      // Don't modify transformation here.
70      // Old implementation did reset transformations for DC to identity matrix.
71      // That was not correct and cause some bugs, like unexpected cropping.
72      // EMRALPHABLEND is rendered into bitmap and metafile contexts with
73      // current transformation. If we don't touch them here BitBlt will copy
74      // same areas.
75      ::BitBlt(metafile_dc,
76               alpha_blend->xDest,
77               alpha_blend->yDest,
78               alpha_blend->cxDest,
79               alpha_blend->cyDest,
80               bitmap_dc,
81               alpha_blend->xDest,
82               alpha_blend->yDest,
83               SRCCOPY);
84      break;
85    }
86    case EMR_CREATEBRUSHINDIRECT:
87    case EMR_CREATECOLORSPACE:
88    case EMR_CREATECOLORSPACEW:
89    case EMR_CREATEDIBPATTERNBRUSHPT:
90    case EMR_CREATEMONOBRUSH:
91    case EMR_CREATEPALETTE:
92    case EMR_CREATEPEN:
93    case EMR_DELETECOLORSPACE:
94    case EMR_DELETEOBJECT:
95    case EMR_EXTCREATEFONTINDIRECTW:
96      // Play object creation command only once.
97      break;
98
99    default:
100      // Play this command to the metafile DC.
101      ::PlayEnhMetaFileRecord(metafile_dc, handle_table, record, num_objects);
102      break;
103    }
104    return 1;  // Continue enumeration
105}
106
107// Bitmapt for rasterization.
108class RasterBitmap {
109 public:
110  explicit RasterBitmap(const gfx::Size& raster_size)
111      : saved_object_(NULL) {
112    context_.Set(::CreateCompatibleDC(NULL));
113    if (!context_) {
114      NOTREACHED() << "Bitmap DC creation failed";
115      return;
116    }
117    ::SetGraphicsMode(context_, GM_ADVANCED);
118    void* bits = NULL;
119    gfx::Rect bitmap_rect(raster_size);
120    gfx::CreateBitmapHeader(raster_size.width(), raster_size.height(),
121                            &header_.bmiHeader);
122    bitmap_.Set(::CreateDIBSection(context_, &header_, DIB_RGB_COLORS, &bits,
123                                   NULL, 0));
124    if (!bitmap_)
125      NOTREACHED() << "Raster bitmap creation for printing failed";
126
127    saved_object_ = ::SelectObject(context_, bitmap_);
128    RECT rect = bitmap_rect.ToRECT();
129    ::FillRect(context_, &rect,
130               static_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH)));
131
132  }
133
134  ~RasterBitmap() {
135    ::SelectObject(context_, saved_object_);
136  }
137
138  HDC context() const {
139    return context_;
140  }
141
142  base::win::ScopedCreateDC context_;
143  BITMAPINFO header_;
144  base::win::ScopedBitmap bitmap_;
145  HGDIOBJ saved_object_;
146
147 private:
148  DISALLOW_COPY_AND_ASSIGN(RasterBitmap);
149};
150
151
152
153}  // namespace
154
155namespace printing {
156
157bool DIBFormatNativelySupported(HDC dc, uint32 escape, const BYTE* bits,
158                                int size) {
159  BOOL supported = FALSE;
160  if (ExtEscape(dc, QUERYESCSUPPORT, sizeof(escape),
161                reinterpret_cast<LPCSTR>(&escape), 0, 0) > 0) {
162    ExtEscape(dc, escape, size, reinterpret_cast<LPCSTR>(bits),
163              sizeof(supported), reinterpret_cast<LPSTR>(&supported));
164  }
165  return !!supported;
166}
167
168Emf::Emf() : emf_(NULL), hdc_(NULL), page_count_(0) {
169}
170
171Emf::~Emf() {
172  DCHECK(!hdc_);
173  if (emf_)
174    DeleteEnhMetaFile(emf_);
175}
176
177bool Emf::InitToFile(const base::FilePath& metafile_path) {
178  DCHECK(!emf_ && !hdc_);
179  hdc_ = CreateEnhMetaFile(NULL, metafile_path.value().c_str(), NULL, NULL);
180  DCHECK(hdc_);
181  return hdc_ != NULL;
182}
183
184bool Emf::InitFromFile(const base::FilePath& metafile_path) {
185  DCHECK(!emf_ && !hdc_);
186  emf_ = GetEnhMetaFile(metafile_path.value().c_str());
187  DCHECK(emf_);
188  return emf_ != NULL;
189}
190
191bool Emf::Init() {
192  DCHECK(!emf_ && !hdc_);
193  hdc_ = CreateEnhMetaFile(NULL, NULL, NULL, NULL);
194  DCHECK(hdc_);
195  return hdc_ != NULL;
196}
197
198bool Emf::InitFromData(const void* src_buffer, uint32 src_buffer_size) {
199  DCHECK(!emf_ && !hdc_);
200  emf_ = SetEnhMetaFileBits(src_buffer_size,
201                            reinterpret_cast<const BYTE*>(src_buffer));
202  return emf_ != NULL;
203}
204
205bool Emf::FinishDocument() {
206  DCHECK(!emf_ && hdc_);
207  emf_ = CloseEnhMetaFile(hdc_);
208  DCHECK(emf_);
209  hdc_ = NULL;
210  return emf_ != NULL;
211}
212
213bool Emf::Playback(HDC hdc, const RECT* rect) const {
214  DCHECK(emf_ && !hdc_);
215  RECT bounds;
216  if (!rect) {
217    // Get the natural bounds of the EMF buffer.
218    bounds = GetPageBounds(1).ToRECT();
219    rect = &bounds;
220  }
221  return PlayEnhMetaFile(hdc, emf_, rect) != 0;
222}
223
224bool Emf::SafePlayback(HDC context) const {
225  DCHECK(emf_ && !hdc_);
226  XFORM base_matrix;
227  if (!GetWorldTransform(context, &base_matrix)) {
228    NOTREACHED();
229    return false;
230  }
231  Emf::EnumerationContext playback_context;
232  playback_context.base_matrix = &base_matrix;
233  RECT rect = GetPageBounds(1).ToRECT();
234  return EnumEnhMetaFile(context,
235                         emf_,
236                         &Emf::SafePlaybackProc,
237                         reinterpret_cast<void*>(&playback_context),
238                         &rect) != 0;
239}
240
241gfx::Rect Emf::GetPageBounds(unsigned int page_number) const {
242  DCHECK(emf_ && !hdc_);
243  DCHECK_EQ(1U, page_number);
244  ENHMETAHEADER header;
245  if (GetEnhMetaFileHeader(emf_, sizeof(header), &header) != sizeof(header)) {
246    NOTREACHED();
247    return gfx::Rect();
248  }
249  // Add 1 to right and bottom because it's inclusive rectangle.
250  // See ENHMETAHEADER.
251  return gfx::Rect(header.rclBounds.left,
252                   header.rclBounds.top,
253                   header.rclBounds.right - header.rclBounds.left + 1,
254                   header.rclBounds.bottom - header.rclBounds.top + 1);
255}
256
257uint32 Emf::GetDataSize() const {
258  DCHECK(emf_ && !hdc_);
259  return GetEnhMetaFileBits(emf_, 0, NULL);
260}
261
262bool Emf::GetData(void* buffer, uint32 size) const {
263  DCHECK(emf_ && !hdc_);
264  DCHECK(buffer && size);
265  uint32 size2 =
266      GetEnhMetaFileBits(emf_, size, reinterpret_cast<BYTE*>(buffer));
267  DCHECK(size2 == size);
268  return size2 == size && size2 != 0;
269}
270
271bool Emf::GetDataAsVector(std::vector<uint8>* buffer) const {
272  uint32 size = GetDataSize();
273  if (!size)
274    return false;
275
276  buffer->resize(size);
277  if (!GetData(&buffer->front(), size))
278    return false;
279  return true;
280}
281
282bool Emf::SaveTo(const base::FilePath& file_path) const {
283  HANDLE file = CreateFile(file_path.value().c_str(), GENERIC_WRITE,
284                           FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
285                           CREATE_ALWAYS, 0, NULL);
286  if (file == INVALID_HANDLE_VALUE)
287    return false;
288
289  bool success = false;
290  std::vector<uint8> buffer;
291  if (GetDataAsVector(&buffer)) {
292    DWORD written = 0;
293    if (WriteFile(file, &*buffer.begin(), static_cast<DWORD>(buffer.size()),
294                  &written, NULL) &&
295        written == buffer.size()) {
296      success = true;
297    }
298  }
299  CloseHandle(file);
300  return success;
301}
302
303int CALLBACK Emf::SafePlaybackProc(HDC hdc,
304                                   HANDLETABLE* handle_table,
305                                   const ENHMETARECORD* record,
306                                   int objects_count,
307                                   LPARAM param) {
308  Emf::EnumerationContext* context =
309      reinterpret_cast<Emf::EnumerationContext*>(param);
310  context->handle_table = handle_table;
311  context->objects_count = objects_count;
312  context->hdc = hdc;
313  Record record_instance(record);
314  bool success = record_instance.SafePlayback(context);
315  DCHECK(success);
316  return 1;
317}
318
319Emf::EnumerationContext::EnumerationContext() {
320  memset(this, 0, sizeof(*this));
321}
322
323Emf::Record::Record(const ENHMETARECORD* record)
324    : record_(record) {
325  DCHECK(record_);
326}
327
328bool Emf::Record::Play(Emf::EnumerationContext* context) const {
329  return 0 != PlayEnhMetaFileRecord(context->hdc,
330                                    context->handle_table,
331                                    record_,
332                                    context->objects_count);
333}
334
335bool Emf::Record::SafePlayback(Emf::EnumerationContext* context) const {
336  // For EMF field description, see [MS-EMF] Enhanced Metafile Format
337  // Specification.
338  //
339  // This is the second major EMF breakage I get; the first one being
340  // SetDCBrushColor/SetDCPenColor/DC_PEN/DC_BRUSH being silently ignored.
341  //
342  // This function is the guts of the fix for bug 1186598. Some printer drivers
343  // somehow choke on certain EMF records, but calling the corresponding
344  // function directly on the printer HDC is fine. Still, playing the EMF record
345  // fails. Go figure.
346  //
347  // The main issue is that SetLayout is totally unsupported on these printers
348  // (HP 4500/4700). I used to call SetLayout and I stopped. I found out this is
349  // not sufficient because GDI32!PlayEnhMetaFile internally calls SetLayout(!)
350  // Damn.
351  //
352  // So I resorted to manually parse the EMF records and play them one by one.
353  // The issue with this method compared to using PlayEnhMetaFile to play back
354  // an EMF buffer is that the later silently fixes the matrix to take in
355  // account the matrix currently loaded at the time of the call.
356  // The matrix magic is done transparently when using PlayEnhMetaFile but since
357  // I'm processing one field at a time, I need to do the fixup myself. Note
358  // that PlayEnhMetaFileRecord doesn't fix the matrix correctly even when
359  // called inside an EnumEnhMetaFile loop. Go figure (bis).
360  //
361  // So when I see a EMR_SETWORLDTRANSFORM and EMR_MODIFYWORLDTRANSFORM, I need
362  // to fix the matrix according to the matrix previously loaded before playing
363  // back the buffer. Otherwise, the previously loaded matrix would be ignored
364  // and the EMF buffer would always be played back at its native resolution.
365  // Duh.
366  //
367  // I also use this opportunity to skip over eventual EMR_SETLAYOUT record that
368  // could remain.
369  //
370  // Another tweak we make is for JPEGs/PNGs in calls to StretchDIBits.
371  // (Our Pepper plugin code uses a JPEG). If the printer does not support
372  // JPEGs/PNGs natively we decompress the JPEG/PNG and then set it to the
373  // device.
374  // TODO(sanjeevr): We should also add JPEG/PNG support for SetSIBitsToDevice
375  //
376  // We also process any custom EMR_GDICOMMENT records which are our
377  // placeholders for StartPage and EndPage.
378  // Note: I should probably care about view ports and clipping, eventually.
379  bool res = false;
380  const XFORM* base_matrix = context->base_matrix;
381  switch (record()->iType) {
382    case EMR_STRETCHDIBITS: {
383      const EMRSTRETCHDIBITS * sdib_record =
384          reinterpret_cast<const EMRSTRETCHDIBITS*>(record());
385      const BYTE* record_start = reinterpret_cast<const BYTE *>(record());
386      const BITMAPINFOHEADER *bmih =
387          reinterpret_cast<const BITMAPINFOHEADER *>(record_start +
388                                                     sdib_record->offBmiSrc);
389      const BYTE* bits = record_start + sdib_record->offBitsSrc;
390      bool play_normally = true;
391      res = false;
392      HDC hdc = context->hdc;
393      scoped_ptr<SkBitmap> bitmap;
394      if (bmih->biCompression == BI_JPEG) {
395        if (!DIBFormatNativelySupported(hdc, CHECKJPEGFORMAT, bits,
396                                        bmih->biSizeImage)) {
397          play_normally = false;
398          bitmap.reset(gfx::JPEGCodec::Decode(bits, bmih->biSizeImage));
399        }
400      } else if (bmih->biCompression == BI_PNG) {
401        if (!DIBFormatNativelySupported(hdc, CHECKPNGFORMAT, bits,
402                                        bmih->biSizeImage)) {
403          play_normally = false;
404          bitmap.reset(new SkBitmap());
405          gfx::PNGCodec::Decode(bits, bmih->biSizeImage, bitmap.get());
406        }
407      }
408      if (!play_normally) {
409        DCHECK(bitmap.get());
410        if (bitmap.get()) {
411          SkAutoLockPixels lock(*bitmap.get());
412          DCHECK_EQ(bitmap->config(), SkBitmap::kARGB_8888_Config);
413          const uint32_t* pixels =
414              static_cast<const uint32_t*>(bitmap->getPixels());
415          if (pixels == NULL) {
416            NOTREACHED();
417            return false;
418          }
419          BITMAPINFOHEADER bmi = {0};
420          gfx::CreateBitmapHeader(bitmap->width(), bitmap->height(), &bmi);
421          res = (0 != StretchDIBits(hdc, sdib_record->xDest, sdib_record->yDest,
422                                    sdib_record->cxDest,
423                                    sdib_record->cyDest, sdib_record->xSrc,
424                                    sdib_record->ySrc,
425                                    sdib_record->cxSrc, sdib_record->cySrc,
426                                    pixels,
427                                    reinterpret_cast<const BITMAPINFO *>(&bmi),
428                                    sdib_record->iUsageSrc,
429                                    sdib_record->dwRop));
430        }
431      } else {
432        res = Play(context);
433      }
434      break;
435    }
436    case EMR_SETWORLDTRANSFORM: {
437      DCHECK_EQ(record()->nSize, sizeof(DWORD) * 2 + sizeof(XFORM));
438      const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm);
439      HDC hdc = context->hdc;
440      if (base_matrix) {
441        res = 0 != SetWorldTransform(hdc, base_matrix) &&
442                   ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
443      } else {
444        res = 0 != SetWorldTransform(hdc, xform);
445      }
446      break;
447    }
448    case EMR_MODIFYWORLDTRANSFORM: {
449      DCHECK_EQ(record()->nSize,
450                sizeof(DWORD) * 2 + sizeof(XFORM) + sizeof(DWORD));
451      const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm);
452      const DWORD* option = reinterpret_cast<const DWORD*>(xform + 1);
453      HDC hdc = context->hdc;
454      switch (*option) {
455        case MWT_IDENTITY:
456          if (base_matrix) {
457            res = 0 != SetWorldTransform(hdc, base_matrix);
458          } else {
459            res = 0 != ModifyWorldTransform(hdc, xform, MWT_IDENTITY);
460          }
461          break;
462        case MWT_LEFTMULTIPLY:
463        case MWT_RIGHTMULTIPLY:
464          res = 0 != ModifyWorldTransform(hdc, xform, *option);
465          break;
466        case 4:  // MWT_SET
467          if (base_matrix) {
468            res = 0 != SetWorldTransform(hdc, base_matrix) &&
469                       ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
470          } else {
471            res = 0 != SetWorldTransform(hdc, xform);
472          }
473          break;
474        default:
475          res = false;
476          break;
477      }
478      break;
479    }
480    case EMR_SETLAYOUT:
481      // Ignore it.
482      res = true;
483      break;
484    case EMR_GDICOMMENT: {
485      const EMRGDICOMMENT* comment_record =
486          reinterpret_cast<const EMRGDICOMMENT*>(record());
487      if (comment_record->cbData == sizeof(PageBreakRecord)) {
488        const PageBreakRecord* page_break_record =
489            reinterpret_cast<const PageBreakRecord*>(comment_record->Data);
490        if (page_break_record && page_break_record->IsValid()) {
491          if (page_break_record->type == PageBreakRecord::START_PAGE) {
492            res = !!::StartPage(context->hdc);
493            DCHECK_EQ(0, context->dc_on_page_start);
494            context->dc_on_page_start = ::SaveDC(context->hdc);
495          } else if (page_break_record->type == PageBreakRecord::END_PAGE) {
496            DCHECK_NE(0, context->dc_on_page_start);
497            ::RestoreDC(context->hdc, context->dc_on_page_start);
498            context->dc_on_page_start = 0;
499            res = !!::EndPage(context->hdc);
500          } else {
501            res = false;
502            NOTREACHED();
503          }
504        } else {
505          res = Play(context);
506        }
507      } else {
508        res = true;
509      }
510      break;
511    }
512    default: {
513      res = Play(context);
514      break;
515    }
516  }
517  return res;
518}
519
520SkBaseDevice* Emf::StartPageForVectorCanvas(
521    const gfx::Size& page_size, const gfx::Rect& content_area,
522    const float& scale_factor) {
523  if (!StartPage(page_size, content_area, scale_factor))
524    return NULL;
525
526  return skia::VectorPlatformDeviceEmf::CreateDevice(page_size.width(),
527                                                     page_size.height(),
528                                                     true, hdc_);
529}
530
531bool Emf::StartPage(const gfx::Size& /*page_size*/,
532                    const gfx::Rect& /*content_area*/,
533                    const float& /*scale_factor*/) {
534  DCHECK(hdc_);
535  if (!hdc_)
536    return false;
537  page_count_++;
538  PageBreakRecord record(PageBreakRecord::START_PAGE);
539  return !!GdiComment(hdc_, sizeof(record),
540                      reinterpret_cast<const BYTE *>(&record));
541}
542
543bool Emf::FinishPage() {
544  DCHECK(hdc_);
545  if (!hdc_)
546    return false;
547  PageBreakRecord record(PageBreakRecord::END_PAGE);
548  return !!GdiComment(hdc_, sizeof(record),
549                      reinterpret_cast<const BYTE *>(&record));
550}
551
552Emf::Enumerator::Enumerator(const Emf& emf, HDC context, const RECT* rect) {
553  items_.clear();
554  if (!EnumEnhMetaFile(context,
555                       emf.emf(),
556                       &Emf::Enumerator::EnhMetaFileProc,
557                       reinterpret_cast<void*>(this),
558                       rect)) {
559    NOTREACHED();
560    items_.clear();
561  }
562  DCHECK_EQ(context_.hdc, context);
563}
564
565Emf::Enumerator::const_iterator Emf::Enumerator::begin() const {
566  return items_.begin();
567}
568
569Emf::Enumerator::const_iterator Emf::Enumerator::end() const {
570  return items_.end();
571}
572
573int CALLBACK Emf::Enumerator::EnhMetaFileProc(HDC hdc,
574                                              HANDLETABLE* handle_table,
575                                              const ENHMETARECORD* record,
576                                              int objects_count,
577                                              LPARAM param) {
578  Enumerator& emf = *reinterpret_cast<Enumerator*>(param);
579  if (!emf.context_.handle_table) {
580    DCHECK(!emf.context_.handle_table);
581    DCHECK(!emf.context_.objects_count);
582    emf.context_.handle_table = handle_table;
583    emf.context_.objects_count = objects_count;
584    emf.context_.hdc = hdc;
585  } else {
586    DCHECK_EQ(emf.context_.handle_table, handle_table);
587    DCHECK_EQ(emf.context_.objects_count, objects_count);
588    DCHECK_EQ(emf.context_.hdc, hdc);
589  }
590  emf.items_.push_back(Record(record));
591  return 1;
592}
593
594bool Emf::IsAlphaBlendUsed() const {
595  bool result = false;
596  ::EnumEnhMetaFile(NULL,
597                    emf(),
598                    &IsAlphaBlendUsedEnumProc,
599                    &result,
600                    NULL);
601  return result;
602}
603
604Emf* Emf::RasterizeMetafile(int raster_area_in_pixels) const {
605  gfx::Rect page_bounds = GetPageBounds(1);
606  gfx::Size page_size(page_bounds.size());
607  if (page_size.GetArea() <= 0) {
608    NOTREACHED() << "Metafile is empty";
609    page_bounds = gfx::Rect(1, 1);
610  }
611
612  float scale = sqrt(float(raster_area_in_pixels) / page_size.GetArea());
613  page_size.set_width(std::max<int>(1, page_size.width() * scale));
614  page_size.set_height(std::max<int>(1, page_size.height() * scale));
615
616
617  RasterBitmap bitmap(page_size);
618
619  gfx::Rect bitmap_rect(page_size);
620  RECT rect = bitmap_rect.ToRECT();
621  Playback(bitmap.context(), &rect);
622
623  scoped_ptr<Emf> result(new Emf);
624  result->Init();
625  HDC hdc = result->context();
626  DCHECK(hdc);
627  skia::InitializeDC(hdc);
628
629  // Params are ignored.
630  result->StartPage(page_bounds.size(), page_bounds, 1);
631
632  ::ModifyWorldTransform(hdc, NULL, MWT_IDENTITY);
633  XFORM xform = {
634    float(page_bounds.width()) / bitmap_rect.width(), 0,
635    0, float(page_bounds.height()) / bitmap_rect.height(),
636    page_bounds.x(),
637    page_bounds.y(),
638  };
639  ::SetWorldTransform(hdc, &xform);
640  ::BitBlt(hdc, 0, 0, bitmap_rect.width(), bitmap_rect.height(),
641           bitmap.context(), bitmap_rect.x(), bitmap_rect.y(), SRCCOPY);
642
643  result->FinishPage();
644  result->FinishDocument();
645
646  return result.release();
647}
648
649Emf* Emf::RasterizeAlphaBlend() const {
650  gfx::Rect page_bounds = GetPageBounds(1);
651  if (page_bounds.size().GetArea() <= 0) {
652    NOTREACHED() << "Metafile is empty";
653    page_bounds = gfx::Rect(1, 1);
654  }
655
656  RasterBitmap bitmap(page_bounds.size());
657
658  // Map metafile page_bounds.x(), page_bounds.y() to bitmap 0, 0.
659  XFORM xform = { 1, 0, 0, 1, -page_bounds.x(), -page_bounds.y()};
660  ::SetWorldTransform(bitmap.context(), &xform);
661
662  scoped_ptr<Emf> result(new Emf);
663  result->Init();
664  HDC hdc = result->context();
665  DCHECK(hdc);
666  skia::InitializeDC(hdc);
667
668  HDC bitmap_dc = bitmap.context();
669  RECT rect = page_bounds.ToRECT();
670  ::EnumEnhMetaFile(hdc, emf(), &RasterizeAlphaBlendProc, &bitmap_dc, &rect);
671
672  result->FinishDocument();
673
674  return result.release();
675}
676
677
678}  // namespace printing
679