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