summaryrefslogtreecommitdiffstats
path: root/printing/emf_win.cc
diff options
context:
space:
mode:
authorsverrir@google.com <sverrir@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-06 15:25:50 +0000
committersverrir@google.com <sverrir@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-06 15:25:50 +0000
commit0e0fca32b226a29a774728b642848bfdd732f791 (patch)
tree70547f886163e2a364ed47a8d5c85765d48f0e12 /printing/emf_win.cc
parentd09e2889c21d0a420b70680291906e4a17fb8503 (diff)
downloadchromium_src-0e0fca32b226a29a774728b642848bfdd732f791.zip
chromium_src-0e0fca32b226a29a774728b642848bfdd732f791.tar.gz
chromium_src-0e0fca32b226a29a774728b642848bfdd732f791.tar.bz2
Move Emf class to the printing library. Also creates a platform agnostic NativeMetafile definition to ease platform porting.
BUG=none TEST=none (No functional change) Review URL: http://codereview.chromium.org/149181 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@19943 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'printing/emf_win.cc')
-rw-r--r--printing/emf_win.cc317
1 files changed, 317 insertions, 0 deletions
diff --git a/printing/emf_win.cc b/printing/emf_win.cc
new file mode 100644
index 0000000..42380443
--- /dev/null
+++ b/printing/emf_win.cc
@@ -0,0 +1,317 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "printing/emf_win.h"
+
+#include "base/gfx/rect.h"
+#include "base/logging.h"
+
+namespace printing {
+
+Emf::Emf() : emf_(NULL), hdc_(NULL) {
+}
+
+Emf::~Emf() {
+ CloseEmf();
+ DCHECK(!emf_ && !hdc_);
+}
+
+bool Emf::CreateDc(HDC sibling, const RECT* rect) {
+ DCHECK(!emf_ && !hdc_);
+ hdc_ = CreateEnhMetaFile(sibling, NULL, rect, NULL);
+ DCHECK(hdc_);
+ return hdc_ != NULL;
+}
+
+bool Emf::CreateFromData(const void* buffer, size_t size) {
+ DCHECK(!emf_ && !hdc_);
+ emf_ = SetEnhMetaFileBits(static_cast<unsigned>(size),
+ reinterpret_cast<const BYTE*>(buffer));
+ DCHECK(emf_);
+ return emf_ != NULL;
+}
+
+bool Emf::CloseDc() {
+ DCHECK(!emf_ && hdc_);
+ emf_ = CloseEnhMetaFile(hdc_);
+ DCHECK(emf_);
+ hdc_ = NULL;
+ return emf_ != NULL;
+}
+
+void Emf::CloseEmf() {
+ DCHECK(!hdc_);
+ if (emf_) {
+ DeleteEnhMetaFile(emf_);
+ emf_ = NULL;
+ }
+}
+
+bool Emf::Playback(HDC hdc, const RECT* rect) const {
+ DCHECK(emf_ && !hdc_);
+ RECT bounds;
+ if (!rect) {
+ // Get the natural bounds of the EMF buffer.
+ bounds = GetBounds().ToRECT();
+ rect = &bounds;
+ }
+ return PlayEnhMetaFile(hdc, emf_, rect) != 0;
+}
+
+bool Emf::SafePlayback(HDC context) const {
+ DCHECK(emf_ && !hdc_);
+ XFORM base_matrix;
+ if (!GetWorldTransform(context, &base_matrix)) {
+ NOTREACHED();
+ return false;
+ }
+
+ return EnumEnhMetaFile(context,
+ emf_,
+ &Emf::SafePlaybackProc,
+ reinterpret_cast<void*>(&base_matrix),
+ &GetBounds().ToRECT()) != 0;
+}
+
+gfx::Rect Emf::GetBounds() const {
+ DCHECK(emf_ && !hdc_);
+ ENHMETAHEADER header;
+ if (GetEnhMetaFileHeader(emf_, sizeof(header), &header) != sizeof(header)) {
+ NOTREACHED();
+ return gfx::Rect();
+ }
+ if (header.rclBounds.left == 0 &&
+ header.rclBounds.top == 0 &&
+ header.rclBounds.right == -1 &&
+ header.rclBounds.bottom == -1) {
+ // A freshly created EMF buffer that has no drawing operation has invalid
+ // bounds. Instead of having an (0,0) size, it has a (-1,-1) size. Detect
+ // this special case and returns an empty Rect instead of an invalid one.
+ return gfx::Rect();
+ }
+ return gfx::Rect(header.rclBounds.left,
+ header.rclBounds.top,
+ header.rclBounds.right - header.rclBounds.left,
+ header.rclBounds.bottom - header.rclBounds.top);
+}
+
+unsigned Emf::GetDataSize() const {
+ DCHECK(emf_ && !hdc_);
+ return GetEnhMetaFileBits(emf_, 0, NULL);
+}
+
+bool Emf::GetData(void* buffer, size_t size) const {
+ DCHECK(emf_ && !hdc_);
+ DCHECK(buffer && size);
+ unsigned size2 = GetEnhMetaFileBits(emf_, static_cast<unsigned>(size),
+ reinterpret_cast<BYTE*>(buffer));
+ DCHECK(size2 == size);
+ return size2 == size && size2 != 0;
+}
+
+bool Emf::GetData(std::vector<uint8>* buffer) const {
+ unsigned size = GetDataSize();
+ if (!size)
+ return false;
+
+ buffer->resize(size);
+ if (!GetData(&buffer->front(), size))
+ return false;
+ return true;
+}
+
+bool Emf::SaveTo(const std::wstring& filename) const {
+ HANDLE file = CreateFile(filename.c_str(), GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ CREATE_ALWAYS, 0, NULL);
+ if (file == INVALID_HANDLE_VALUE)
+ return false;
+
+ bool success = false;
+ std::vector<uint8> buffer;
+ if (GetData(&buffer)) {
+ DWORD written = 0;
+ if (WriteFile(file, &*buffer.begin(), static_cast<DWORD>(buffer.size()),
+ &written, NULL) &&
+ written == buffer.size()) {
+ success = true;
+ }
+ }
+ CloseHandle(file);
+ return success;
+}
+
+int CALLBACK Emf::SafePlaybackProc(HDC hdc,
+ HANDLETABLE* handle_table,
+ const ENHMETARECORD* record,
+ int objects_count,
+ LPARAM param) {
+ const XFORM* base_matrix = reinterpret_cast<const XFORM*>(param);
+ EnumerationContext context;
+ context.handle_table = handle_table;
+ context.objects_count = objects_count;
+ context.hdc = hdc;
+ Record record_instance(&context, record);
+ bool success = record_instance.SafePlayback(base_matrix);
+ DCHECK(success);
+ return 1;
+}
+
+Emf::Record::Record() {
+}
+
+Emf::Record::Record(const EnumerationContext* context,
+ const ENHMETARECORD* record)
+ : record_(record),
+ context_(context) {
+ DCHECK(record_);
+}
+
+bool Emf::Record::Play() const {
+ return 0 != PlayEnhMetaFileRecord(context_->hdc,
+ context_->handle_table,
+ record_,
+ context_->objects_count);
+}
+
+bool Emf::Record::SafePlayback(const XFORM* base_matrix) const {
+ // For EMF field description, see [MS-EMF] Enhanced Metafile Format
+ // Specification.
+ //
+ // This is the second major EMF breakage I get; the first one being
+ // SetDCBrushColor/SetDCPenColor/DC_PEN/DC_BRUSH being silently ignored.
+ //
+ // This function is the guts of the fix for bug 1186598. Some printer drivers
+ // somehow choke on certain EMF records, but calling the corresponding
+ // function directly on the printer HDC is fine. Still, playing the EMF record
+ // fails. Go figure.
+ //
+ // The main issue is that SetLayout is totally unsupported on these printers
+ // (HP 4500/4700). I used to call SetLayout and I stopped. I found out this is
+ // not sufficient because GDI32!PlayEnhMetaFile internally calls SetLayout(!)
+ // Damn.
+ //
+ // So I resorted to manually parse the EMF records and play them one by one.
+ // The issue with this method compared to using PlayEnhMetaFile to play back
+ // an EMF buffer is that the later silently fixes the matrix to take in
+ // account the matrix currently loaded at the time of the call.
+ // The matrix magic is done transparently when using PlayEnhMetaFile but since
+ // I'm processing one field at a time, I need to do the fixup myself. Note
+ // that PlayEnhMetaFileRecord doesn't fix the matrix correctly even when
+ // called inside an EnumEnhMetaFile loop. Go figure (bis).
+ //
+ // So when I see a EMR_SETWORLDTRANSFORM and EMR_MODIFYWORLDTRANSFORM, I need
+ // to fix the matrix according to the matrix previously loaded before playing
+ // back the buffer. Otherwise, the previously loaded matrix would be ignored
+ // and the EMF buffer would always be played back at its native resolution.
+ // Duh.
+ //
+ // I also use this opportunity to skip over eventual EMR_SETLAYOUT record that
+ // could remain.
+ //
+ // Note: I should probably care about view ports and clipping, eventually.
+ bool res;
+ switch (record()->iType) {
+ case EMR_SETWORLDTRANSFORM: {
+ DCHECK_EQ(record()->nSize, sizeof(DWORD) * 2 + sizeof(XFORM));
+ const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm);
+ HDC hdc = context_->hdc;
+ if (base_matrix) {
+ res = 0 != SetWorldTransform(hdc, base_matrix) &&
+ ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
+ } else {
+ res = 0 != SetWorldTransform(hdc, xform);
+ }
+ break;
+ }
+ case EMR_MODIFYWORLDTRANSFORM: {
+ DCHECK_EQ(record()->nSize,
+ sizeof(DWORD) * 2 + sizeof(XFORM) + sizeof(DWORD));
+ const XFORM* xform = reinterpret_cast<const XFORM*>(record()->dParm);
+ const DWORD* option = reinterpret_cast<const DWORD*>(xform + 1);
+ HDC hdc = context_->hdc;
+ switch (*option) {
+ case MWT_IDENTITY:
+ if (base_matrix) {
+ res = 0 != SetWorldTransform(hdc, base_matrix);
+ } else {
+ res = 0 != ModifyWorldTransform(hdc, xform, MWT_IDENTITY);
+ }
+ break;
+ case MWT_LEFTMULTIPLY:
+ case MWT_RIGHTMULTIPLY:
+ res = 0 != ModifyWorldTransform(hdc, xform, *option);
+ break;
+ case 4: // MWT_SET
+ if (base_matrix) {
+ res = 0 != SetWorldTransform(hdc, base_matrix) &&
+ ModifyWorldTransform(hdc, xform, MWT_LEFTMULTIPLY);
+ } else {
+ res = 0 != SetWorldTransform(hdc, xform);
+ }
+ break;
+ default:
+ res = false;
+ break;
+ }
+ break;
+ }
+ case EMR_SETLAYOUT:
+ // Ignore it.
+ res = true;
+ break;
+ default: {
+ res = Play();
+ break;
+ }
+ }
+ return res;
+}
+
+Emf::Enumerator::Enumerator(const Emf& emf, HDC context, const RECT* rect) {
+ context_.handle_table = NULL;
+ context_.objects_count = 0;
+ context_.hdc = NULL;
+ items_.clear();
+ if (!EnumEnhMetaFile(context,
+ emf.emf(),
+ &Emf::Enumerator::EnhMetaFileProc,
+ reinterpret_cast<void*>(this),
+ rect)) {
+ NOTREACHED();
+ items_.clear();
+ }
+ DCHECK_EQ(context_.hdc, context);
+}
+
+Emf::Enumerator::const_iterator Emf::Enumerator::begin() const {
+ return items_.begin();
+}
+
+Emf::Enumerator::const_iterator Emf::Enumerator::end() const {
+ return items_.end();
+}
+
+int CALLBACK Emf::Enumerator::EnhMetaFileProc(HDC hdc,
+ HANDLETABLE* handle_table,
+ const ENHMETARECORD* record,
+ int objects_count,
+ LPARAM param) {
+ Enumerator& emf = *reinterpret_cast<Enumerator*>(param);
+ if (!emf.context_.handle_table) {
+ DCHECK(!emf.context_.handle_table);
+ DCHECK(!emf.context_.objects_count);
+ emf.context_.handle_table = handle_table;
+ emf.context_.objects_count = objects_count;
+ emf.context_.hdc = hdc;
+ } else {
+ DCHECK_EQ(emf.context_.handle_table, handle_table);
+ DCHECK_EQ(emf.context_.objects_count, objects_count);
+ DCHECK_EQ(emf.context_.hdc, hdc);
+ }
+ emf.items_.push_back(Record(&emf.context_, record));
+ return 1;
+}
+
+} // namespace printing