NaCl: disable TLS check on IRT nexe
[chromium-blink-merge.git] / printing / emf_win.cc
bloba5d4c024070042c7e8ebd02f0cd88467ca345a6a
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.
5 #include "printing/emf_win.h"
7 #include "base/file_path.h"
8 #include "base/logging.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/win/scoped_gdi_object.h"
11 #include "base/win/scoped_hdc.h"
12 #include "base/win/scoped_select_object.h"
13 #include "skia/ext/vector_platform_device_emf_win.h"
14 #include "third_party/skia/include/core/SkBitmap.h"
15 #include "ui/gfx/codec/jpeg_codec.h"
16 #include "ui/gfx/codec/png_codec.h"
17 #include "ui/gfx/gdi_util.h"
18 #include "ui/gfx/rect.h"
19 #include "ui/gfx/size.h"
21 namespace {
23 const int kCustomGdiCommentSignature = 0xdeadbabe;
24 struct PageBreakRecord {
25 int signature;
26 enum PageBreakType {
27 START_PAGE,
28 END_PAGE,
29 } type;
30 explicit PageBreakRecord(PageBreakType type_in)
31 : signature(kCustomGdiCommentSignature), type(type_in) {
33 bool IsValid() const {
34 return (signature == kCustomGdiCommentSignature) &&
35 (type >= START_PAGE) && (type <= END_PAGE);
39 int CALLBACK IsAlphaBlendUsedEnumProc(HDC,
40 HANDLETABLE*,
41 const ENHMETARECORD *record,
42 int,
43 LPARAM data) {
44 bool* result = reinterpret_cast<bool*>(data);
45 if (!result)
46 return 0;
47 switch (record->iType) {
48 case EMR_ALPHABLEND: {
49 *result = true;
50 return 0;
51 break;
54 return 1;
57 } // namespace
59 namespace printing {
61 bool DIBFormatNativelySupported(HDC dc, uint32 escape, const BYTE* bits,
62 int size) {
63 BOOL supported = FALSE;
64 if (ExtEscape(dc, QUERYESCSUPPORT, sizeof(escape),
65 reinterpret_cast<LPCSTR>(&escape), 0, 0) > 0) {
66 ExtEscape(dc, escape, size, reinterpret_cast<LPCSTR>(bits),
67 sizeof(supported), reinterpret_cast<LPSTR>(&supported));
69 return !!supported;
72 Emf::Emf() : emf_(NULL), hdc_(NULL), page_count_(0) {
75 Emf::~Emf() {
76 DCHECK(!hdc_);
77 if (emf_)
78 DeleteEnhMetaFile(emf_);
81 bool Emf::InitToFile(const FilePath& metafile_path) {
82 DCHECK(!emf_ && !hdc_);
83 hdc_ = CreateEnhMetaFile(NULL, metafile_path.value().c_str(), NULL, NULL);
84 DCHECK(hdc_);
85 return hdc_ != NULL;
88 bool Emf::InitFromFile(const FilePath& metafile_path) {
89 DCHECK(!emf_ && !hdc_);
90 emf_ = GetEnhMetaFile(metafile_path.value().c_str());
91 DCHECK(emf_);
92 return emf_ != NULL;
95 bool Emf::Init() {
96 DCHECK(!emf_ && !hdc_);
97 hdc_ = CreateEnhMetaFile(NULL, NULL, NULL, NULL);
98 DCHECK(hdc_);
99 return hdc_ != NULL;
102 bool Emf::InitFromData(const void* src_buffer, uint32 src_buffer_size) {
103 DCHECK(!emf_ && !hdc_);
104 emf_ = SetEnhMetaFileBits(src_buffer_size,
105 reinterpret_cast<const BYTE*>(src_buffer));
106 return emf_ != NULL;
109 bool Emf::FinishDocument() {
110 DCHECK(!emf_ && hdc_);
111 emf_ = CloseEnhMetaFile(hdc_);
112 DCHECK(emf_);
113 hdc_ = NULL;
114 return emf_ != NULL;
117 bool Emf::Playback(HDC hdc, const RECT* rect) const {
118 DCHECK(emf_ && !hdc_);
119 RECT bounds;
120 if (!rect) {
121 // Get the natural bounds of the EMF buffer.
122 bounds = GetPageBounds(1).ToRECT();
123 rect = &bounds;
125 return PlayEnhMetaFile(hdc, emf_, rect) != 0;
128 bool Emf::SafePlayback(HDC context) const {
129 DCHECK(emf_ && !hdc_);
130 XFORM base_matrix;
131 if (!GetWorldTransform(context, &base_matrix)) {
132 NOTREACHED();
133 return false;
135 Emf::EnumerationContext playback_context;
136 playback_context.base_matrix = &base_matrix;
137 return EnumEnhMetaFile(context,
138 emf_,
139 &Emf::SafePlaybackProc,
140 reinterpret_cast<void*>(&playback_context),
141 &GetPageBounds(1).ToRECT()) != 0;
144 gfx::Rect Emf::GetPageBounds(unsigned int page_number) const {
145 DCHECK(emf_ && !hdc_);
146 DCHECK_EQ(1U, page_number);
147 ENHMETAHEADER header;
148 if (GetEnhMetaFileHeader(emf_, sizeof(header), &header) != sizeof(header)) {
149 NOTREACHED();
150 return gfx::Rect();
152 // Add 1 to right and bottom because it's inclusive rectangle.
153 // See ENHMETAHEADER.
154 return gfx::Rect(header.rclBounds.left,
155 header.rclBounds.top,
156 header.rclBounds.right - header.rclBounds.left + 1,
157 header.rclBounds.bottom - header.rclBounds.top + 1);
160 uint32 Emf::GetDataSize() const {
161 DCHECK(emf_ && !hdc_);
162 return GetEnhMetaFileBits(emf_, 0, NULL);
165 bool Emf::GetData(void* buffer, uint32 size) const {
166 DCHECK(emf_ && !hdc_);
167 DCHECK(buffer && size);
168 uint32 size2 =
169 GetEnhMetaFileBits(emf_, size, reinterpret_cast<BYTE*>(buffer));
170 DCHECK(size2 == size);
171 return size2 == size && size2 != 0;
174 bool Emf::GetDataAsVector(std::vector<uint8>* buffer) const {
175 uint32 size = GetDataSize();
176 if (!size)
177 return false;
179 buffer->resize(size);
180 if (!GetData(&buffer->front(), size))
181 return false;
182 return true;
185 bool Emf::SaveTo(const FilePath& file_path) const {
186 HANDLE file = CreateFile(file_path.value().c_str(), GENERIC_WRITE,
187 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
188 CREATE_ALWAYS, 0, NULL);
189 if (file == INVALID_HANDLE_VALUE)
190 return false;
192 bool success = false;
193 std::vector<uint8> buffer;
194 if (GetDataAsVector(&buffer)) {
195 DWORD written = 0;
196 if (WriteFile(file, &*buffer.begin(), static_cast<DWORD>(buffer.size()),
197 &written, NULL) &&
198 written == buffer.size()) {
199 success = true;
202 CloseHandle(file);
203 return success;
206 int CALLBACK Emf::SafePlaybackProc(HDC hdc,
207 HANDLETABLE* handle_table,
208 const ENHMETARECORD* record,
209 int objects_count,
210 LPARAM param) {
211 Emf::EnumerationContext* context =
212 reinterpret_cast<Emf::EnumerationContext*>(param);
213 context->handle_table = handle_table;
214 context->objects_count = objects_count;
215 context->hdc = hdc;
216 Record record_instance(record);
217 bool success = record_instance.SafePlayback(context);
218 DCHECK(success);
219 return 1;
222 Emf::EnumerationContext::EnumerationContext() {
223 memset(this, 0, sizeof(*this));
226 Emf::Record::Record(const ENHMETARECORD* record)
227 : record_(record) {
228 DCHECK(record_);
231 bool Emf::Record::Play(Emf::EnumerationContext* context) const {
232 return 0 != PlayEnhMetaFileRecord(context->hdc,
233 context->handle_table,
234 record_,
235 context->objects_count);
238 bool Emf::Record::SafePlayback(Emf::EnumerationContext* context) const {
239 // For EMF field description, see [MS-EMF] Enhanced Metafile Format
240 // Specification.
242 // This is the second major EMF breakage I get; the first one being
243 // SetDCBrushColor/SetDCPenColor/DC_PEN/DC_BRUSH being silently ignored.
245 // This function is the guts of the fix for bug 1186598. Some printer drivers
246 // somehow choke on certain EMF records, but calling the corresponding
247 // function directly on the printer HDC is fine. Still, playing the EMF record
248 // fails. Go figure.
250 // The main issue is that SetLayout is totally unsupported on these printers
251 // (HP 4500/4700). I used to call SetLayout and I stopped. I found out this is
252 // not sufficient because GDI32!PlayEnhMetaFile internally calls SetLayout(!)
253 // Damn.
255 // So I resorted to manually parse the EMF records and play them one by one.
256 // The issue with this method compared to using PlayEnhMetaFile to play back
257 // an EMF buffer is that the later silently fixes the matrix to take in
258 // account the matrix currently loaded at the time of the call.
259 // The matrix magic is done transparently when using PlayEnhMetaFile but since
260 // I'm processing one field at a time, I need to do the fixup myself. Note
261 // that PlayEnhMetaFileRecord doesn't fix the matrix correctly even when
262 // called inside an EnumEnhMetaFile loop. Go figure (bis).
264 // So when I see a EMR_SETWORLDTRANSFORM and EMR_MODIFYWORLDTRANSFORM, I need
265 // to fix the matrix according to the matrix previously loaded before playing
266 // back the buffer. Otherwise, the previously loaded matrix would be ignored
267 // and the EMF buffer would always be played back at its native resolution.
268 // Duh.
270 // I also use this opportunity to skip over eventual EMR_SETLAYOUT record that
271 // could remain.
273 // Another tweak we make is for JPEGs/PNGs in calls to StretchDIBits.
274 // (Our Pepper plugin code uses a JPEG). If the printer does not support
275 // JPEGs/PNGs natively we decompress the JPEG/PNG and then set it to the
276 // device.
277 // TODO(sanjeevr): We should also add JPEG/PNG support for SetSIBitsToDevice
279 // We also process any custom EMR_GDICOMMENT records which are our
280 // placeholders for StartPage and EndPage.
281 // Note: I should probably care about view ports and clipping, eventually.
282 bool res = false;
283 const XFORM* base_matrix = context->base_matrix;
284 switch (record()->iType) {
285 case EMR_STRETCHDIBITS: {
286 const EMRSTRETCHDIBITS * sdib_record =
287 reinterpret_cast<const EMRSTRETCHDIBITS*>(record());
288 const BYTE* record_start = reinterpret_cast<const BYTE *>(record());
289 const BITMAPINFOHEADER *bmih =
290 reinterpret_cast<const BITMAPINFOHEADER *>(record_start +
291 sdib_record->offBmiSrc);
292 const BYTE* bits = record_start + sdib_record->offBitsSrc;
293 bool play_normally = true;
294 res = false;
295 HDC hdc = context->hdc;
296 scoped_ptr<SkBitmap> bitmap;
297 if (bmih->biCompression == BI_JPEG) {
298 if (!DIBFormatNativelySupported(hdc, CHECKJPEGFORMAT, bits,
299 bmih->biSizeImage)) {
300 play_normally = false;
301 bitmap.reset(gfx::JPEGCodec::Decode(bits, bmih->biSizeImage));
303 } else if (bmih->biCompression == BI_PNG) {
304 if (!DIBFormatNativelySupported(hdc, CHECKPNGFORMAT, bits,
305 bmih->biSizeImage)) {
306 play_normally = false;
307 bitmap.reset(new SkBitmap());
308 gfx::PNGCodec::Decode(bits, bmih->biSizeImage, bitmap.get());
311 if (!play_normally) {
312 DCHECK(bitmap.get());
313 if (bitmap.get()) {
314 SkAutoLockPixels lock(*bitmap.get());
315 DCHECK_EQ(bitmap->config(), SkBitmap::kARGB_8888_Config);
316 const uint32_t* pixels =
317 static_cast<const uint32_t*>(bitmap->getPixels());
318 if (pixels == NULL) {
319 NOTREACHED();
320 return false;
322 BITMAPINFOHEADER bmi = {0};
323 gfx::CreateBitmapHeader(bitmap->width(), bitmap->height(), &bmi);
324 res = (0 != StretchDIBits(hdc, sdib_record->xDest, sdib_record->yDest,
325 sdib_record->cxDest,
326 sdib_record->cyDest, sdib_record->xSrc,
327 sdib_record->ySrc,
328 sdib_record->cxSrc, sdib_record->cySrc,
329 pixels,
330 reinterpret_cast<const BITMAPINFO *>(&bmi),
331 sdib_record->iUsageSrc,
332 sdib_record->dwRop));
334 } else {
335 res = Play(context);
337 break;
339 case EMR_SETWORLDTRANSFORM: {
340 DCHECK_EQ(record()->nSize, sizeof(DWORD) * 2 + sizeof(XFORM));
341 const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm);
342 HDC hdc = context->hdc;
343 if (base_matrix) {
344 res = 0 != SetWorldTransform(hdc, base_matrix) &&
345 ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
346 } else {
347 res = 0 != SetWorldTransform(hdc, xform);
349 break;
351 case EMR_MODIFYWORLDTRANSFORM: {
352 DCHECK_EQ(record()->nSize,
353 sizeof(DWORD) * 2 + sizeof(XFORM) + sizeof(DWORD));
354 const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm);
355 const DWORD* option = reinterpret_cast<const DWORD*>(xform + 1);
356 HDC hdc = context->hdc;
357 switch (*option) {
358 case MWT_IDENTITY:
359 if (base_matrix) {
360 res = 0 != SetWorldTransform(hdc, base_matrix);
361 } else {
362 res = 0 != ModifyWorldTransform(hdc, xform, MWT_IDENTITY);
364 break;
365 case MWT_LEFTMULTIPLY:
366 case MWT_RIGHTMULTIPLY:
367 res = 0 != ModifyWorldTransform(hdc, xform, *option);
368 break;
369 case 4: // MWT_SET
370 if (base_matrix) {
371 res = 0 != SetWorldTransform(hdc, base_matrix) &&
372 ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
373 } else {
374 res = 0 != SetWorldTransform(hdc, xform);
376 break;
377 default:
378 res = false;
379 break;
381 break;
383 case EMR_SETLAYOUT:
384 // Ignore it.
385 res = true;
386 break;
387 case EMR_GDICOMMENT: {
388 const EMRGDICOMMENT* comment_record =
389 reinterpret_cast<const EMRGDICOMMENT*>(record());
390 if (comment_record->cbData == sizeof(PageBreakRecord)) {
391 const PageBreakRecord* page_break_record =
392 reinterpret_cast<const PageBreakRecord*>(comment_record->Data);
393 if (page_break_record && page_break_record->IsValid()) {
394 if (page_break_record->type == PageBreakRecord::START_PAGE) {
395 res = !!::StartPage(context->hdc);
396 DCHECK_EQ(0, context->dc_on_page_start);
397 context->dc_on_page_start = ::SaveDC(context->hdc);
398 } else if (page_break_record->type == PageBreakRecord::END_PAGE) {
399 DCHECK_NE(0, context->dc_on_page_start);
400 ::RestoreDC(context->hdc, context->dc_on_page_start);
401 context->dc_on_page_start = 0;
402 res = !!::EndPage(context->hdc);
403 } else {
404 res = false;
405 NOTREACHED();
407 } else {
408 res = Play(context);
410 } else {
411 res = true;
413 break;
415 default: {
416 res = Play(context);
417 break;
420 return res;
423 SkDevice* Emf::StartPageForVectorCanvas(
424 const gfx::Size& page_size, const gfx::Rect& content_area,
425 const float& scale_factor) {
426 if (!StartPage(page_size, content_area, scale_factor))
427 return NULL;
429 return skia::VectorPlatformDeviceEmf::CreateDevice(page_size.width(),
430 page_size.height(),
431 true, hdc_);
434 bool Emf::StartPage(const gfx::Size& /*page_size*/,
435 const gfx::Rect& /*content_area*/,
436 const float& /*scale_factor*/) {
437 DCHECK(hdc_);
438 if (!hdc_)
439 return false;
440 page_count_++;
441 PageBreakRecord record(PageBreakRecord::START_PAGE);
442 return !!GdiComment(hdc_, sizeof(record),
443 reinterpret_cast<const BYTE *>(&record));
446 bool Emf::FinishPage() {
447 DCHECK(hdc_);
448 if (!hdc_)
449 return false;
450 PageBreakRecord record(PageBreakRecord::END_PAGE);
451 return !!GdiComment(hdc_, sizeof(record),
452 reinterpret_cast<const BYTE *>(&record));
455 Emf::Enumerator::Enumerator(const Emf& emf, HDC context, const RECT* rect) {
456 items_.clear();
457 if (!EnumEnhMetaFile(context,
458 emf.emf(),
459 &Emf::Enumerator::EnhMetaFileProc,
460 reinterpret_cast<void*>(this),
461 rect)) {
462 NOTREACHED();
463 items_.clear();
465 DCHECK_EQ(context_.hdc, context);
468 Emf::Enumerator::const_iterator Emf::Enumerator::begin() const {
469 return items_.begin();
472 Emf::Enumerator::const_iterator Emf::Enumerator::end() const {
473 return items_.end();
476 int CALLBACK Emf::Enumerator::EnhMetaFileProc(HDC hdc,
477 HANDLETABLE* handle_table,
478 const ENHMETARECORD* record,
479 int objects_count,
480 LPARAM param) {
481 Enumerator& emf = *reinterpret_cast<Enumerator*>(param);
482 if (!emf.context_.handle_table) {
483 DCHECK(!emf.context_.handle_table);
484 DCHECK(!emf.context_.objects_count);
485 emf.context_.handle_table = handle_table;
486 emf.context_.objects_count = objects_count;
487 emf.context_.hdc = hdc;
488 } else {
489 DCHECK_EQ(emf.context_.handle_table, handle_table);
490 DCHECK_EQ(emf.context_.objects_count, objects_count);
491 DCHECK_EQ(emf.context_.hdc, hdc);
493 emf.items_.push_back(Record(record));
494 return 1;
497 bool Emf::IsAlphaBlendUsed() const {
498 bool result = false;
499 ::EnumEnhMetaFile(NULL,
500 emf(),
501 &IsAlphaBlendUsedEnumProc,
502 &result,
503 NULL);
504 return result;
507 Emf* Emf::RasterizeMetafile(int raster_area_in_pixels) const {
508 gfx::Rect page_bounds = GetPageBounds(1);
509 gfx::Size page_size(page_bounds.size());
510 if (page_size.GetArea() <= 0) {
511 NOTREACHED() << "Metafile is empty";
512 page_bounds = gfx::Rect(1, 1);
515 float scale = sqrt(float(raster_area_in_pixels) / page_size.GetArea());
516 page_size.set_width(std::max<int>(1, page_size.width() * scale));
517 page_size.set_height(std::max<int>(1, page_size.height() * scale));
519 base::win::ScopedCreateDC bitmap_dc(::CreateCompatibleDC(NULL));
520 if (!bitmap_dc) {
521 NOTREACHED() << "Bitmap DC creation failed";
522 return NULL;
524 ::SetGraphicsMode(bitmap_dc, GM_ADVANCED);
525 void* bits = NULL;
526 BITMAPINFO hdr;
527 gfx::CreateBitmapHeader(page_size.width(), page_size.height(),
528 &hdr.bmiHeader);
529 base::win::ScopedBitmap hbitmap(CreateDIBSection(
530 bitmap_dc, &hdr, DIB_RGB_COLORS, &bits, NULL, 0));
531 if (!hbitmap)
532 NOTREACHED() << "Raster bitmap creation for printing failed";
534 base::win::ScopedSelectObject selectBitmap(bitmap_dc, hbitmap);
535 RECT rect = { 0, 0, page_size.width(), page_size.height() };
536 HBRUSH white_brush = static_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH));
537 FillRect(bitmap_dc, &rect, white_brush);
539 gfx::Rect bitmap_rect(page_size);
540 Playback(bitmap_dc, &bitmap_rect.ToRECT());
542 scoped_ptr<Emf> result(new Emf);
543 result->Init();
544 HDC hdc = result->context();
545 DCHECK(hdc);
546 skia::InitializeDC(hdc);
548 // Params are ignored.
549 result->StartPage(page_bounds.size(), page_bounds, 1);
551 ::ModifyWorldTransform(hdc, NULL, MWT_IDENTITY);
552 XFORM xform = {
553 float(page_bounds.width()) / bitmap_rect.width(), 0,
554 0, float(page_bounds.height()) / bitmap_rect.height(),
555 page_bounds.x(),
556 page_bounds.y(),
558 ::SetWorldTransform(hdc, &xform);
559 ::BitBlt(hdc, 0, 0, bitmap_rect.width(), bitmap_rect.height(),
560 bitmap_dc, bitmap_rect.x(), bitmap_rect.y(), SRCCOPY);
562 result->FinishPage();
563 result->FinishDocument();
565 return result.release();
568 } // namespace printing