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