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 21 namespace { 22 23 const int kCustomGdiCommentSignature = 0xdeadbabe; 24 struct 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 39 int 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 57 int 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. 108 class 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 155 namespace printing { 156 157 bool 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 168 Emf::Emf() : emf_(NULL), hdc_(NULL), page_count_(0) { 169 } 170 171 Emf::~Emf() { 172 DCHECK(!hdc_); 173 if (emf_) 174 DeleteEnhMetaFile(emf_); 175 } 176 177 bool 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 184 bool 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 191 bool Emf::Init() { 192 DCHECK(!emf_ && !hdc_); 193 hdc_ = CreateEnhMetaFile(NULL, NULL, NULL, NULL); 194 DCHECK(hdc_); 195 return hdc_ != NULL; 196 } 197 198 bool 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 205 bool Emf::FinishDocument() { 206 DCHECK(!emf_ && hdc_); 207 emf_ = CloseEnhMetaFile(hdc_); 208 DCHECK(emf_); 209 hdc_ = NULL; 210 return emf_ != NULL; 211 } 212 213 bool 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 224 bool 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 241 gfx::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 257 uint32 Emf::GetDataSize() const { 258 DCHECK(emf_ && !hdc_); 259 return GetEnhMetaFileBits(emf_, 0, NULL); 260 } 261 262 bool 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 271 bool 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 282 bool 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 303 int 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 319 Emf::EnumerationContext::EnumerationContext() { 320 memset(this, 0, sizeof(*this)); 321 } 322 323 Emf::Record::Record(const ENHMETARECORD* record) 324 : record_(record) { 325 DCHECK(record_); 326 } 327 328 bool 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 335 bool 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 520 SkBaseDevice* 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 531 bool 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 543 bool 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 552 Emf::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 565 Emf::Enumerator::const_iterator Emf::Enumerator::begin() const { 566 return items_.begin(); 567 } 568 569 Emf::Enumerator::const_iterator Emf::Enumerator::end() const { 570 return items_.end(); 571 } 572 573 int 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 594 bool Emf::IsAlphaBlendUsed() const { 595 bool result = false; 596 ::EnumEnhMetaFile(NULL, 597 emf(), 598 &IsAlphaBlendUsedEnumProc, 599 &result, 600 NULL); 601 return result; 602 } 603 604 Emf* 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 649 Emf* 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