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