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