blob: 4238044391160ca68856883f0b3d93edaa15a8b4 [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 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.
initial.commit09911bf2008-07-26 23:55:294
[email protected]0e0fca32009-07-06 15:25:505#include "printing/emf_win.h"
initial.commit09911bf2008-07-26 23:55:296
7#include "base/gfx/rect.h"
8#include "base/logging.h"
9
[email protected]0e0fca32009-07-06 15:25:5010namespace printing {
initial.commit09911bf2008-07-26 23:55:2911
12Emf::Emf() : emf_(NULL), hdc_(NULL) {
13}
14
15Emf::~Emf() {
16 CloseEmf();
17 DCHECK(!emf_ && !hdc_);
18}
19
20bool Emf::CreateDc(HDC sibling, const RECT* rect) {
21 DCHECK(!emf_ && !hdc_);
22 hdc_ = CreateEnhMetaFile(sibling, NULL, rect, NULL);
23 DCHECK(hdc_);
24 return hdc_ != NULL;
25}
26
27bool Emf::CreateFromData(const void* buffer, size_t size) {
28 DCHECK(!emf_ && !hdc_);
29 emf_ = SetEnhMetaFileBits(static_cast<unsigned>(size),
30 reinterpret_cast<const BYTE*>(buffer));
31 DCHECK(emf_);
32 return emf_ != NULL;
33}
34
35bool Emf::CloseDc() {
36 DCHECK(!emf_ && hdc_);
37 emf_ = CloseEnhMetaFile(hdc_);
38 DCHECK(emf_);
39 hdc_ = NULL;
40 return emf_ != NULL;
41}
42
43void Emf::CloseEmf() {
44 DCHECK(!hdc_);
45 if (emf_) {
46 DeleteEnhMetaFile(emf_);
47 emf_ = NULL;
48 }
49}
50
51bool Emf::Playback(HDC hdc, const RECT* rect) const {
52 DCHECK(emf_ && !hdc_);
53 RECT bounds;
54 if (!rect) {
55 // Get the natural bounds of the EMF buffer.
56 bounds = GetBounds().ToRECT();
57 rect = &bounds;
58 }
59 return PlayEnhMetaFile(hdc, emf_, rect) != 0;
60}
61
62bool Emf::SafePlayback(HDC context) const {
63 DCHECK(emf_ && !hdc_);
64 XFORM base_matrix;
65 if (!GetWorldTransform(context, &base_matrix)) {
66 NOTREACHED();
67 return false;
68 }
69
70 return EnumEnhMetaFile(context,
71 emf_,
72 &Emf::SafePlaybackProc,
73 reinterpret_cast<void*>(&base_matrix),
74 &GetBounds().ToRECT()) != 0;
75}
76
77gfx::Rect Emf::GetBounds() const {
78 DCHECK(emf_ && !hdc_);
79 ENHMETAHEADER header;
80 if (GetEnhMetaFileHeader(emf_, sizeof(header), &header) != sizeof(header)) {
81 NOTREACHED();
82 return gfx::Rect();
83 }
84 if (header.rclBounds.left == 0 &&
85 header.rclBounds.top == 0 &&
86 header.rclBounds.right == -1 &&
87 header.rclBounds.bottom == -1) {
88 // A freshly created EMF buffer that has no drawing operation has invalid
89 // bounds. Instead of having an (0,0) size, it has a (-1,-1) size. Detect
90 // this special case and returns an empty Rect instead of an invalid one.
91 return gfx::Rect();
92 }
93 return gfx::Rect(header.rclBounds.left,
94 header.rclBounds.top,
95 header.rclBounds.right - header.rclBounds.left,
96 header.rclBounds.bottom - header.rclBounds.top);
97}
98
99unsigned Emf::GetDataSize() const {
100 DCHECK(emf_ && !hdc_);
101 return GetEnhMetaFileBits(emf_, 0, NULL);
102}
103
104bool Emf::GetData(void* buffer, size_t size) const {
105 DCHECK(emf_ && !hdc_);
106 DCHECK(buffer && size);
107 unsigned size2 = GetEnhMetaFileBits(emf_, static_cast<unsigned>(size),
108 reinterpret_cast<BYTE*>(buffer));
109 DCHECK(size2 == size);
110 return size2 == size && size2 != 0;
111}
112
113bool Emf::GetData(std::vector<uint8>* buffer) const {
114 unsigned size = GetDataSize();
115 if (!size)
116 return false;
117
118 buffer->resize(size);
119 if (!GetData(&buffer->front(), size))
120 return false;
121 return true;
122}
123
124bool Emf::SaveTo(const std::wstring& filename) const {
125 HANDLE file = CreateFile(filename.c_str(), GENERIC_WRITE,
126 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
127 CREATE_ALWAYS, 0, NULL);
128 if (file == INVALID_HANDLE_VALUE)
129 return false;
130
131 bool success = false;
132 std::vector<uint8> buffer;
133 if (GetData(&buffer)) {
134 DWORD written = 0;
135 if (WriteFile(file, &*buffer.begin(), static_cast<DWORD>(buffer.size()),
136 &written, NULL) &&
137 written == buffer.size()) {
138 success = true;
139 }
140 }
141 CloseHandle(file);
142 return success;
143}
144
145int CALLBACK Emf::SafePlaybackProc(HDC hdc,
146 HANDLETABLE* handle_table,
147 const ENHMETARECORD* record,
148 int objects_count,
149 LPARAM param) {
150 const XFORM* base_matrix = reinterpret_cast<const XFORM*>(param);
151 EnumerationContext context;
152 context.handle_table = handle_table;
153 context.objects_count = objects_count;
154 context.hdc = hdc;
155 Record record_instance(&context, record);
156 bool success = record_instance.SafePlayback(base_matrix);
157 DCHECK(success);
158 return 1;
159}
160
161Emf::Record::Record() {
162}
163
164Emf::Record::Record(const EnumerationContext* context,
165 const ENHMETARECORD* record)
166 : record_(record),
167 context_(context) {
168 DCHECK(record_);
169}
170
171bool Emf::Record::Play() const {
172 return 0 != PlayEnhMetaFileRecord(context_->hdc,
173 context_->handle_table,
174 record_,
175 context_->objects_count);
176}
177
178bool Emf::Record::SafePlayback(const XFORM* base_matrix) const {
179 // For EMF field description, see [MS-EMF] Enhanced Metafile Format
180 // Specification.
181 //
182 // This is the second major EMF breakage I get; the first one being
183 // SetDCBrushColor/SetDCPenColor/DC_PEN/DC_BRUSH being silently ignored.
184 //
185 // This function is the guts of the fix for bug 1186598. Some printer drivers
186 // somehow choke on certain EMF records, but calling the corresponding
187 // function directly on the printer HDC is fine. Still, playing the EMF record
188 // fails. Go figure.
189 //
190 // The main issue is that SetLayout is totally unsupported on these printers
191 // (HP 4500/4700). I used to call SetLayout and I stopped. I found out this is
192 // not sufficient because GDI32!PlayEnhMetaFile internally calls SetLayout(!)
193 // Damn.
194 //
195 // So I resorted to manually parse the EMF records and play them one by one.
196 // The issue with this method compared to using PlayEnhMetaFile to play back
197 // an EMF buffer is that the later silently fixes the matrix to take in
198 // account the matrix currently loaded at the time of the call.
199 // The matrix magic is done transparently when using PlayEnhMetaFile but since
200 // I'm processing one field at a time, I need to do the fixup myself. Note
201 // that PlayEnhMetaFileRecord doesn't fix the matrix correctly even when
202 // called inside an EnumEnhMetaFile loop. Go figure (bis).
203 //
204 // So when I see a EMR_SETWORLDTRANSFORM and EMR_MODIFYWORLDTRANSFORM, I need
205 // to fix the matrix according to the matrix previously loaded before playing
206 // back the buffer. Otherwise, the previously loaded matrix would be ignored
207 // and the EMF buffer would always be played back at its native resolution.
208 // Duh.
209 //
210 // I also use this opportunity to skip over eventual EMR_SETLAYOUT record that
211 // could remain.
212 //
213 // Note: I should probably care about view ports and clipping, eventually.
214 bool res;
215 switch (record()->iType) {
216 case EMR_SETWORLDTRANSFORM: {
217 DCHECK_EQ(record()->nSize, sizeof(DWORD) * 2 + sizeof(XFORM));
218 const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm);
219 HDC hdc = context_->hdc;
220 if (base_matrix) {
221 res = 0 != SetWorldTransform(hdc, base_matrix) &&
222 ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
223 } else {
224 res = 0 != SetWorldTransform(hdc, xform);
225 }
226 break;
227 }
228 case EMR_MODIFYWORLDTRANSFORM: {
229 DCHECK_EQ(record()->nSize,
230 sizeof(DWORD) * 2 + sizeof(XFORM) + sizeof(DWORD));
231 const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm);
232 const DWORD* option = reinterpret_cast<const DWORD*>(xform + 1);
233 HDC hdc = context_->hdc;
234 switch (*option) {
235 case MWT_IDENTITY:
236 if (base_matrix) {
237 res = 0 != SetWorldTransform(hdc, base_matrix);
238 } else {
239 res = 0 != ModifyWorldTransform(hdc, xform, MWT_IDENTITY);
240 }
241 break;
242 case MWT_LEFTMULTIPLY:
243 case MWT_RIGHTMULTIPLY:
244 res = 0 != ModifyWorldTransform(hdc, xform, *option);
245 break;
246 case 4: // MWT_SET
247 if (base_matrix) {
248 res = 0 != SetWorldTransform(hdc, base_matrix) &&
249 ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
250 } else {
251 res = 0 != SetWorldTransform(hdc, xform);
252 }
253 break;
254 default:
255 res = false;
256 break;
257 }
258 break;
259 }
260 case EMR_SETLAYOUT:
261 // Ignore it.
262 res = true;
263 break;
264 default: {
265 res = Play();
266 break;
267 }
268 }
269 return res;
270}
271
272Emf::Enumerator::Enumerator(const Emf& emf, HDC context, const RECT* rect) {
273 context_.handle_table = NULL;
274 context_.objects_count = 0;
275 context_.hdc = NULL;
276 items_.clear();
277 if (!EnumEnhMetaFile(context,
278 emf.emf(),
279 &Emf::Enumerator::EnhMetaFileProc,
280 reinterpret_cast<void*>(this),
281 rect)) {
282 NOTREACHED();
283 items_.clear();
284 }
285 DCHECK_EQ(context_.hdc, context);
286}
287
288Emf::Enumerator::const_iterator Emf::Enumerator::begin() const {
289 return items_.begin();
290}
291
292Emf::Enumerator::const_iterator Emf::Enumerator::end() const {
293 return items_.end();
294}
295
296int CALLBACK Emf::Enumerator::EnhMetaFileProc(HDC hdc,
297 HANDLETABLE* handle_table,
298 const ENHMETARECORD* record,
299 int objects_count,
300 LPARAM param) {
301 Enumerator& emf = *reinterpret_cast<Enumerator*>(param);
302 if (!emf.context_.handle_table) {
303 DCHECK(!emf.context_.handle_table);
304 DCHECK(!emf.context_.objects_count);
305 emf.context_.handle_table = handle_table;
306 emf.context_.objects_count = objects_count;
307 emf.context_.hdc = hdc;
308 } else {
309 DCHECK_EQ(emf.context_.handle_table, handle_table);
310 DCHECK_EQ(emf.context_.objects_count, objects_count);
311 DCHECK_EQ(emf.context_.hdc, hdc);
312 }
313 emf.items_.push_back(Record(&emf.context_, record));
314 return 1;
315}
316
[email protected]0e0fca32009-07-06 15:25:50317} // namespace printing