Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / printing / emf_win.cc
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
22 namespace {
23
24 int 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
42 int 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.
93 class 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
140 namespace printing {
141
142 bool 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
153 Emf::Emf() : emf_(NULL), hdc_(NULL) {
154 }
155
156 Emf::~Emf() {
157   Close();
158 }
159
160 void Emf::Close() {
161   DCHECK(!hdc_);
162   if (emf_)
163     DeleteEnhMetaFile(emf_);
164   emf_ = NULL;
165 }
166
167 bool 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
174 bool 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
181 bool Emf::Init() {
182   DCHECK(!emf_ && !hdc_);
183   hdc_ = CreateEnhMetaFile(NULL, NULL, NULL, NULL);
184   DCHECK(hdc_);
185   return hdc_ != NULL;
186 }
187
188 bool 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
195 bool Emf::FinishDocument() {
196   DCHECK(!emf_ && hdc_);
197   emf_ = CloseEnhMetaFile(hdc_);
198   DCHECK(emf_);
199   hdc_ = NULL;
200   return emf_ != NULL;
201 }
202
203 bool 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
214 bool 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
233 gfx::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
249 uint32 Emf::GetDataSize() const {
250   DCHECK(emf_ && !hdc_);
251   return GetEnhMetaFileBits(emf_, 0, NULL);
252 }
253
254 bool 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
263 int 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
279 Emf::EnumerationContext::EnumerationContext() {
280   memset(this, 0, sizeof(*this));
281 }
282
283 Emf::Record::Record(const ENHMETARECORD* record)
284     : record_(record) {
285   DCHECK(record_);
286 }
287
288 bool 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
295 bool 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
452 bool Emf::StartPage(const gfx::Size& /*page_size*/,
453                     const gfx::Rect& /*content_area*/,
454                     const float& /*scale_factor*/) {
455   return true;
456 }
457
458 bool Emf::FinishPage() {
459   return true;
460 }
461
462 Emf::Enumerator::Enumerator(const Emf& emf, HDC context, const RECT* rect) {
463   items_.clear();
464   if (!EnumEnhMetaFile(context,
465                        emf.emf(),
466                        &Emf::Enumerator::EnhMetaFileProc,
467                        reinterpret_cast<void*>(this),
468                        rect)) {
469     NOTREACHED();
470     items_.clear();
471   }
472   DCHECK_EQ(context_.hdc, context);
473 }
474
475 Emf::Enumerator::const_iterator Emf::Enumerator::begin() const {
476   return items_.begin();
477 }
478
479 Emf::Enumerator::const_iterator Emf::Enumerator::end() const {
480   return items_.end();
481 }
482
483 int CALLBACK Emf::Enumerator::EnhMetaFileProc(HDC hdc,
484                                               HANDLETABLE* handle_table,
485                                               const ENHMETARECORD* record,
486                                               int objects_count,
487                                               LPARAM param) {
488   Enumerator& emf = *reinterpret_cast<Enumerator*>(param);
489   if (!emf.context_.handle_table) {
490     DCHECK(!emf.context_.handle_table);
491     DCHECK(!emf.context_.objects_count);
492     emf.context_.handle_table = handle_table;
493     emf.context_.objects_count = objects_count;
494     emf.context_.hdc = hdc;
495   } else {
496     DCHECK_EQ(emf.context_.handle_table, handle_table);
497     DCHECK_EQ(emf.context_.objects_count, objects_count);
498     DCHECK_EQ(emf.context_.hdc, hdc);
499   }
500   emf.items_.push_back(Record(record));
501   return 1;
502 }
503
504 bool Emf::IsAlphaBlendUsed() const {
505   bool result = false;
506   ::EnumEnhMetaFile(NULL,
507                     emf(),
508                     &IsAlphaBlendUsedEnumProc,
509                     &result,
510                     NULL);
511   return result;
512 }
513
514 scoped_ptr<Emf> Emf::RasterizeMetafile(int raster_area_in_pixels) const {
515   gfx::Rect page_bounds = GetPageBounds(1);
516   gfx::Size page_size(page_bounds.size());
517   if (page_size.GetArea() <= 0) {
518     NOTREACHED() << "Metafile is empty";
519     page_bounds = gfx::Rect(1, 1);
520   }
521
522   float scale = sqrt(float(raster_area_in_pixels) / page_size.GetArea());
523   page_size.set_width(std::max<int>(1, page_size.width() * scale));
524   page_size.set_height(std::max<int>(1, page_size.height() * scale));
525
526
527   RasterBitmap bitmap(page_size);
528
529   gfx::Rect bitmap_rect(page_size);
530   RECT rect = bitmap_rect.ToRECT();
531   Playback(bitmap.context(), &rect);
532
533   scoped_ptr<Emf> result(new Emf);
534   result->Init();
535   HDC hdc = result->context();
536   DCHECK(hdc);
537   skia::InitializeDC(hdc);
538
539   // Params are ignored.
540   result->StartPage(page_bounds.size(), page_bounds, 1);
541
542   ::ModifyWorldTransform(hdc, NULL, MWT_IDENTITY);
543   XFORM xform = {
544     float(page_bounds.width()) / bitmap_rect.width(), 0,
545     0, float(page_bounds.height()) / bitmap_rect.height(),
546     page_bounds.x(),
547     page_bounds.y(),
548   };
549   ::SetWorldTransform(hdc, &xform);
550   ::BitBlt(hdc, 0, 0, bitmap_rect.width(), bitmap_rect.height(),
551            bitmap.context(), bitmap_rect.x(), bitmap_rect.y(), SRCCOPY);
552
553   result->FinishPage();
554   result->FinishDocument();
555
556   return result.Pass();
557 }
558
559 scoped_ptr<Emf> Emf::RasterizeAlphaBlend() const {
560   gfx::Rect page_bounds = GetPageBounds(1);
561   if (page_bounds.size().GetArea() <= 0) {
562     NOTREACHED() << "Metafile is empty";
563     page_bounds = gfx::Rect(1, 1);
564   }
565
566   RasterBitmap bitmap(page_bounds.size());
567
568   // Map metafile page_bounds.x(), page_bounds.y() to bitmap 0, 0.
569   XFORM xform = { 1, 0, 0, 1, -page_bounds.x(), -page_bounds.y()};
570   ::SetWorldTransform(bitmap.context(), &xform);
571
572   scoped_ptr<Emf> result(new Emf);
573   result->Init();
574   HDC hdc = result->context();
575   DCHECK(hdc);
576   skia::InitializeDC(hdc);
577
578   HDC bitmap_dc = bitmap.context();
579   RECT rect = page_bounds.ToRECT();
580   ::EnumEnhMetaFile(hdc, emf(), &RasterizeAlphaBlendProc, &bitmap_dc, &rect);
581
582   result->FinishDocument();
583
584   return result.Pass();
585 }
586
587
588 }  // namespace printing