[email protected] | f53351f | 2012-02-23 03:28:30 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
license.bot | bf09a50 | 2008-08-24 00:55:55 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 4 | |
[email protected] | 0e0fca3 | 2009-07-06 15:25:50 | [diff] [blame] | 5 | #include "printing/emf_win.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 6 | |
[email protected] | e6cddc57 | 2010-09-29 21:39:45 | [diff] [blame] | 7 | #include "base/file_path.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 8 | #include "base/logging.h" |
[email protected] | 3b63f8f4 | 2011-03-28 01:54:15 | [diff] [blame] | 9 | #include "base/memory/scoped_ptr.h" |
[email protected] | 60d77bd | 2012-08-22 00:10:07 | [diff] [blame] | 10 | #include "base/win/scoped_gdi_object.h" |
| 11 | #include "base/win/scoped_hdc.h" |
| 12 | #include "base/win/scoped_select_object.h" |
[email protected] | 9b61753 | 2011-04-04 23:15:06 | [diff] [blame] | 13 | #include "skia/ext/vector_platform_device_emf_win.h" |
[email protected] | 38bba4f | 2010-03-12 05:29:07 | [diff] [blame] | 14 | #include "third_party/skia/include/core/SkBitmap.h" |
[email protected] | 08397d5 | 2011-02-05 01:53:38 | [diff] [blame] | 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" |
[email protected] | edc531f9 | 2011-03-18 17:52:23 | [diff] [blame] | 19 | #include "ui/gfx/size.h" |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 20 | |
[email protected] | 2aa8e18 | 2010-07-12 16:25:04 | [diff] [blame] | 21 | namespace { |
[email protected] | 60d77bd | 2012-08-22 00:10:07 | [diff] [blame] | 22 | |
[email protected] | 2aa8e18 | 2010-07-12 16:25:04 | [diff] [blame] | 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) { |
| 32 | } |
| 33 | bool IsValid() const { |
| 34 | return (signature == kCustomGdiCommentSignature) && |
| 35 | (type >= START_PAGE) && (type <= END_PAGE); |
| 36 | } |
| 37 | }; |
[email protected] | 60d77bd | 2012-08-22 00:10:07 | [diff] [blame] | 38 | |
| 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; |
| 52 | } |
| 53 | } |
| 54 | return 1; |
[email protected] | 2aa8e18 | 2010-07-12 16:25:04 | [diff] [blame] | 55 | } |
| 56 | |
[email protected] | 64f5f92 | 2012-10-13 04:50:06 | [diff] [blame] | 57 | int CALLBACK RasterizeAlphaBlendProc(HDC metafile_dc, |
| 58 | HANDLETABLE* handle_table, |
| 59 | const ENHMETARECORD *record, |
| 60 | int num_objects, |
| 61 | LPARAM data) { |
| 62 | HDC bitmap_dc = *reinterpret_cast<HDC*>(data); |
| 63 | // Play this command to the bitmap DC. |
| 64 | ::PlayEnhMetaFileRecord(bitmap_dc, handle_table, record, num_objects); |
| 65 | switch (record->iType) { |
| 66 | case EMR_ALPHABLEND: { |
| 67 | const EMRALPHABLEND* alpha_blend = |
| 68 | reinterpret_cast<const EMRALPHABLEND*>(record); |
| 69 | // Don't modify transformation here. |
| 70 | // Old implementation did reset transformations for DC to identity matrix. |
| 71 | // That was not correct and cause some bugs, like unexpected cropping. |
| 72 | // EMRALPHABLEND is rendered into bitmap and metafile contexts with |
| 73 | // current transformation. If we don't touch them here BitBlt will copy |
| 74 | // same areas. |
| 75 | ::BitBlt(metafile_dc, |
| 76 | alpha_blend->xDest, |
| 77 | alpha_blend->yDest, |
| 78 | alpha_blend->cxDest, |
| 79 | alpha_blend->cyDest, |
| 80 | bitmap_dc, |
| 81 | alpha_blend->xDest, |
| 82 | alpha_blend->yDest, |
| 83 | SRCCOPY); |
| 84 | break; |
| 85 | } |
| 86 | case EMR_CREATEBRUSHINDIRECT: |
| 87 | case EMR_CREATECOLORSPACE: |
| 88 | case EMR_CREATECOLORSPACEW: |
| 89 | case EMR_CREATEDIBPATTERNBRUSHPT: |
| 90 | case EMR_CREATEMONOBRUSH: |
| 91 | case EMR_CREATEPALETTE: |
| 92 | case EMR_CREATEPEN: |
| 93 | case EMR_DELETECOLORSPACE: |
| 94 | case EMR_DELETEOBJECT: |
| 95 | case EMR_EXTCREATEFONTINDIRECTW: |
| 96 | // Play object creation command only once. |
| 97 | break; |
| 98 | |
| 99 | default: |
| 100 | // Play this command to the metafile DC. |
| 101 | ::PlayEnhMetaFileRecord(metafile_dc, handle_table, record, num_objects); |
| 102 | break; |
| 103 | } |
| 104 | return 1; // Continue enumeration |
| 105 | } |
| 106 | |
| 107 | // Bitmapt for rasterization. |
| 108 | class RasterBitmap { |
| 109 | public: |
| 110 | explicit RasterBitmap(const gfx::Size& raster_size) |
| 111 | : saved_object_(NULL) { |
| 112 | context_.Set(::CreateCompatibleDC(NULL)); |
| 113 | if (!context_) { |
| 114 | NOTREACHED() << "Bitmap DC creation failed"; |
| 115 | return; |
| 116 | } |
| 117 | ::SetGraphicsMode(context_, GM_ADVANCED); |
| 118 | void* bits = NULL; |
| 119 | gfx::Rect bitmap_rect(raster_size); |
| 120 | gfx::CreateBitmapHeader(raster_size.width(), raster_size.height(), |
| 121 | &header_.bmiHeader); |
| 122 | bitmap_.Set(::CreateDIBSection(context_, &header_, DIB_RGB_COLORS, &bits, |
| 123 | NULL, 0)); |
| 124 | if (!bitmap_) |
| 125 | NOTREACHED() << "Raster bitmap creation for printing failed"; |
| 126 | |
| 127 | saved_object_ = ::SelectObject(context_, bitmap_); |
| 128 | ::FillRect(context_, &bitmap_rect.ToRECT(), |
| 129 | static_cast<HBRUSH>(::GetStockObject(WHITE_BRUSH))); |
| 130 | |
| 131 | } |
| 132 | |
| 133 | ~RasterBitmap() { |
| 134 | ::SelectObject(context_, saved_object_); |
| 135 | } |
| 136 | |
| 137 | HDC context() const { |
| 138 | return context_; |
| 139 | } |
| 140 | |
| 141 | base::win::ScopedCreateDC context_; |
| 142 | BITMAPINFO header_; |
| 143 | base::win::ScopedBitmap bitmap_; |
| 144 | HGDIOBJ saved_object_; |
| 145 | |
| 146 | private: |
| 147 | DISALLOW_COPY_AND_ASSIGN(RasterBitmap); |
| 148 | }; |
| 149 | |
| 150 | |
| 151 | |
[email protected] | 60d77bd | 2012-08-22 00:10:07 | [diff] [blame] | 152 | } // namespace |
| 153 | |
[email protected] | 0e0fca3 | 2009-07-06 15:25:50 | [diff] [blame] | 154 | namespace printing { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 155 | |
[email protected] | 38bba4f | 2010-03-12 05:29:07 | [diff] [blame] | 156 | bool DIBFormatNativelySupported(HDC dc, uint32 escape, const BYTE* bits, |
| 157 | int size) { |
| 158 | BOOL supported = FALSE; |
| 159 | if (ExtEscape(dc, QUERYESCSUPPORT, sizeof(escape), |
| 160 | reinterpret_cast<LPCSTR>(&escape), 0, 0) > 0) { |
| 161 | ExtEscape(dc, escape, size, reinterpret_cast<LPCSTR>(bits), |
| 162 | sizeof(supported), reinterpret_cast<LPSTR>(&supported)); |
| 163 | } |
| 164 | return !!supported; |
| 165 | } |
| 166 | |
[email protected] | 830cf74 | 2011-04-01 16:06:25 | [diff] [blame] | 167 | Emf::Emf() : emf_(NULL), hdc_(NULL), page_count_(0) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 168 | } |
| 169 | |
| 170 | Emf::~Emf() { |
[email protected] | 481edc5 | 2011-03-22 06:36:58 | [diff] [blame] | 171 | DCHECK(!hdc_); |
| 172 | if (emf_) |
| 173 | DeleteEnhMetaFile(emf_); |
| 174 | } |
| 175 | |
| 176 | bool Emf::InitToFile(const FilePath& metafile_path) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 177 | DCHECK(!emf_ && !hdc_); |
[email protected] | 481edc5 | 2011-03-22 06:36:58 | [diff] [blame] | 178 | hdc_ = CreateEnhMetaFile(NULL, metafile_path.value().c_str(), NULL, NULL); |
| 179 | DCHECK(hdc_); |
| 180 | return hdc_ != NULL; |
| 181 | } |
| 182 | |
| 183 | bool Emf::InitFromFile(const FilePath& metafile_path) { |
| 184 | DCHECK(!emf_ && !hdc_); |
| 185 | emf_ = GetEnhMetaFile(metafile_path.value().c_str()); |
| 186 | DCHECK(emf_); |
| 187 | return emf_ != NULL; |
| 188 | } |
| 189 | |
| 190 | bool Emf::Init() { |
| 191 | DCHECK(!emf_ && !hdc_); |
| 192 | hdc_ = CreateEnhMetaFile(NULL, NULL, NULL, NULL); |
| 193 | DCHECK(hdc_); |
| 194 | return hdc_ != NULL; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 195 | } |
| 196 | |
[email protected] | 89eff96a | 2011-03-17 23:22:06 | [diff] [blame] | 197 | bool Emf::InitFromData(const void* src_buffer, uint32 src_buffer_size) { |
[email protected] | e8980a5 | 2010-10-07 20:11:24 | [diff] [blame] | 198 | DCHECK(!emf_ && !hdc_); |
| 199 | emf_ = SetEnhMetaFileBits(src_buffer_size, |
| 200 | reinterpret_cast<const BYTE*>(src_buffer)); |
[email protected] | e8980a5 | 2010-10-07 20:11:24 | [diff] [blame] | 201 | return emf_ != NULL; |
| 202 | } |
| 203 | |
[email protected] | cdd19f5 | 2011-03-19 01:04:57 | [diff] [blame] | 204 | bool Emf::FinishDocument() { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 205 | DCHECK(!emf_ && hdc_); |
| 206 | emf_ = CloseEnhMetaFile(hdc_); |
| 207 | DCHECK(emf_); |
| 208 | hdc_ = NULL; |
| 209 | return emf_ != NULL; |
| 210 | } |
| 211 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 212 | bool Emf::Playback(HDC hdc, const RECT* rect) const { |
| 213 | DCHECK(emf_ && !hdc_); |
| 214 | RECT bounds; |
| 215 | if (!rect) { |
| 216 | // Get the natural bounds of the EMF buffer. |
[email protected] | 8f17cd3e | 2011-03-16 01:39:42 | [diff] [blame] | 217 | bounds = GetPageBounds(1).ToRECT(); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 218 | rect = &bounds; |
| 219 | } |
| 220 | return PlayEnhMetaFile(hdc, emf_, rect) != 0; |
| 221 | } |
| 222 | |
| 223 | bool Emf::SafePlayback(HDC context) const { |
| 224 | DCHECK(emf_ && !hdc_); |
| 225 | XFORM base_matrix; |
| 226 | if (!GetWorldTransform(context, &base_matrix)) { |
| 227 | NOTREACHED(); |
| 228 | return false; |
| 229 | } |
[email protected] | 0064b39 | 2012-06-20 01:57:38 | [diff] [blame] | 230 | Emf::EnumerationContext playback_context; |
| 231 | playback_context.base_matrix = &base_matrix; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 232 | return EnumEnhMetaFile(context, |
| 233 | emf_, |
| 234 | &Emf::SafePlaybackProc, |
[email protected] | 0064b39 | 2012-06-20 01:57:38 | [diff] [blame] | 235 | reinterpret_cast<void*>(&playback_context), |
[email protected] | 8f17cd3e | 2011-03-16 01:39:42 | [diff] [blame] | 236 | &GetPageBounds(1).ToRECT()) != 0; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 237 | } |
| 238 | |
[email protected] | 8f17cd3e | 2011-03-16 01:39:42 | [diff] [blame] | 239 | gfx::Rect Emf::GetPageBounds(unsigned int page_number) const { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 240 | DCHECK(emf_ && !hdc_); |
[email protected] | 8f17cd3e | 2011-03-16 01:39:42 | [diff] [blame] | 241 | DCHECK_EQ(1U, page_number); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 242 | ENHMETAHEADER header; |
| 243 | if (GetEnhMetaFileHeader(emf_, sizeof(header), &header) != sizeof(header)) { |
| 244 | NOTREACHED(); |
| 245 | return gfx::Rect(); |
| 246 | } |
[email protected] | 60d77bd | 2012-08-22 00:10:07 | [diff] [blame] | 247 | // Add 1 to right and bottom because it's inclusive rectangle. |
| 248 | // See ENHMETAHEADER. |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 249 | return gfx::Rect(header.rclBounds.left, |
| 250 | header.rclBounds.top, |
[email protected] | 60d77bd | 2012-08-22 00:10:07 | [diff] [blame] | 251 | header.rclBounds.right - header.rclBounds.left + 1, |
| 252 | header.rclBounds.bottom - header.rclBounds.top + 1); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 253 | } |
| 254 | |
[email protected] | b5ab398 | 2010-02-16 23:58:27 | [diff] [blame] | 255 | uint32 Emf::GetDataSize() const { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 256 | DCHECK(emf_ && !hdc_); |
| 257 | return GetEnhMetaFileBits(emf_, 0, NULL); |
| 258 | } |
| 259 | |
[email protected] | b5ab398 | 2010-02-16 23:58:27 | [diff] [blame] | 260 | bool Emf::GetData(void* buffer, uint32 size) const { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 261 | DCHECK(emf_ && !hdc_); |
| 262 | DCHECK(buffer && size); |
[email protected] | b5ab398 | 2010-02-16 23:58:27 | [diff] [blame] | 263 | uint32 size2 = |
| 264 | GetEnhMetaFileBits(emf_, size, reinterpret_cast<BYTE*>(buffer)); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 265 | DCHECK(size2 == size); |
| 266 | return size2 == size && size2 != 0; |
| 267 | } |
| 268 | |
[email protected] | 96fddd8 | 2011-03-24 23:37:45 | [diff] [blame] | 269 | bool Emf::GetDataAsVector(std::vector<uint8>* buffer) const { |
[email protected] | b5ab398 | 2010-02-16 23:58:27 | [diff] [blame] | 270 | uint32 size = GetDataSize(); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 271 | if (!size) |
| 272 | return false; |
| 273 | |
| 274 | buffer->resize(size); |
| 275 | if (!GetData(&buffer->front(), size)) |
| 276 | return false; |
| 277 | return true; |
| 278 | } |
| 279 | |
[email protected] | 8f17cd3e | 2011-03-16 01:39:42 | [diff] [blame] | 280 | bool Emf::SaveTo(const FilePath& file_path) const { |
| 281 | HANDLE file = CreateFile(file_path.value().c_str(), GENERIC_WRITE, |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 282 | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, |
| 283 | CREATE_ALWAYS, 0, NULL); |
| 284 | if (file == INVALID_HANDLE_VALUE) |
| 285 | return false; |
| 286 | |
| 287 | bool success = false; |
| 288 | std::vector<uint8> buffer; |
[email protected] | 96fddd8 | 2011-03-24 23:37:45 | [diff] [blame] | 289 | if (GetDataAsVector(&buffer)) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 290 | DWORD written = 0; |
| 291 | if (WriteFile(file, &*buffer.begin(), static_cast<DWORD>(buffer.size()), |
| 292 | &written, NULL) && |
| 293 | written == buffer.size()) { |
| 294 | success = true; |
| 295 | } |
| 296 | } |
| 297 | CloseHandle(file); |
| 298 | return success; |
| 299 | } |
| 300 | |
| 301 | int CALLBACK Emf::SafePlaybackProc(HDC hdc, |
| 302 | HANDLETABLE* handle_table, |
| 303 | const ENHMETARECORD* record, |
| 304 | int objects_count, |
| 305 | LPARAM param) { |
[email protected] | 0064b39 | 2012-06-20 01:57:38 | [diff] [blame] | 306 | Emf::EnumerationContext* context = |
| 307 | reinterpret_cast<Emf::EnumerationContext*>(param); |
| 308 | context->handle_table = handle_table; |
| 309 | context->objects_count = objects_count; |
| 310 | context->hdc = hdc; |
| 311 | Record record_instance(record); |
| 312 | bool success = record_instance.SafePlayback(context); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 313 | DCHECK(success); |
| 314 | return 1; |
| 315 | } |
| 316 | |
[email protected] | 0064b39 | 2012-06-20 01:57:38 | [diff] [blame] | 317 | Emf::EnumerationContext::EnumerationContext() { |
| 318 | memset(this, 0, sizeof(*this)); |
| 319 | } |
| 320 | |
| 321 | Emf::Record::Record(const ENHMETARECORD* record) |
| 322 | : record_(record) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 323 | DCHECK(record_); |
| 324 | } |
| 325 | |
[email protected] | 0064b39 | 2012-06-20 01:57:38 | [diff] [blame] | 326 | bool Emf::Record::Play(Emf::EnumerationContext* context) const { |
| 327 | return 0 != PlayEnhMetaFileRecord(context->hdc, |
| 328 | context->handle_table, |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 329 | record_, |
[email protected] | 0064b39 | 2012-06-20 01:57:38 | [diff] [blame] | 330 | context->objects_count); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 331 | } |
| 332 | |
[email protected] | 0064b39 | 2012-06-20 01:57:38 | [diff] [blame] | 333 | bool Emf::Record::SafePlayback(Emf::EnumerationContext* context) const { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 334 | // For EMF field description, see [MS-EMF] Enhanced Metafile Format |
| 335 | // Specification. |
| 336 | // |
| 337 | // This is the second major EMF breakage I get; the first one being |
| 338 | // SetDCBrushColor/SetDCPenColor/DC_PEN/DC_BRUSH being silently ignored. |
| 339 | // |
| 340 | // This function is the guts of the fix for bug 1186598. Some printer drivers |
| 341 | // somehow choke on certain EMF records, but calling the corresponding |
| 342 | // function directly on the printer HDC is fine. Still, playing the EMF record |
| 343 | // fails. Go figure. |
| 344 | // |
| 345 | // The main issue is that SetLayout is totally unsupported on these printers |
| 346 | // (HP 4500/4700). I used to call SetLayout and I stopped. I found out this is |
| 347 | // not sufficient because GDI32!PlayEnhMetaFile internally calls SetLayout(!) |
| 348 | // Damn. |
| 349 | // |
| 350 | // So I resorted to manually parse the EMF records and play them one by one. |
| 351 | // The issue with this method compared to using PlayEnhMetaFile to play back |
| 352 | // an EMF buffer is that the later silently fixes the matrix to take in |
| 353 | // account the matrix currently loaded at the time of the call. |
| 354 | // The matrix magic is done transparently when using PlayEnhMetaFile but since |
| 355 | // I'm processing one field at a time, I need to do the fixup myself. Note |
| 356 | // that PlayEnhMetaFileRecord doesn't fix the matrix correctly even when |
| 357 | // called inside an EnumEnhMetaFile loop. Go figure (bis). |
| 358 | // |
| 359 | // So when I see a EMR_SETWORLDTRANSFORM and EMR_MODIFYWORLDTRANSFORM, I need |
| 360 | // to fix the matrix according to the matrix previously loaded before playing |
| 361 | // back the buffer. Otherwise, the previously loaded matrix would be ignored |
| 362 | // and the EMF buffer would always be played back at its native resolution. |
| 363 | // Duh. |
| 364 | // |
| 365 | // I also use this opportunity to skip over eventual EMR_SETLAYOUT record that |
| 366 | // could remain. |
| 367 | // |
[email protected] | 38bba4f | 2010-03-12 05:29:07 | [diff] [blame] | 368 | // Another tweak we make is for JPEGs/PNGs in calls to StretchDIBits. |
| 369 | // (Our Pepper plugin code uses a JPEG). If the printer does not support |
| 370 | // JPEGs/PNGs natively we decompress the JPEG/PNG and then set it to the |
| 371 | // device. |
| 372 | // TODO(sanjeevr): We should also add JPEG/PNG support for SetSIBitsToDevice |
| 373 | // |
[email protected] | 2aa8e18 | 2010-07-12 16:25:04 | [diff] [blame] | 374 | // We also process any custom EMR_GDICOMMENT records which are our |
| 375 | // placeholders for StartPage and EndPage. |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 376 | // Note: I should probably care about view ports and clipping, eventually. |
[email protected] | 0064b39 | 2012-06-20 01:57:38 | [diff] [blame] | 377 | bool res = false; |
| 378 | const XFORM* base_matrix = context->base_matrix; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 379 | switch (record()->iType) { |
[email protected] | 38bba4f | 2010-03-12 05:29:07 | [diff] [blame] | 380 | case EMR_STRETCHDIBITS: { |
| 381 | const EMRSTRETCHDIBITS * sdib_record = |
| 382 | reinterpret_cast<const EMRSTRETCHDIBITS*>(record()); |
| 383 | const BYTE* record_start = reinterpret_cast<const BYTE *>(record()); |
| 384 | const BITMAPINFOHEADER *bmih = |
| 385 | reinterpret_cast<const BITMAPINFOHEADER *>(record_start + |
| 386 | sdib_record->offBmiSrc); |
| 387 | const BYTE* bits = record_start + sdib_record->offBitsSrc; |
| 388 | bool play_normally = true; |
| 389 | res = false; |
[email protected] | 0064b39 | 2012-06-20 01:57:38 | [diff] [blame] | 390 | HDC hdc = context->hdc; |
[email protected] | 38bba4f | 2010-03-12 05:29:07 | [diff] [blame] | 391 | scoped_ptr<SkBitmap> bitmap; |
| 392 | if (bmih->biCompression == BI_JPEG) { |
| 393 | if (!DIBFormatNativelySupported(hdc, CHECKJPEGFORMAT, bits, |
| 394 | bmih->biSizeImage)) { |
| 395 | play_normally = false; |
[email protected] | 38bba4f | 2010-03-12 05:29:07 | [diff] [blame] | 396 | bitmap.reset(gfx::JPEGCodec::Decode(bits, bmih->biSizeImage)); |
[email protected] | 38bba4f | 2010-03-12 05:29:07 | [diff] [blame] | 397 | } |
| 398 | } else if (bmih->biCompression == BI_PNG) { |
| 399 | if (!DIBFormatNativelySupported(hdc, CHECKPNGFORMAT, bits, |
| 400 | bmih->biSizeImage)) { |
| 401 | play_normally = false; |
| 402 | bitmap.reset(new SkBitmap()); |
[email protected] | 38bba4f | 2010-03-12 05:29:07 | [diff] [blame] | 403 | gfx::PNGCodec::Decode(bits, bmih->biSizeImage, bitmap.get()); |
[email protected] | 38bba4f | 2010-03-12 05:29:07 | [diff] [blame] | 404 | } |
| 405 | } |
| 406 | if (!play_normally) { |
| 407 | DCHECK(bitmap.get()); |
| 408 | if (bitmap.get()) { |
| 409 | SkAutoLockPixels lock(*bitmap.get()); |
[email protected] | 125b6200 | 2012-03-19 14:30:40 | [diff] [blame] | 410 | DCHECK_EQ(bitmap->config(), SkBitmap::kARGB_8888_Config); |
[email protected] | 38bba4f | 2010-03-12 05:29:07 | [diff] [blame] | 411 | const uint32_t* pixels = |
| 412 | static_cast<const uint32_t*>(bitmap->getPixels()); |
| 413 | if (pixels == NULL) { |
| 414 | NOTREACHED(); |
| 415 | return false; |
| 416 | } |
| 417 | BITMAPINFOHEADER bmi = {0}; |
| 418 | gfx::CreateBitmapHeader(bitmap->width(), bitmap->height(), &bmi); |
| 419 | res = (0 != StretchDIBits(hdc, sdib_record->xDest, sdib_record->yDest, |
| 420 | sdib_record->cxDest, |
| 421 | sdib_record->cyDest, sdib_record->xSrc, |
| 422 | sdib_record->ySrc, |
| 423 | sdib_record->cxSrc, sdib_record->cySrc, |
| 424 | pixels, |
| 425 | reinterpret_cast<const BITMAPINFO *>(&bmi), |
| 426 | sdib_record->iUsageSrc, |
| 427 | sdib_record->dwRop)); |
| 428 | } |
| 429 | } else { |
[email protected] | 0064b39 | 2012-06-20 01:57:38 | [diff] [blame] | 430 | res = Play(context); |
[email protected] | 38bba4f | 2010-03-12 05:29:07 | [diff] [blame] | 431 | } |
| 432 | break; |
| 433 | } |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 434 | case EMR_SETWORLDTRANSFORM: { |
| 435 | DCHECK_EQ(record()->nSize, sizeof(DWORD) * 2 + sizeof(XFORM)); |
| 436 | const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm); |
[email protected] | 0064b39 | 2012-06-20 01:57:38 | [diff] [blame] | 437 | HDC hdc = context->hdc; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 438 | if (base_matrix) { |
| 439 | res = 0 != SetWorldTransform(hdc, base_matrix) && |
| 440 | ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY); |
| 441 | } else { |
| 442 | res = 0 != SetWorldTransform(hdc, xform); |
| 443 | } |
| 444 | break; |
| 445 | } |
| 446 | case EMR_MODIFYWORLDTRANSFORM: { |
| 447 | DCHECK_EQ(record()->nSize, |
| 448 | sizeof(DWORD) * 2 + sizeof(XFORM) + sizeof(DWORD)); |
| 449 | const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm); |
| 450 | const DWORD* option = reinterpret_cast<const DWORD*>(xform + 1); |
[email protected] | 0064b39 | 2012-06-20 01:57:38 | [diff] [blame] | 451 | HDC hdc = context->hdc; |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 452 | switch (*option) { |
| 453 | case MWT_IDENTITY: |
| 454 | if (base_matrix) { |
| 455 | res = 0 != SetWorldTransform(hdc, base_matrix); |
| 456 | } else { |
| 457 | res = 0 != ModifyWorldTransform(hdc, xform, MWT_IDENTITY); |
| 458 | } |
| 459 | break; |
| 460 | case MWT_LEFTMULTIPLY: |
| 461 | case MWT_RIGHTMULTIPLY: |
| 462 | res = 0 != ModifyWorldTransform(hdc, xform, *option); |
| 463 | break; |
| 464 | case 4: // MWT_SET |
| 465 | if (base_matrix) { |
| 466 | res = 0 != SetWorldTransform(hdc, base_matrix) && |
| 467 | ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY); |
| 468 | } else { |
| 469 | res = 0 != SetWorldTransform(hdc, xform); |
| 470 | } |
| 471 | break; |
| 472 | default: |
| 473 | res = false; |
| 474 | break; |
| 475 | } |
| 476 | break; |
| 477 | } |
| 478 | case EMR_SETLAYOUT: |
| 479 | // Ignore it. |
| 480 | res = true; |
| 481 | break; |
[email protected] | 2aa8e18 | 2010-07-12 16:25:04 | [diff] [blame] | 482 | case EMR_GDICOMMENT: { |
| 483 | const EMRGDICOMMENT* comment_record = |
| 484 | reinterpret_cast<const EMRGDICOMMENT*>(record()); |
| 485 | if (comment_record->cbData == sizeof(PageBreakRecord)) { |
| 486 | const PageBreakRecord* page_break_record = |
| 487 | reinterpret_cast<const PageBreakRecord*>(comment_record->Data); |
| 488 | if (page_break_record && page_break_record->IsValid()) { |
| 489 | if (page_break_record->type == PageBreakRecord::START_PAGE) { |
[email protected] | 0064b39 | 2012-06-20 01:57:38 | [diff] [blame] | 490 | res = !!::StartPage(context->hdc); |
| 491 | DCHECK_EQ(0, context->dc_on_page_start); |
| 492 | context->dc_on_page_start = ::SaveDC(context->hdc); |
[email protected] | 2aa8e18 | 2010-07-12 16:25:04 | [diff] [blame] | 493 | } else if (page_break_record->type == PageBreakRecord::END_PAGE) { |
[email protected] | 0064b39 | 2012-06-20 01:57:38 | [diff] [blame] | 494 | DCHECK_NE(0, context->dc_on_page_start); |
| 495 | ::RestoreDC(context->hdc, context->dc_on_page_start); |
| 496 | context->dc_on_page_start = 0; |
| 497 | res = !!::EndPage(context->hdc); |
[email protected] | 2aa8e18 | 2010-07-12 16:25:04 | [diff] [blame] | 498 | } else { |
| 499 | res = false; |
| 500 | NOTREACHED(); |
| 501 | } |
| 502 | } else { |
[email protected] | 0064b39 | 2012-06-20 01:57:38 | [diff] [blame] | 503 | res = Play(context); |
[email protected] | 2aa8e18 | 2010-07-12 16:25:04 | [diff] [blame] | 504 | } |
[email protected] | b2b0fce | 2011-01-12 16:34:40 | [diff] [blame] | 505 | } else { |
| 506 | res = true; |
[email protected] | 2aa8e18 | 2010-07-12 16:25:04 | [diff] [blame] | 507 | } |
| 508 | break; |
| 509 | } |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 510 | default: { |
[email protected] | 0064b39 | 2012-06-20 01:57:38 | [diff] [blame] | 511 | res = Play(context); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 512 | break; |
| 513 | } |
| 514 | } |
| 515 | return res; |
| 516 | } |
| 517 | |
[email protected] | 62f2e80 | 2011-05-26 14:28:35 | [diff] [blame] | 518 | SkDevice* Emf::StartPageForVectorCanvas( |
[email protected] | 534c4fb | 2011-08-02 16:44:20 | [diff] [blame] | 519 | const gfx::Size& page_size, const gfx::Rect& content_area, |
[email protected] | d2fdcf0 | 2011-03-21 22:16:43 | [diff] [blame] | 520 | const float& scale_factor) { |
[email protected] | 39892b9 | 2011-04-30 02:24:44 | [diff] [blame] | 521 | if (!StartPage(page_size, content_area, scale_factor)) |
[email protected] | d2fdcf0 | 2011-03-21 22:16:43 | [diff] [blame] | 522 | return NULL; |
| 523 | |
[email protected] | 1e56416 | 2011-07-05 14:17:31 | [diff] [blame] | 524 | return skia::VectorPlatformDeviceEmf::CreateDevice(page_size.width(), |
| 525 | page_size.height(), |
| 526 | true, hdc_); |
[email protected] | d2fdcf0 | 2011-03-21 22:16:43 | [diff] [blame] | 527 | } |
| 528 | |
[email protected] | edc531f9 | 2011-03-18 17:52:23 | [diff] [blame] | 529 | bool Emf::StartPage(const gfx::Size& /*page_size*/, |
[email protected] | 39892b9 | 2011-04-30 02:24:44 | [diff] [blame] | 530 | const gfx::Rect& /*content_area*/, |
[email protected] | 1c7d72d | 2011-04-20 06:59:23 | [diff] [blame] | 531 | const float& /*scale_factor*/) { |
[email protected] | 2aa8e18 | 2010-07-12 16:25:04 | [diff] [blame] | 532 | DCHECK(hdc_); |
| 533 | if (!hdc_) |
| 534 | return false; |
[email protected] | 830cf74 | 2011-04-01 16:06:25 | [diff] [blame] | 535 | page_count_++; |
[email protected] | 2aa8e18 | 2010-07-12 16:25:04 | [diff] [blame] | 536 | PageBreakRecord record(PageBreakRecord::START_PAGE); |
| 537 | return !!GdiComment(hdc_, sizeof(record), |
| 538 | reinterpret_cast<const BYTE *>(&record)); |
| 539 | } |
| 540 | |
[email protected] | 8f17cd3e | 2011-03-16 01:39:42 | [diff] [blame] | 541 | bool Emf::FinishPage() { |
[email protected] | 2aa8e18 | 2010-07-12 16:25:04 | [diff] [blame] | 542 | DCHECK(hdc_); |
| 543 | if (!hdc_) |
| 544 | return false; |
| 545 | PageBreakRecord record(PageBreakRecord::END_PAGE); |
| 546 | return !!GdiComment(hdc_, sizeof(record), |
| 547 | reinterpret_cast<const BYTE *>(&record)); |
| 548 | } |
| 549 | |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 550 | Emf::Enumerator::Enumerator(const Emf& emf, HDC context, const RECT* rect) { |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 551 | items_.clear(); |
| 552 | if (!EnumEnhMetaFile(context, |
| 553 | emf.emf(), |
| 554 | &Emf::Enumerator::EnhMetaFileProc, |
| 555 | reinterpret_cast<void*>(this), |
| 556 | rect)) { |
| 557 | NOTREACHED(); |
| 558 | items_.clear(); |
| 559 | } |
| 560 | DCHECK_EQ(context_.hdc, context); |
| 561 | } |
| 562 | |
| 563 | Emf::Enumerator::const_iterator Emf::Enumerator::begin() const { |
| 564 | return items_.begin(); |
| 565 | } |
| 566 | |
| 567 | Emf::Enumerator::const_iterator Emf::Enumerator::end() const { |
| 568 | return items_.end(); |
| 569 | } |
| 570 | |
| 571 | int CALLBACK Emf::Enumerator::EnhMetaFileProc(HDC hdc, |
| 572 | HANDLETABLE* handle_table, |
| 573 | const ENHMETARECORD* record, |
| 574 | int objects_count, |
| 575 | LPARAM param) { |
| 576 | Enumerator& emf = *reinterpret_cast<Enumerator*>(param); |
| 577 | if (!emf.context_.handle_table) { |
| 578 | DCHECK(!emf.context_.handle_table); |
| 579 | DCHECK(!emf.context_.objects_count); |
| 580 | emf.context_.handle_table = handle_table; |
| 581 | emf.context_.objects_count = objects_count; |
| 582 | emf.context_.hdc = hdc; |
| 583 | } else { |
| 584 | DCHECK_EQ(emf.context_.handle_table, handle_table); |
| 585 | DCHECK_EQ(emf.context_.objects_count, objects_count); |
| 586 | DCHECK_EQ(emf.context_.hdc, hdc); |
| 587 | } |
[email protected] | 0064b39 | 2012-06-20 01:57:38 | [diff] [blame] | 588 | emf.items_.push_back(Record(record)); |
initial.commit | 09911bf | 2008-07-26 23:55:29 | [diff] [blame] | 589 | return 1; |
| 590 | } |
| 591 | |
[email protected] | 60d77bd | 2012-08-22 00:10:07 | [diff] [blame] | 592 | bool Emf::IsAlphaBlendUsed() const { |
| 593 | bool result = false; |
| 594 | ::EnumEnhMetaFile(NULL, |
| 595 | emf(), |
| 596 | &IsAlphaBlendUsedEnumProc, |
| 597 | &result, |
| 598 | NULL); |
| 599 | return result; |
| 600 | } |
| 601 | |
| 602 | Emf* Emf::RasterizeMetafile(int raster_area_in_pixels) const { |
| 603 | gfx::Rect page_bounds = GetPageBounds(1); |
| 604 | gfx::Size page_size(page_bounds.size()); |
| 605 | if (page_size.GetArea() <= 0) { |
| 606 | NOTREACHED() << "Metafile is empty"; |
| 607 | page_bounds = gfx::Rect(1, 1); |
| 608 | } |
| 609 | |
| 610 | float scale = sqrt(float(raster_area_in_pixels) / page_size.GetArea()); |
| 611 | page_size.set_width(std::max<int>(1, page_size.width() * scale)); |
| 612 | page_size.set_height(std::max<int>(1, page_size.height() * scale)); |
| 613 | |
[email protected] | 60d77bd | 2012-08-22 00:10:07 | [diff] [blame] | 614 | |
[email protected] | 64f5f92 | 2012-10-13 04:50:06 | [diff] [blame] | 615 | RasterBitmap bitmap(page_size); |
[email protected] | 60d77bd | 2012-08-22 00:10:07 | [diff] [blame] | 616 | |
| 617 | gfx::Rect bitmap_rect(page_size); |
[email protected] | 64f5f92 | 2012-10-13 04:50:06 | [diff] [blame] | 618 | Playback(bitmap.context(), &bitmap_rect.ToRECT()); |
[email protected] | 60d77bd | 2012-08-22 00:10:07 | [diff] [blame] | 619 | |
| 620 | scoped_ptr<Emf> result(new Emf); |
| 621 | result->Init(); |
| 622 | HDC hdc = result->context(); |
| 623 | DCHECK(hdc); |
| 624 | skia::InitializeDC(hdc); |
| 625 | |
| 626 | // Params are ignored. |
| 627 | result->StartPage(page_bounds.size(), page_bounds, 1); |
| 628 | |
| 629 | ::ModifyWorldTransform(hdc, NULL, MWT_IDENTITY); |
| 630 | XFORM xform = { |
| 631 | float(page_bounds.width()) / bitmap_rect.width(), 0, |
| 632 | 0, float(page_bounds.height()) / bitmap_rect.height(), |
| 633 | page_bounds.x(), |
| 634 | page_bounds.y(), |
| 635 | }; |
| 636 | ::SetWorldTransform(hdc, &xform); |
| 637 | ::BitBlt(hdc, 0, 0, bitmap_rect.width(), bitmap_rect.height(), |
[email protected] | 64f5f92 | 2012-10-13 04:50:06 | [diff] [blame] | 638 | bitmap.context(), bitmap_rect.x(), bitmap_rect.y(), SRCCOPY); |
[email protected] | 60d77bd | 2012-08-22 00:10:07 | [diff] [blame] | 639 | |
| 640 | result->FinishPage(); |
| 641 | result->FinishDocument(); |
| 642 | |
| 643 | return result.release(); |
| 644 | } |
| 645 | |
[email protected] | 64f5f92 | 2012-10-13 04:50:06 | [diff] [blame] | 646 | Emf* Emf::RasterizeAlphaBlend() const { |
| 647 | gfx::Rect page_bounds = GetPageBounds(1); |
| 648 | if (page_bounds.size().GetArea() <= 0) { |
| 649 | NOTREACHED() << "Metafile is empty"; |
| 650 | page_bounds = gfx::Rect(1, 1); |
| 651 | } |
| 652 | |
| 653 | RasterBitmap bitmap(page_bounds.size()); |
| 654 | |
| 655 | // Map metafile page_bounds.x(), page_bounds.y() to bitmap 0, 0. |
| 656 | XFORM xform = { 1, 0, 0, 1, -page_bounds.x(), -page_bounds.y()}; |
| 657 | ::SetWorldTransform(bitmap.context(), &xform); |
| 658 | |
| 659 | scoped_ptr<Emf> result(new Emf); |
| 660 | result->Init(); |
| 661 | HDC hdc = result->context(); |
| 662 | DCHECK(hdc); |
| 663 | skia::InitializeDC(hdc); |
| 664 | |
| 665 | HDC bitmap_dc = bitmap.context(); |
| 666 | ::EnumEnhMetaFile(hdc, emf(), &RasterizeAlphaBlendProc, &bitmap_dc, |
| 667 | &page_bounds.ToRECT()); |
| 668 | |
| 669 | result->FinishDocument(); |
| 670 | |
| 671 | return result.release(); |
| 672 | } |
| 673 | |
| 674 | |
[email protected] | 0e0fca3 | 2009-07-06 15:25:50 | [diff] [blame] | 675 | } // namespace printing |