Upload upstream chromium 73.0.3683.0
[platform/framework/web/chromium-efl.git] / 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 <stdint.h>
8
9 #include <algorithm>
10 #include <memory>
11
12 #include "base/files/file.h"
13 #include "base/files/file_path.h"
14 #include "base/logging.h"
15 #include "base/macros.h"
16 #include "base/numerics/safe_conversions.h"
17 #include "skia/ext/skia_utils_win.h"
18 #include "third_party/skia/include/core/SkBitmap.h"
19 #include "ui/gfx/codec/jpeg_codec.h"
20 #include "ui/gfx/codec/png_codec.h"
21 #include "ui/gfx/geometry/rect.h"
22 #include "ui/gfx/geometry/size.h"
23
24 namespace printing {
25
26 namespace {
27
28 bool DIBFormatNativelySupported(HDC dc, uint32_t escape, const BYTE* bits,
29                                 int size) {
30   BOOL supported = FALSE;
31   if (ExtEscape(dc, QUERYESCSUPPORT, sizeof(escape),
32                 reinterpret_cast<LPCSTR>(&escape), 0, 0) > 0) {
33     ExtEscape(dc, escape, size, reinterpret_cast<LPCSTR>(bits),
34               sizeof(supported), reinterpret_cast<LPSTR>(&supported));
35   }
36   return !!supported;
37 }
38
39 }  // namespace
40
41 Emf::Emf() : emf_(nullptr), hdc_(nullptr) {}
42
43 Emf::~Emf() {
44   Close();
45 }
46
47 void Emf::Close() {
48   DCHECK(!hdc_);
49   if (emf_)
50     DeleteEnhMetaFile(emf_);
51   emf_ = nullptr;
52 }
53
54 bool Emf::InitToFile(const base::FilePath& metafile_path) {
55   DCHECK(!emf_ && !hdc_);
56   hdc_ = CreateEnhMetaFile(nullptr, metafile_path.value().c_str(), nullptr,
57                            nullptr);
58   DCHECK(hdc_);
59   return !!hdc_;
60 }
61
62 bool Emf::InitFromFile(const base::FilePath& metafile_path) {
63   DCHECK(!emf_ && !hdc_);
64   emf_ = GetEnhMetaFile(metafile_path.value().c_str());
65   DCHECK(emf_);
66   return !!emf_;
67 }
68
69 bool Emf::Init() {
70   DCHECK(!emf_ && !hdc_);
71   hdc_ = CreateEnhMetaFile(nullptr, nullptr, nullptr, nullptr);
72   DCHECK(hdc_);
73   return !!hdc_;
74 }
75
76 bool Emf::InitFromData(const void* src_buffer, size_t src_buffer_size) {
77   DCHECK(!emf_ && !hdc_);
78   if (!base::IsValueInRangeForNumericType<UINT>(src_buffer_size))
79     return false;
80
81   emf_ = SetEnhMetaFileBits(static_cast<UINT>(src_buffer_size),
82                             reinterpret_cast<const BYTE*>(src_buffer));
83   return !!emf_;
84 }
85
86 bool Emf::FinishDocument() {
87   DCHECK(!emf_ && hdc_);
88   emf_ = CloseEnhMetaFile(hdc_);
89   DCHECK(emf_);
90   hdc_ = nullptr;
91   return !!emf_;
92 }
93
94 bool Emf::Playback(HDC hdc, const RECT* rect) const {
95   DCHECK(emf_ && !hdc_);
96   RECT bounds;
97   if (!rect) {
98     // Get the natural bounds of the EMF buffer.
99     bounds = GetPageBounds(1).ToRECT();
100     rect = &bounds;
101   }
102   return PlayEnhMetaFile(hdc, emf_, rect) != 0;
103 }
104
105 bool Emf::SafePlayback(HDC context) const {
106   DCHECK(emf_ && !hdc_);
107   XFORM base_matrix;
108   if (!GetWorldTransform(context, &base_matrix)) {
109     NOTREACHED();
110     return false;
111   }
112   Emf::EnumerationContext playback_context;
113   playback_context.base_matrix = &base_matrix;
114   gfx::Rect bound = GetPageBounds(1);
115   RECT rect = bound.ToRECT();
116   return bound.IsEmpty() ||
117          EnumEnhMetaFile(context,
118                          emf_,
119                          &Emf::SafePlaybackProc,
120                          reinterpret_cast<void*>(&playback_context),
121                          &rect) != 0;
122 }
123
124 gfx::Rect Emf::GetPageBounds(unsigned int page_number) const {
125   DCHECK(emf_ && !hdc_);
126   DCHECK_EQ(1U, page_number);
127   ENHMETAHEADER header;
128   if (GetEnhMetaFileHeader(emf_, sizeof(header), &header) != sizeof(header)) {
129     NOTREACHED();
130     return gfx::Rect();
131   }
132   // Add 1 to right and bottom because it's inclusive rectangle.
133   // See ENHMETAHEADER.
134   return gfx::Rect(header.rclBounds.left,
135                    header.rclBounds.top,
136                    header.rclBounds.right - header.rclBounds.left + 1,
137                    header.rclBounds.bottom - header.rclBounds.top + 1);
138 }
139
140 unsigned int Emf::GetPageCount() const {
141   return 1;
142 }
143
144 HDC Emf::context() const {
145   return hdc_;
146 }
147
148 uint32_t Emf::GetDataSize() const {
149   DCHECK(emf_ && !hdc_);
150   return GetEnhMetaFileBits(emf_, 0, nullptr);
151 }
152
153 bool Emf::GetData(void* buffer, uint32_t size) const {
154   DCHECK(emf_ && !hdc_);
155   DCHECK(buffer && size);
156   uint32_t size2 =
157       GetEnhMetaFileBits(emf_, size, reinterpret_cast<BYTE*>(buffer));
158   DCHECK(size2 == size);
159   return size2 == size && size2 != 0;
160 }
161
162 int CALLBACK Emf::SafePlaybackProc(HDC hdc,
163                                    HANDLETABLE* handle_table,
164                                    const ENHMETARECORD* record,
165                                    int objects_count,
166                                    LPARAM param) {
167   Emf::EnumerationContext* context =
168       reinterpret_cast<Emf::EnumerationContext*>(param);
169   context->handle_table = handle_table;
170   context->objects_count = objects_count;
171   context->hdc = hdc;
172   Record record_instance(record);
173   bool success = record_instance.SafePlayback(context);
174   DCHECK(success);
175   return 1;
176 }
177
178 Emf::EnumerationContext::EnumerationContext() {
179   memset(this, 0, sizeof(*this));
180 }
181
182 Emf::Record::Record(const ENHMETARECORD* record)
183     : record_(record) {
184   DCHECK(record_);
185 }
186
187 bool Emf::Record::Play(Emf::EnumerationContext* context) const {
188   return 0 != PlayEnhMetaFileRecord(context->hdc,
189                                     context->handle_table,
190                                     record_,
191                                     context->objects_count);
192 }
193
194 bool Emf::Record::SafePlayback(Emf::EnumerationContext* context) const {
195   // For EMF field description, see [MS-EMF] Enhanced Metafile Format
196   // Specification.
197   //
198   // This is the second major EMF breakage I get; the first one being
199   // SetDCBrushColor/SetDCPenColor/DC_PEN/DC_BRUSH being silently ignored.
200   //
201   // This function is the guts of the fix for bug 1186598. Some printer drivers
202   // somehow choke on certain EMF records, but calling the corresponding
203   // function directly on the printer HDC is fine. Still, playing the EMF record
204   // fails. Go figure.
205   //
206   // The main issue is that SetLayout is totally unsupported on these printers
207   // (HP 4500/4700). I used to call SetLayout and I stopped. I found out this is
208   // not sufficient because GDI32!PlayEnhMetaFile internally calls SetLayout(!)
209   // Damn.
210   //
211   // So I resorted to manually parse the EMF records and play them one by one.
212   // The issue with this method compared to using PlayEnhMetaFile to play back
213   // an EMF buffer is that the later silently fixes the matrix to take in
214   // account the matrix currently loaded at the time of the call.
215   // The matrix magic is done transparently when using PlayEnhMetaFile but since
216   // I'm processing one field at a time, I need to do the fixup myself. Note
217   // that PlayEnhMetaFileRecord doesn't fix the matrix correctly even when
218   // called inside an EnumEnhMetaFile loop. Go figure (bis).
219   //
220   // So when I see a EMR_SETWORLDTRANSFORM and EMR_MODIFYWORLDTRANSFORM, I need
221   // to fix the matrix according to the matrix previously loaded before playing
222   // back the buffer. Otherwise, the previously loaded matrix would be ignored
223   // and the EMF buffer would always be played back at its native resolution.
224   // Duh.
225   //
226   // I also use this opportunity to skip over eventual EMR_SETLAYOUT record that
227   // could remain.
228   //
229   // Another tweak we make is for JPEGs/PNGs in calls to StretchDIBits.
230   // (Our Pepper plugin code uses a JPEG). If the printer does not support
231   // JPEGs/PNGs natively we decompress the JPEG/PNG and then set it to the
232   // device.
233   // TODO(sanjeevr): We should also add JPEG/PNG support for SetSIBitsToDevice
234   //
235   // We also process any custom EMR_GDICOMMENT records which are our
236   // placeholders for StartPage and EndPage.
237   // Note: I should probably care about view ports and clipping, eventually.
238   bool res = false;
239   const XFORM* base_matrix = context->base_matrix;
240   switch (record()->iType) {
241     case EMR_STRETCHDIBITS: {
242       const EMRSTRETCHDIBITS* sdib_record =
243           reinterpret_cast<const EMRSTRETCHDIBITS*>(record());
244       const BYTE* record_start = reinterpret_cast<const BYTE *>(record());
245       const BITMAPINFOHEADER* bmih = reinterpret_cast<const BITMAPINFOHEADER*>(
246           record_start + sdib_record->offBmiSrc);
247       const BYTE* bits = record_start + sdib_record->offBitsSrc;
248       bool play_normally = true;
249       res = false;
250       HDC hdc = context->hdc;
251       std::unique_ptr<SkBitmap> bitmap;
252       if (bmih->biCompression == BI_JPEG) {
253         if (!DIBFormatNativelySupported(hdc, CHECKJPEGFORMAT, bits,
254                                         bmih->biSizeImage)) {
255           play_normally = false;
256           bitmap = gfx::JPEGCodec::Decode(bits, bmih->biSizeImage);
257         }
258       } else if (bmih->biCompression == BI_PNG) {
259         if (!DIBFormatNativelySupported(hdc, CHECKPNGFORMAT, bits,
260                                         bmih->biSizeImage)) {
261           play_normally = false;
262           bitmap = std::make_unique<SkBitmap>();
263           gfx::PNGCodec::Decode(bits, bmih->biSizeImage, bitmap.get());
264         }
265       }
266       if (play_normally) {
267         res = Play(context);
268       } else {
269         DCHECK(bitmap.get());
270         if (bitmap.get()) {
271           DCHECK_EQ(bitmap->colorType(), kN32_SkColorType);
272           const uint32_t* pixels =
273               static_cast<const uint32_t*>(bitmap->getPixels());
274           if (!pixels) {
275             NOTREACHED();
276             return false;
277           }
278           BITMAPINFOHEADER bmi = {0};
279           skia::CreateBitmapHeader(bitmap->width(), bitmap->height(), &bmi);
280           res = (0 != StretchDIBits(hdc, sdib_record->xDest, sdib_record->yDest,
281                                     sdib_record->cxDest,
282                                     sdib_record->cyDest, sdib_record->xSrc,
283                                     sdib_record->ySrc,
284                                     sdib_record->cxSrc, sdib_record->cySrc,
285                                     pixels,
286                                     reinterpret_cast<const BITMAPINFO *>(&bmi),
287                                     sdib_record->iUsageSrc,
288                                     sdib_record->dwRop));
289         }
290       }
291       break;
292     }
293     case EMR_SETWORLDTRANSFORM: {
294       DCHECK_EQ(record()->nSize, sizeof(DWORD) * 2 + sizeof(XFORM));
295       const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm);
296       HDC hdc = context->hdc;
297       if (base_matrix) {
298         res = 0 != SetWorldTransform(hdc, base_matrix) &&
299                    ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
300       } else {
301         res = 0 != SetWorldTransform(hdc, xform);
302       }
303       break;
304     }
305     case EMR_MODIFYWORLDTRANSFORM: {
306       DCHECK_EQ(record()->nSize,
307                 sizeof(DWORD) * 2 + sizeof(XFORM) + sizeof(DWORD));
308       const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm);
309       const DWORD* option = reinterpret_cast<const DWORD*>(xform + 1);
310       HDC hdc = context->hdc;
311       switch (*option) {
312         case MWT_IDENTITY:
313           if (base_matrix) {
314             res = 0 != SetWorldTransform(hdc, base_matrix);
315           } else {
316             res = 0 != ModifyWorldTransform(hdc, xform, MWT_IDENTITY);
317           }
318           break;
319         case MWT_LEFTMULTIPLY:
320         case MWT_RIGHTMULTIPLY:
321           res = 0 != ModifyWorldTransform(hdc, xform, *option);
322           break;
323         case 4:  // MWT_SET
324           if (base_matrix) {
325             res = 0 != SetWorldTransform(hdc, base_matrix) &&
326                        ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
327           } else {
328             res = 0 != SetWorldTransform(hdc, xform);
329           }
330           break;
331         default:
332           res = false;
333           break;
334       }
335       break;
336     }
337     case EMR_SETLAYOUT:
338       // Ignore it.
339       res = true;
340       break;
341     default: {
342       res = Play(context);
343       break;
344     }
345   }
346   return res;
347 }
348
349 void Emf::StartPage(const gfx::Size& /*page_size*/,
350                     const gfx::Rect& /*content_area*/,
351                     const float& /*scale_factor*/) {
352 }
353
354 bool Emf::FinishPage() {
355   return true;
356 }
357
358 Emf::Enumerator::Enumerator(const Emf& emf, HDC context, const RECT* rect) {
359   items_.clear();
360   if (!EnumEnhMetaFile(context,
361                        emf.emf(),
362                        &Emf::Enumerator::EnhMetaFileProc,
363                        reinterpret_cast<void*>(this),
364                        rect)) {
365     NOTREACHED();
366     items_.clear();
367   }
368   DCHECK_EQ(context_.hdc, context);
369 }
370
371 Emf::Enumerator::~Enumerator() {
372 }
373
374 Emf::Enumerator::const_iterator Emf::Enumerator::begin() const {
375   return items_.begin();
376 }
377
378 Emf::Enumerator::const_iterator Emf::Enumerator::end() const {
379   return items_.end();
380 }
381
382 int CALLBACK Emf::Enumerator::EnhMetaFileProc(HDC hdc,
383                                               HANDLETABLE* handle_table,
384                                               const ENHMETARECORD* record,
385                                               int objects_count,
386                                               LPARAM param) {
387   Enumerator& emf = *reinterpret_cast<Enumerator*>(param);
388   if (!emf.context_.handle_table) {
389     DCHECK(!emf.context_.handle_table);
390     DCHECK(!emf.context_.objects_count);
391     emf.context_.handle_table = handle_table;
392     emf.context_.objects_count = objects_count;
393     emf.context_.hdc = hdc;
394   } else {
395     DCHECK_EQ(emf.context_.handle_table, handle_table);
396     DCHECK_EQ(emf.context_.objects_count, objects_count);
397     DCHECK_EQ(emf.context_.hdc, hdc);
398   }
399   emf.items_.push_back(Record(record));
400   return 1;
401 }
402
403 }  // namespace printing