Home | History | Annotate | Download | only in printing
      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