1 // Copyright 2012 The Chromium Authors
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"
16 #include "base/files/file_path.h"
17 #include "base/files/file_util.h"
18 #include "base/files/scoped_temp_dir.h"
19 #include "base/path_service.h"
20 #include "base/win/scoped_hdc.h"
21 #include "printing/mojom/print.mojom.h"
22 #include "printing/printing_context.h"
23 #include "printing/printing_context_win.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 #include "ui/gfx/geometry/point.h"
26 #include "ui/gfx/geometry/size.h"
32 // This test is automatically disabled if no printer named "UnitTest Printer" is
34 class EmfPrintingTest : public testing::Test, public PrintingContext::Delegate {
36 typedef testing::Test Parent;
37 static bool IsTestCaseDisabled() {
38 // It is assumed this printer is a HP Color LaserJet 4550 PCL or 4700.
39 HDC hdc = CreateDC(L"WINSPOOL", L"UnitTest Printer", nullptr, nullptr);
46 // PrintingContext::Delegate methods.
47 gfx::NativeView GetParentView() override { return nullptr; }
48 std::string GetAppLocale() override { return std::string(); }
51 const uint32_t EMF_HEADER_SIZE = 128;
57 std::vector<char> data;
60 EXPECT_TRUE(emf.Init());
61 EXPECT_TRUE(emf.context());
62 // An empty EMF is invalid, so we put at least a rectangle in it.
63 ::Rectangle(emf.context(), 10, 10, 190, 190);
64 EXPECT_TRUE(emf.FinishDocument());
65 uint32_t size = emf.GetDataSize();
66 EXPECT_GT(size, EMF_HEADER_SIZE);
67 EXPECT_TRUE(emf.GetDataAsVector(&data));
68 ASSERT_EQ(data.size(), size);
73 // TODO(thestig): Make `data` uint8_t and avoid the base::as_bytes() call.
74 EXPECT_TRUE(emf.InitFromData(base::as_bytes(base::make_span(data))));
75 HDC hdc = CreateCompatibleDC(nullptr);
77 RECT output_rect = {0, 0, 10, 10};
78 EXPECT_TRUE(emf.Playback(hdc, &output_rect));
79 EXPECT_TRUE(DeleteDC(hdc));
82 // Disabled if no "UnitTest printer" exist. Useful to reproduce bug 1186598.
83 TEST_F(EmfPrintingTest, Enumerate) {
84 if (IsTestCaseDisabled())
87 auto settings = std::make_unique<PrintSettings>();
89 // My test case is a HP Color LaserJet 4550 PCL.
90 settings->set_device_name(u"UnitTest Printer");
93 PrintingContextWin context(this);
94 EXPECT_EQ(mojom::ResultCode::kSuccess,
95 context.InitWithSettingsForTest(std::move(settings)));
97 base::FilePath emf_file;
98 EXPECT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &emf_file));
99 emf_file = emf_file.Append(FILE_PATH_LITERAL("printing"))
100 .Append(FILE_PATH_LITERAL("test"))
101 .Append(FILE_PATH_LITERAL("data"))
102 .Append(FILE_PATH_LITERAL("emf"))
103 .Append(FILE_PATH_LITERAL("test4.emf"));
105 // Load any EMF with an image.
106 std::string emf_data;
107 base::ReadFileToString(emf_file, &emf_data);
108 ASSERT_TRUE(emf_data.size());
111 EXPECT_TRUE(emf.InitFromData(base::as_bytes(base::make_span(emf_data))));
113 // This will print to file. The reason is that when running inside a
114 // unit_test, PrintingContext automatically dumps its files to the
115 // current directory.
116 // TODO(maruel): Clean the .PRN file generated in current directory.
117 context.NewDocument(u"EmfTest.Enumerate");
118 // Process one at a time.
119 RECT page_bounds = emf.GetPageBounds(1).ToRECT();
120 Emf::Enumerator emf_enum(emf, context.context(), &page_bounds);
121 for (Emf::Enumerator::const_iterator itr = emf_enum.begin();
122 itr != emf_enum.end(); ++itr) {
123 // To help debugging.
124 ptrdiff_t index = itr - emf_enum.begin();
125 // If you get this assert, you need to lookup iType in wingdi.h. It starts
128 EXPECT_TRUE(itr->SafePlayback(&emf_enum.context_))
129 << " index: " << index << " type: " << itr->record()->iType;
131 context.DocumentDone();
134 // Disabled if no "UnitTest printer" exists.
135 TEST_F(EmfPrintingTest, PageBreak) {
136 base::win::ScopedCreateDC dc(
137 CreateDC(L"WINSPOOL", L"UnitTest Printer", nullptr, nullptr));
140 std::vector<char> data;
143 EXPECT_TRUE(emf.Init());
144 EXPECT_TRUE(emf.context());
147 emf.StartPage(gfx::Size(), gfx::Rect(), 1,
148 mojom::PageOrientation::kUpright);
149 ::Rectangle(emf.context(), 10, 10, 190, 190);
150 EXPECT_TRUE(emf.FinishPage());
153 EXPECT_EQ(3U, emf.GetPageCount());
154 EXPECT_TRUE(emf.FinishDocument());
155 uint32_t size = emf.GetDataSize();
156 EXPECT_TRUE(emf.GetDataAsVector(&data));
157 ASSERT_EQ(data.size(), size);
160 // Playback the data.
162 di.cbSize = sizeof(DOCINFO);
163 di.lpszDocName = L"Test Job";
164 int job_id = ::StartDoc(dc.Get(), &di);
166 // TODO(thestig): Make `data` uint8_t and avoid the base::as_bytes() call.
167 EXPECT_TRUE(emf.InitFromData(base::as_bytes(base::make_span(data))));
168 EXPECT_TRUE(emf.SafePlayback(dc.Get()));
170 // Since presumably the printer is not real, let us just delete the job from
172 HANDLE printer = nullptr;
173 if (::OpenPrinter(const_cast<LPTSTR>(L"UnitTest Printer"), &printer,
175 ::SetJob(printer, job_id, 0, nullptr, JOB_CONTROL_DELETE);
176 ClosePrinter(printer);
180 TEST(EmfTest, FileBackedEmf) {
181 // Simplest use case.
182 base::ScopedTempDir scratch_metafile_dir;
183 ASSERT_TRUE(scratch_metafile_dir.CreateUniqueTempDir());
184 base::FilePath metafile_path;
185 EXPECT_TRUE(base::CreateTemporaryFileInDir(scratch_metafile_dir.GetPath(),
188 std::vector<char> data;
191 EXPECT_TRUE(emf.InitToFile(metafile_path));
192 EXPECT_TRUE(emf.context());
193 // An empty EMF is invalid, so we put at least a rectangle in it.
194 ::Rectangle(emf.context(), 10, 10, 190, 190);
195 EXPECT_TRUE(emf.FinishDocument());
196 size = emf.GetDataSize();
197 EXPECT_GT(size, EMF_HEADER_SIZE);
198 EXPECT_TRUE(emf.GetDataAsVector(&data));
199 EXPECT_EQ(data.size(), size);
201 int64_t file_size = 0;
202 base::GetFileSize(metafile_path, &file_size);
203 EXPECT_EQ(size, file_size);
205 // Playback the data.
206 HDC hdc = CreateCompatibleDC(nullptr);
209 EXPECT_TRUE(emf.InitFromFile(metafile_path));
210 RECT output_rect = {0, 0, 10, 10};
211 EXPECT_TRUE(emf.Playback(hdc, &output_rect));
212 EXPECT_TRUE(DeleteDC(hdc));
215 } // namespace printing