Enable password generation only if sync for passwords is enabled.
[chromium-blink-merge.git] / printing / emf_win.cc
blob984c8b398e4b959b59a599ceeded6e7a01b43c0b
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 "skia/ext/vector_platform_device_emf_win.h"
11 #include "third_party/skia/include/core/SkBitmap.h"
12 #include "ui/gfx/codec/jpeg_codec.h"
13 #include "ui/gfx/codec/png_codec.h"
14 #include "ui/gfx/gdi_util.h"
15 #include "ui/gfx/rect.h"
16 #include "ui/gfx/size.h"
18 namespace {
19 const int kCustomGdiCommentSignature = 0xdeadbabe;
20 struct PageBreakRecord {
21 int signature;
22 enum PageBreakType {
23 START_PAGE,
24 END_PAGE,
25 } type;
26 explicit PageBreakRecord(PageBreakType type_in)
27 : signature(kCustomGdiCommentSignature), type(type_in) {
29 bool IsValid() const {
30 return (signature == kCustomGdiCommentSignature) &&
31 (type >= START_PAGE) && (type <= END_PAGE);
36 namespace printing {
38 bool DIBFormatNativelySupported(HDC dc, uint32 escape, const BYTE* bits,
39 int size) {
40 BOOL supported = FALSE;
41 if (ExtEscape(dc, QUERYESCSUPPORT, sizeof(escape),
42 reinterpret_cast<LPCSTR>(&escape), 0, 0) > 0) {
43 ExtEscape(dc, escape, size, reinterpret_cast<LPCSTR>(bits),
44 sizeof(supported), reinterpret_cast<LPSTR>(&supported));
46 return !!supported;
49 Emf::Emf() : emf_(NULL), hdc_(NULL), page_count_(0) {
52 Emf::~Emf() {
53 DCHECK(!hdc_);
54 if (emf_)
55 DeleteEnhMetaFile(emf_);
58 bool Emf::InitToFile(const FilePath& metafile_path) {
59 DCHECK(!emf_ && !hdc_);
60 hdc_ = CreateEnhMetaFile(NULL, metafile_path.value().c_str(), NULL, NULL);
61 DCHECK(hdc_);
62 return hdc_ != NULL;
65 bool Emf::InitFromFile(const FilePath& metafile_path) {
66 DCHECK(!emf_ && !hdc_);
67 emf_ = GetEnhMetaFile(metafile_path.value().c_str());
68 DCHECK(emf_);
69 return emf_ != NULL;
72 bool Emf::Init() {
73 DCHECK(!emf_ && !hdc_);
74 hdc_ = CreateEnhMetaFile(NULL, NULL, NULL, NULL);
75 DCHECK(hdc_);
76 return hdc_ != NULL;
79 bool Emf::InitFromData(const void* src_buffer, uint32 src_buffer_size) {
80 DCHECK(!emf_ && !hdc_);
81 emf_ = SetEnhMetaFileBits(src_buffer_size,
82 reinterpret_cast<const BYTE*>(src_buffer));
83 return emf_ != NULL;
86 bool Emf::FinishDocument() {
87 DCHECK(!emf_ && hdc_);
88 emf_ = CloseEnhMetaFile(hdc_);
89 DCHECK(emf_);
90 hdc_ = NULL;
91 return emf_ != NULL;
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;
102 return PlayEnhMetaFile(hdc, emf_, rect) != 0;
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;
112 return EnumEnhMetaFile(context,
113 emf_,
114 &Emf::SafePlaybackProc,
115 reinterpret_cast<void*>(&base_matrix),
116 &GetPageBounds(1).ToRECT()) != 0;
119 gfx::Rect Emf::GetPageBounds(unsigned int page_number) const {
120 DCHECK(emf_ && !hdc_);
121 DCHECK_EQ(1U, page_number);
122 ENHMETAHEADER header;
123 if (GetEnhMetaFileHeader(emf_, sizeof(header), &header) != sizeof(header)) {
124 NOTREACHED();
125 return gfx::Rect();
127 if (header.rclBounds.left == 0 &&
128 header.rclBounds.top == 0 &&
129 header.rclBounds.right == -1 &&
130 header.rclBounds.bottom == -1) {
131 // A freshly created EMF buffer that has no drawing operation has invalid
132 // bounds. Instead of having an (0,0) size, it has a (-1,-1) size. Detect
133 // this special case and returns an empty Rect instead of an invalid one.
134 return gfx::Rect();
136 return gfx::Rect(header.rclBounds.left,
137 header.rclBounds.top,
138 header.rclBounds.right - header.rclBounds.left,
139 header.rclBounds.bottom - header.rclBounds.top);
142 uint32 Emf::GetDataSize() const {
143 DCHECK(emf_ && !hdc_);
144 return GetEnhMetaFileBits(emf_, 0, NULL);
147 bool Emf::GetData(void* buffer, uint32 size) const {
148 DCHECK(emf_ && !hdc_);
149 DCHECK(buffer && size);
150 uint32 size2 =
151 GetEnhMetaFileBits(emf_, size, reinterpret_cast<BYTE*>(buffer));
152 DCHECK(size2 == size);
153 return size2 == size && size2 != 0;
156 bool Emf::GetDataAsVector(std::vector<uint8>* buffer) const {
157 uint32 size = GetDataSize();
158 if (!size)
159 return false;
161 buffer->resize(size);
162 if (!GetData(&buffer->front(), size))
163 return false;
164 return true;
167 bool Emf::SaveTo(const FilePath& file_path) const {
168 HANDLE file = CreateFile(file_path.value().c_str(), GENERIC_WRITE,
169 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
170 CREATE_ALWAYS, 0, NULL);
171 if (file == INVALID_HANDLE_VALUE)
172 return false;
174 bool success = false;
175 std::vector<uint8> buffer;
176 if (GetDataAsVector(&buffer)) {
177 DWORD written = 0;
178 if (WriteFile(file, &*buffer.begin(), static_cast<DWORD>(buffer.size()),
179 &written, NULL) &&
180 written == buffer.size()) {
181 success = true;
184 CloseHandle(file);
185 return success;
188 int CALLBACK Emf::SafePlaybackProc(HDC hdc,
189 HANDLETABLE* handle_table,
190 const ENHMETARECORD* record,
191 int objects_count,
192 LPARAM param) {
193 const XFORM* base_matrix = reinterpret_cast<const XFORM*>(param);
194 EnumerationContext context;
195 context.handle_table = handle_table;
196 context.objects_count = objects_count;
197 context.hdc = hdc;
198 Record record_instance(&context, record);
199 bool success = record_instance.SafePlayback(base_matrix);
200 DCHECK(success);
201 return 1;
204 Emf::Record::Record(const EnumerationContext* context,
205 const ENHMETARECORD* record)
206 : record_(record),
207 context_(context) {
208 DCHECK(record_);
211 bool Emf::Record::Play() const {
212 return 0 != PlayEnhMetaFileRecord(context_->hdc,
213 context_->handle_table,
214 record_,
215 context_->objects_count);
218 bool Emf::Record::SafePlayback(const XFORM* base_matrix) const {
219 // For EMF field description, see [MS-EMF] Enhanced Metafile Format
220 // Specification.
222 // This is the second major EMF breakage I get; the first one being
223 // SetDCBrushColor/SetDCPenColor/DC_PEN/DC_BRUSH being silently ignored.
225 // This function is the guts of the fix for bug 1186598. Some printer drivers
226 // somehow choke on certain EMF records, but calling the corresponding
227 // function directly on the printer HDC is fine. Still, playing the EMF record
228 // fails. Go figure.
230 // The main issue is that SetLayout is totally unsupported on these printers
231 // (HP 4500/4700). I used to call SetLayout and I stopped. I found out this is
232 // not sufficient because GDI32!PlayEnhMetaFile internally calls SetLayout(!)
233 // Damn.
235 // So I resorted to manually parse the EMF records and play them one by one.
236 // The issue with this method compared to using PlayEnhMetaFile to play back
237 // an EMF buffer is that the later silently fixes the matrix to take in
238 // account the matrix currently loaded at the time of the call.
239 // The matrix magic is done transparently when using PlayEnhMetaFile but since
240 // I'm processing one field at a time, I need to do the fixup myself. Note
241 // that PlayEnhMetaFileRecord doesn't fix the matrix correctly even when
242 // called inside an EnumEnhMetaFile loop. Go figure (bis).
244 // So when I see a EMR_SETWORLDTRANSFORM and EMR_MODIFYWORLDTRANSFORM, I need
245 // to fix the matrix according to the matrix previously loaded before playing
246 // back the buffer. Otherwise, the previously loaded matrix would be ignored
247 // and the EMF buffer would always be played back at its native resolution.
248 // Duh.
250 // I also use this opportunity to skip over eventual EMR_SETLAYOUT record that
251 // could remain.
253 // Another tweak we make is for JPEGs/PNGs in calls to StretchDIBits.
254 // (Our Pepper plugin code uses a JPEG). If the printer does not support
255 // JPEGs/PNGs natively we decompress the JPEG/PNG and then set it to the
256 // device.
257 // TODO(sanjeevr): We should also add JPEG/PNG support for SetSIBitsToDevice
259 // We also process any custom EMR_GDICOMMENT records which are our
260 // placeholders for StartPage and EndPage.
261 // Note: I should probably care about view ports and clipping, eventually.
262 bool res;
263 switch (record()->iType) {
264 case EMR_STRETCHDIBITS: {
265 const EMRSTRETCHDIBITS * sdib_record =
266 reinterpret_cast<const EMRSTRETCHDIBITS*>(record());
267 const BYTE* record_start = reinterpret_cast<const BYTE *>(record());
268 const BITMAPINFOHEADER *bmih =
269 reinterpret_cast<const BITMAPINFOHEADER *>(record_start +
270 sdib_record->offBmiSrc);
271 const BYTE* bits = record_start + sdib_record->offBitsSrc;
272 bool play_normally = true;
273 res = false;
274 HDC hdc = context_->hdc;
275 scoped_ptr<SkBitmap> bitmap;
276 if (bmih->biCompression == BI_JPEG) {
277 if (!DIBFormatNativelySupported(hdc, CHECKJPEGFORMAT, bits,
278 bmih->biSizeImage)) {
279 play_normally = false;
280 bitmap.reset(gfx::JPEGCodec::Decode(bits, bmih->biSizeImage));
282 } else if (bmih->biCompression == BI_PNG) {
283 if (!DIBFormatNativelySupported(hdc, CHECKPNGFORMAT, bits,
284 bmih->biSizeImage)) {
285 play_normally = false;
286 bitmap.reset(new SkBitmap());
287 gfx::PNGCodec::Decode(bits, bmih->biSizeImage, bitmap.get());
290 if (!play_normally) {
291 DCHECK(bitmap.get());
292 if (bitmap.get()) {
293 SkAutoLockPixels lock(*bitmap.get());
294 DCHECK_EQ(bitmap->config(), SkBitmap::kARGB_8888_Config);
295 const uint32_t* pixels =
296 static_cast<const uint32_t*>(bitmap->getPixels());
297 if (pixels == NULL) {
298 NOTREACHED();
299 return false;
301 BITMAPINFOHEADER bmi = {0};
302 gfx::CreateBitmapHeader(bitmap->width(), bitmap->height(), &bmi);
303 res = (0 != StretchDIBits(hdc, sdib_record->xDest, sdib_record->yDest,
304 sdib_record->cxDest,
305 sdib_record->cyDest, sdib_record->xSrc,
306 sdib_record->ySrc,
307 sdib_record->cxSrc, sdib_record->cySrc,
308 pixels,
309 reinterpret_cast<const BITMAPINFO *>(&bmi),
310 sdib_record->iUsageSrc,
311 sdib_record->dwRop));
313 } else {
314 res = Play();
316 break;
318 case EMR_SETWORLDTRANSFORM: {
319 DCHECK_EQ(record()->nSize, sizeof(DWORD) * 2 + sizeof(XFORM));
320 const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm);
321 HDC hdc = context_->hdc;
322 if (base_matrix) {
323 res = 0 != SetWorldTransform(hdc, base_matrix) &&
324 ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
325 } else {
326 res = 0 != SetWorldTransform(hdc, xform);
328 break;
330 case EMR_MODIFYWORLDTRANSFORM: {
331 DCHECK_EQ(record()->nSize,
332 sizeof(DWORD) * 2 + sizeof(XFORM) + sizeof(DWORD));
333 const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm);
334 const DWORD* option = reinterpret_cast<const DWORD*>(xform + 1);
335 HDC hdc = context_->hdc;
336 switch (*option) {
337 case MWT_IDENTITY:
338 if (base_matrix) {
339 res = 0 != SetWorldTransform(hdc, base_matrix);
340 } else {
341 res = 0 != ModifyWorldTransform(hdc, xform, MWT_IDENTITY);
343 break;
344 case MWT_LEFTMULTIPLY:
345 case MWT_RIGHTMULTIPLY:
346 res = 0 != ModifyWorldTransform(hdc, xform, *option);
347 break;
348 case 4: // MWT_SET
349 if (base_matrix) {
350 res = 0 != SetWorldTransform(hdc, base_matrix) &&
351 ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
352 } else {
353 res = 0 != SetWorldTransform(hdc, xform);
355 break;
356 default:
357 res = false;
358 break;
360 break;
362 case EMR_SETLAYOUT:
363 // Ignore it.
364 res = true;
365 break;
366 case EMR_GDICOMMENT: {
367 const EMRGDICOMMENT* comment_record =
368 reinterpret_cast<const EMRGDICOMMENT*>(record());
369 if (comment_record->cbData == sizeof(PageBreakRecord)) {
370 const PageBreakRecord* page_break_record =
371 reinterpret_cast<const PageBreakRecord*>(comment_record->Data);
372 if (page_break_record && page_break_record->IsValid()) {
373 if (page_break_record->type == PageBreakRecord::START_PAGE) {
374 res = !!::StartPage(context_->hdc);
375 } else if (page_break_record->type == PageBreakRecord::END_PAGE) {
376 res = !!::EndPage(context_->hdc);
377 } else {
378 res = false;
379 NOTREACHED();
381 } else {
382 res = Play();
384 } else {
385 res = true;
387 break;
389 default: {
390 res = Play();
391 break;
394 return res;
397 SkDevice* Emf::StartPageForVectorCanvas(
398 const gfx::Size& page_size, const gfx::Rect& content_area,
399 const float& scale_factor) {
400 if (!StartPage(page_size, content_area, scale_factor))
401 return NULL;
403 return skia::VectorPlatformDeviceEmf::CreateDevice(page_size.width(),
404 page_size.height(),
405 true, hdc_);
408 bool Emf::StartPage(const gfx::Size& /*page_size*/,
409 const gfx::Rect& /*content_area*/,
410 const float& /*scale_factor*/) {
411 DCHECK(hdc_);
412 if (!hdc_)
413 return false;
414 page_count_++;
415 PageBreakRecord record(PageBreakRecord::START_PAGE);
416 return !!GdiComment(hdc_, sizeof(record),
417 reinterpret_cast<const BYTE *>(&record));
420 bool Emf::FinishPage() {
421 DCHECK(hdc_);
422 if (!hdc_)
423 return false;
424 PageBreakRecord record(PageBreakRecord::END_PAGE);
425 return !!GdiComment(hdc_, sizeof(record),
426 reinterpret_cast<const BYTE *>(&record));
429 Emf::Enumerator::Enumerator(const Emf& emf, HDC context, const RECT* rect) {
430 context_.handle_table = NULL;
431 context_.objects_count = 0;
432 context_.hdc = NULL;
433 items_.clear();
434 if (!EnumEnhMetaFile(context,
435 emf.emf(),
436 &Emf::Enumerator::EnhMetaFileProc,
437 reinterpret_cast<void*>(this),
438 rect)) {
439 NOTREACHED();
440 items_.clear();
442 DCHECK_EQ(context_.hdc, context);
445 Emf::Enumerator::const_iterator Emf::Enumerator::begin() const {
446 return items_.begin();
449 Emf::Enumerator::const_iterator Emf::Enumerator::end() const {
450 return items_.end();
453 int CALLBACK Emf::Enumerator::EnhMetaFileProc(HDC hdc,
454 HANDLETABLE* handle_table,
455 const ENHMETARECORD* record,
456 int objects_count,
457 LPARAM param) {
458 Enumerator& emf = *reinterpret_cast<Enumerator*>(param);
459 if (!emf.context_.handle_table) {
460 DCHECK(!emf.context_.handle_table);
461 DCHECK(!emf.context_.objects_count);
462 emf.context_.handle_table = handle_table;
463 emf.context_.objects_count = objects_count;
464 emf.context_.hdc = hdc;
465 } else {
466 DCHECK_EQ(emf.context_.handle_table, handle_table);
467 DCHECK_EQ(emf.context_.objects_count, objects_count);
468 DCHECK_EQ(emf.context_.hdc, hdc);
470 emf.items_.push_back(Record(&emf.context_, record));
471 return 1;
474 } // namespace printing