Add tests for FPDF_SetPrintMode().
Set the mode to FPDF_PRINTMODE_POSTSCRIPT2 and
FPDF_PRINTMODE_POSTSCRIPT3, and try to generate some EMFs
that encapsulate PS data.
Add EmbedderTest::GetPostScriptFromEmf(), which is a simplified version
of Chromium code with similar functions, and use it to extract out the
PS data for comparison in tests.
Change-Id: I5cff4ebf0fbac60f63f07b98490a45b8ee3515df
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/55091
Commit-Queue: Lei Zhang <thestig@chromium.org>
Reviewed-by: Tom Sepez <tsepez@chromium.org>
diff --git a/fpdfsdk/fpdf_view_embeddertest.cpp b/fpdfsdk/fpdf_view_embeddertest.cpp
index d8eef2c..077be7a 100644
--- a/fpdfsdk/fpdf_view_embeddertest.cpp
+++ b/fpdfsdk/fpdf_view_embeddertest.cpp
@@ -21,6 +21,55 @@
namespace {
+#if defined(OS_WIN)
+const char kExpectedRectanglePostScript[] = R"(
+save
+/im/initmatrix load def
+/n/newpath load def/m/moveto load def/l/lineto load def/c/curveto load def/h/closepath load def
+/f/fill load def/F/eofill load def/s/stroke load def/W/clip load def/W*/eoclip load def
+/rg/setrgbcolor load def/k/setcmykcolor load def
+/J/setlinecap load def/j/setlinejoin load def/w/setlinewidth load def/M/setmiterlimit load def/d/setdash load def
+/q/gsave load def/Q/grestore load def/iM/imagemask load def
+/Tj/show load def/Ff/findfont load def/Fs/scalefont load def/Sf/setfont load def
+/cm/concat load def/Cm/currentmatrix load def/mx/matrix load def/sm/setmatrix load def
+0 300 m 0 0 l 200 0 l 200 300 l 0 300 l h W n
+q
+0 300 m 0 0 l 200 0 l 200 300 l 0 300 l h W n
+q
+0 J
+[]0 d
+0 j
+1 w
+10 M
+mx Cm [1 0 0 -1 0 300]cm 0 290 m 10 290 l 10 300 l 0 300 l 0 290 l h 0 0 0 rg
+q F Q s sm
+mx Cm [1 0 0 -1 0 300]cm 10 150 m 60 150 l 60 180 l 10 180 l 10 150 l h q F Q s sm
+mx Cm [1 0 0 -1 0 300]cm 190 290 m 200 290 l 200 300 l 190 300 l 190 290 l h 0 0 1 rg
+q F Q 0 0 0 rg
+s sm
+mx Cm [1 0 0 -1 0 300]cm 70 232 m 120 232 l 120 262 l 70 262 l 70 232 l h 0 0 1 rg
+q F Q 0 0 0 rg
+s sm
+mx Cm [1 0 0 -1 0 300]cm 190 0 m 200 0 l 200 10 l 190 10 l 190 0 l h 0 1 0 rg
+q F Q 0 0 0 rg
+s sm
+mx Cm [1 0 0 -1 0 300]cm 130 150 m 180 150 l 180 180 l 130 180 l 130 150 l h 0 1 0 rg
+q F Q 0 0 0 rg
+s sm
+mx Cm [1 0 0 -1 0 300]cm 0 0 m 10 0 l 10 10 l 0 10 l 0 0 l h 1 0 0 rg
+q F Q 0 0 0 rg
+s sm
+mx Cm [1 0 0 -1 0 300]cm 70 67 m 120 67 l 120 97 l 70 97 l 70 67 l h 1 0 0 rg
+q F Q 0 0 0 rg
+s sm
+Q
+Q
+Q
+
+restore
+)";
+#endif // defined(OS_WIN)
+
class MockDownloadHints final : public FX_DOWNLOADHINTS {
public:
static void SAddSegment(FX_DOWNLOADHINTS* pThis, size_t offset, size_t size) {
@@ -908,7 +957,7 @@
}
#if defined(OS_WIN)
-TEST_F(FPDFViewEmbedderTest, FPDF_RenderPage) {
+TEST_F(FPDFViewEmbedderTest, FPDFRenderPageEmf) {
ASSERT_TRUE(OpenDocument("rectangles.pdf"));
FPDF_PAGE page = LoadPage(0);
ASSERT_TRUE(page);
@@ -923,4 +972,75 @@
UnloadPage(page);
}
-#endif
+
+class PostScriptRenderEmbedderTestBase : public FPDFViewEmbedderTest {
+ protected:
+ ~PostScriptRenderEmbedderTestBase() override = default;
+
+ // FPDFViewEmbedderTest:
+ void TearDown() override {
+ FPDF_SetPrintMode(FPDF_PRINTMODE_EMF);
+ FPDFViewEmbedderTest::TearDown();
+ }
+};
+
+class PostScriptLevel2EmbedderTest : public PostScriptRenderEmbedderTestBase {
+ public:
+ PostScriptLevel2EmbedderTest() = default;
+ ~PostScriptLevel2EmbedderTest() override = default;
+
+ protected:
+ // FPDFViewEmbedderTest:
+ void SetUp() override {
+ FPDFViewEmbedderTest::SetUp();
+ FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT2);
+ }
+};
+
+class PostScriptLevel3EmbedderTest : public PostScriptRenderEmbedderTestBase {
+ public:
+ PostScriptLevel3EmbedderTest() = default;
+ ~PostScriptLevel3EmbedderTest() override = default;
+
+ protected:
+ // FPDFViewEmbedderTest:
+ void SetUp() override {
+ FPDFViewEmbedderTest::SetUp();
+ FPDF_SetPrintMode(FPDF_PRINTMODE_POSTSCRIPT3);
+ }
+};
+
+TEST_F(PostScriptLevel2EmbedderTest, Rectangles) {
+ ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+ FPDF_PAGE page = LoadPage(0);
+ ASSERT_TRUE(page);
+
+ std::vector<uint8_t> emf_normal = RenderPageWithFlagsToEmf(page, 0);
+ std::string ps_data = GetPostScriptFromEmf(emf_normal);
+ EXPECT_STREQ(kExpectedRectanglePostScript, ps_data.c_str());
+
+ // FPDF_REVERSE_BYTE_ORDER is ignored since PostScript is not bitmap-based.
+ std::vector<uint8_t> emf_reverse_byte_order =
+ RenderPageWithFlagsToEmf(page, FPDF_REVERSE_BYTE_ORDER);
+ EXPECT_EQ(emf_normal, emf_reverse_byte_order);
+
+ UnloadPage(page);
+}
+
+TEST_F(PostScriptLevel3EmbedderTest, Rectangles) {
+ ASSERT_TRUE(OpenDocument("rectangles.pdf"));
+ FPDF_PAGE page = LoadPage(0);
+ ASSERT_TRUE(page);
+
+ std::vector<uint8_t> emf_normal = RenderPageWithFlagsToEmf(page, 0);
+ std::string ps_data = GetPostScriptFromEmf(emf_normal);
+ EXPECT_STREQ(kExpectedRectanglePostScript, ps_data.c_str());
+
+ // FPDF_REVERSE_BYTE_ORDER is ignored since PostScript is not bitmap-based.
+ std::vector<uint8_t> emf_reverse_byte_order =
+ RenderPageWithFlagsToEmf(page, FPDF_REVERSE_BYTE_ORDER);
+ EXPECT_EQ(emf_normal, emf_reverse_byte_order);
+
+ UnloadPage(page);
+}
+#endif // defined(OS_WIN)
diff --git a/testing/embedder_test.cpp b/testing/embedder_test.cpp
index 9e64ee5..b092bc6 100644
--- a/testing/embedder_test.cpp
+++ b/testing/embedder_test.cpp
@@ -52,6 +52,18 @@
}
}
+#if defined(OS_WIN)
+int CALLBACK GetRecordProc(HDC hdc,
+ HANDLETABLE* handle_table,
+ const ENHMETARECORD* record,
+ int objects_count,
+ LPARAM param) {
+ auto& records = *reinterpret_cast<std::vector<const ENHMETARECORD*>*>(param);
+ records.push_back(record);
+ return 1;
+}
+#endif // defined(OS_WIN)
+
} // namespace
EmbedderTest::EmbedderTest()
@@ -405,6 +417,40 @@
DeleteEnhMetaFile(emf);
return buffer;
}
+
+// static
+std::string EmbedderTest::GetPostScriptFromEmf(
+ const std::vector<uint8_t>& emf_data) {
+ // This comes from Emf::InitFromData() in Chromium.
+ HENHMETAFILE emf = SetEnhMetaFileBits(emf_data.size(), emf_data.data());
+ if (!emf)
+ return std::string();
+
+ // This comes from Emf::Enumerator::Enumerator() in Chromium.
+ std::vector<const ENHMETARECORD*> records;
+ if (!EnumEnhMetaFile(nullptr, emf, &GetRecordProc, &records, nullptr)) {
+ DeleteEnhMetaFile(emf);
+ return std::string();
+ }
+
+ // This comes from PostScriptMetaFile::SafePlayback() in Chromium.
+ std::string ps_data;
+ for (const auto* record : records) {
+ if (record->iType != EMR_GDICOMMENT)
+ continue;
+
+ // PostScript data is encapsulated inside EMF comment records.
+ // The first two bytes of the comment indicate the string length. The rest
+ // is the actual string data.
+ const auto* comment = reinterpret_cast<const EMRGDICOMMENT*>(record);
+ const char* data = reinterpret_cast<const char*>(comment->Data);
+ uint16_t size = *reinterpret_cast<const uint16_t*>(data);
+ data += 2;
+ ps_data.append(data, size);
+ }
+ DeleteEnhMetaFile(emf);
+ return ps_data;
+}
#endif // defined(OS_WIN)
FPDF_DOCUMENT EmbedderTest::OpenSavedDocument() {
diff --git a/testing/embedder_test.h b/testing/embedder_test.h
index 56bf61e..a088577 100644
--- a/testing/embedder_test.h
+++ b/testing/embedder_test.h
@@ -166,6 +166,9 @@
// Convert |page| into EMF with the specified page rendering |flags|.
static std::vector<uint8_t> RenderPageWithFlagsToEmf(FPDF_PAGE page,
int flags);
+
+ // Get the PostScript data from |emf_data|.
+ static std::string GetPostScriptFromEmf(const std::vector<uint8_t>& emf_data);
#endif
protected: