blob: 4a67c1720b5b8892d14918635c5e681a8092fe00 [file] [log] [blame]
Tom Sepezd483eb42016-01-06 10:03:59 -08001// Copyright 2016 PDFium 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.
4
Dan Sinclair85c8e7f2016-11-21 13:50:32 -05005#include <memory>
6#include <string>
7
Nicolas Pena0fc185e2017-02-08 12:13:20 -05008#include "core/fxcrt/fx_system.h"
Tom Sepezd483eb42016-01-06 10:03:59 -08009#include "public/fpdf_edit.h"
10#include "public/fpdfview.h"
11#include "testing/embedder_test.h"
Tom Sepez0aec19b2016-01-07 12:22:44 -080012#include "testing/gmock/include/gmock/gmock-matchers.h"
Tom Sepezd483eb42016-01-06 10:03:59 -080013#include "testing/gtest/include/gtest/gtest.h"
Tom Sepez0aec19b2016-01-07 12:22:44 -080014#include "testing/test_support.h"
Tom Sepezd483eb42016-01-06 10:03:59 -080015
Tom Sepez0aec19b2016-01-07 12:22:44 -080016class FPDFEditEmbeddertest : public EmbedderTest, public TestSaver {};
Tom Sepezd483eb42016-01-06 10:03:59 -080017
etienneb7712c262016-04-26 08:13:45 -070018namespace {
thestigdc7ec032016-11-21 15:32:52 -080019
etienneb7712c262016-04-26 08:13:45 -070020const char kExpectedPDF[] =
21 "%PDF-1.7\r\n"
22 "%\xA1\xB3\xC5\xD7\r\n"
23 "1 0 obj\r\n"
24 "<</Pages 2 0 R /Type/Catalog>>\r\n"
25 "endobj\r\n"
26 "2 0 obj\r\n"
27 "<</Count 1/Kids\\[ 4 0 R \\]/Type/Pages>>\r\n"
28 "endobj\r\n"
29 "3 0 obj\r\n"
30 "<</CreationDate\\(D:.*\\)/Creator\\(PDFium\\)>>\r\n"
31 "endobj\r\n"
32 "4 0 obj\r\n"
33 "<</Contents 5 0 R /MediaBox\\[ 0 0 640 480\\]"
34 "/Parent 2 0 R /Resources<<>>/Rotate 0/Type/Page"
35 ">>\r\n"
36 "endobj\r\n"
37 "5 0 obj\r\n"
38 "<</Filter/FlateDecode/Length 8>>stream\r\n"
39 // Character '_' is matching '\0' (see comment below).
40 "x\x9C\x3____\x1\r\n"
41 "endstream\r\n"
42 "endobj\r\n"
43 "xref\r\n"
44 "0 6\r\n"
45 "0000000000 65535 f\r\n"
46 "0000000017 00000 n\r\n"
47 "0000000066 00000 n\r\n"
48 "0000000122 00000 n\r\n"
49 "0000000192 00000 n\r\n"
50 "0000000301 00000 n\r\n"
51 "trailer\r\n"
52 "<<\r\n"
53 "/Root 1 0 R\r\n"
54 "/Info 3 0 R\r\n"
55 "/Size 6/ID\\[<.*><.*>\\]>>\r\n"
56 "startxref\r\n"
57 "379\r\n"
58 "%%EOF\r\n";
thestigdc7ec032016-11-21 15:32:52 -080059
60int GetBlockFromString(void* param,
61 unsigned long pos,
62 unsigned char* buf,
63 unsigned long size) {
64 std::string* new_file = static_cast<std::string*>(param);
65 if (!new_file || pos + size < pos)
66 return 0;
67
68 unsigned long file_size = new_file->size();
69 if (pos + size > file_size)
70 return 0;
71
72 memcpy(buf, new_file->data() + pos, size);
73 return 1;
74}
75
etienneb7712c262016-04-26 08:13:45 -070076} // namespace
77
Tom Sepezd483eb42016-01-06 10:03:59 -080078TEST_F(FPDFEditEmbeddertest, EmptyCreation) {
79 EXPECT_TRUE(CreateEmptyDocument());
weili9b777de2016-08-19 16:19:46 -070080 FPDF_PAGE page = FPDFPage_New(document(), 0, 640.0, 480.0);
Tom Sepezd483eb42016-01-06 10:03:59 -080081 EXPECT_NE(nullptr, page);
82 EXPECT_TRUE(FPDFPage_GenerateContent(page));
Tom Sepez0aec19b2016-01-07 12:22:44 -080083 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
etienneb7712c262016-04-26 08:13:45 -070084
85 // The MatchesRegexp doesn't support embedded NUL ('\0') characters. They are
86 // replaced by '_' for the purpose of the test.
87 std::string result = GetString();
88 std::replace(result.begin(), result.end(), '\0', '_');
89 EXPECT_THAT(result, testing::MatchesRegex(
90 std::string(kExpectedPDF, sizeof(kExpectedPDF))));
weili9b777de2016-08-19 16:19:46 -070091 FPDF_ClosePage(page);
Tom Sepezd483eb42016-01-06 10:03:59 -080092}
thestigdc7ec032016-11-21 15:32:52 -080093
94// Regression test for https://crbug.com/667012
95TEST_F(FPDFEditEmbeddertest, RasterizePDF) {
96 const char kAllBlackMd5sum[] = "5708fc5c4a8bd0abde99c8e8f0390615";
97
98 // Get the bitmap for the original document/
99 FPDF_BITMAP orig_bitmap;
100 {
101 EXPECT_TRUE(OpenDocument("black.pdf"));
102 FPDF_PAGE orig_page = LoadPage(0);
103 EXPECT_NE(nullptr, orig_page);
104 orig_bitmap = RenderPage(orig_page);
105 CompareBitmap(orig_bitmap, 612, 792, kAllBlackMd5sum);
106 UnloadPage(orig_page);
107 }
108
109 // Create a new document from |orig_bitmap| and save it.
110 {
111 FPDF_DOCUMENT temp_doc = FPDF_CreateNewDocument();
112 FPDF_PAGE temp_page = FPDFPage_New(temp_doc, 0, 612, 792);
113
114 // Add the bitmap to an image object and add the image object to the output
115 // page.
116 FPDF_PAGEOBJECT temp_img = FPDFPageObj_NewImgeObj(temp_doc);
117 EXPECT_TRUE(FPDFImageObj_SetBitmap(&temp_page, 1, temp_img, orig_bitmap));
118 EXPECT_TRUE(FPDFImageObj_SetMatrix(temp_img, 612, 0, 0, 792, 0, 0));
119 FPDFPage_InsertObject(temp_page, temp_img);
120 EXPECT_TRUE(FPDFPage_GenerateContent(temp_page));
121 EXPECT_TRUE(FPDF_SaveAsCopy(temp_doc, this, 0));
122 FPDF_ClosePage(temp_page);
123 FPDF_CloseDocument(temp_doc);
124 }
125 FPDFBitmap_Destroy(orig_bitmap);
126
127 // Get the generated content. Make sure it is at least as big as the original
128 // PDF.
129 std::string new_file = GetString();
130 EXPECT_GT(new_file.size(), 923U);
131
132 // Read |new_file| in, and verify its rendered bitmap.
133 {
134 FPDF_FILEACCESS file_access;
135 memset(&file_access, 0, sizeof(file_access));
136 file_access.m_FileLen = new_file.size();
137 file_access.m_GetBlock = GetBlockFromString;
138 file_access.m_Param = &new_file;
139
140 FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
141 EXPECT_EQ(1, FPDF_GetPageCount(document_));
142 FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
143 EXPECT_NE(nullptr, new_page);
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500144 FPDF_BITMAP new_bitmap = RenderPage(new_page);
thestigdc7ec032016-11-21 15:32:52 -0800145 CompareBitmap(new_bitmap, 612, 792, kAllBlackMd5sum);
146 FPDF_ClosePage(new_page);
147 FPDF_CloseDocument(new_doc);
148 FPDFBitmap_Destroy(new_bitmap);
149 }
150}
Nicolas Pena0fc185e2017-02-08 12:13:20 -0500151
152TEST_F(FPDFEditEmbeddertest, AddPaths) {
153 // Start with a blank page
154 FPDF_DOCUMENT doc = FPDF_CreateNewDocument();
155 FPDF_PAGE page = FPDFPage_New(doc, 0, 612, 792);
156
157 // We will first add a red rectangle
158 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
159 ASSERT_NE(nullptr, red_rect);
160 // Expect false when trying to set colors out of range
161 EXPECT_FALSE(FPDFPath_SetStrokeColor(red_rect, 100, 100, 100, 300));
162 EXPECT_FALSE(FPDFPath_SetFillColor(red_rect, 200, 256, 200, 0));
163
164 // Fill rectangle with red and insert to the page
165 EXPECT_TRUE(FPDFPath_SetFillColor(red_rect, 255, 0, 0, 255));
166 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
167 FPDFPage_InsertObject(page, red_rect);
168 EXPECT_TRUE(FPDFPage_GenerateContent(page));
169 FPDF_BITMAP page_bitmap = RenderPage(page);
170 CompareBitmap(page_bitmap, 612, 792, "66d02eaa6181e2c069ce2ea99beda497");
171 FPDFBitmap_Destroy(page_bitmap);
172
173 // Now add to that a green rectangle with some medium alpha
174 FPDF_PAGEOBJECT green_rect = FPDFPageObj_CreateNewRect(100, 100, 40, 40);
175 EXPECT_TRUE(FPDFPath_SetFillColor(green_rect, 0, 255, 0, 128));
176 EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect, FPDF_FILLMODE_WINDING, 0));
177 FPDFPage_InsertObject(page, green_rect);
178 EXPECT_TRUE(FPDFPage_GenerateContent(page));
179 page_bitmap = RenderPage(page);
180 CompareBitmap(page_bitmap, 612, 792, "7b0b87604594e773add528fae567a558");
181 FPDFBitmap_Destroy(page_bitmap);
182
183 // Add a black triangle.
184 FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(400, 100);
185 EXPECT_TRUE(FPDFPath_SetFillColor(black_path, 0, 0, 0, 200));
186 EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
187 EXPECT_TRUE(FPDFPath_LineTo(black_path, 400, 200));
188 EXPECT_TRUE(FPDFPath_LineTo(black_path, 300, 100));
189 EXPECT_TRUE(FPDFPath_Close(black_path));
190 FPDFPage_InsertObject(page, black_path);
191 EXPECT_TRUE(FPDFPage_GenerateContent(page));
192 page_bitmap = RenderPage(page);
193 CompareBitmap(page_bitmap, 612, 792, "eadc8020a14dfcf091da2688733d8806");
194 FPDFBitmap_Destroy(page_bitmap);
195
196 // Now add a more complex blue path.
197 FPDF_PAGEOBJECT blue_path = FPDFPageObj_CreateNewPath(200, 200);
198 EXPECT_TRUE(FPDFPath_SetFillColor(blue_path, 0, 0, 255, 255));
199 EXPECT_TRUE(FPDFPath_SetDrawMode(blue_path, FPDF_FILLMODE_WINDING, 0));
200 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 230, 230));
201 EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 250, 250, 280, 280, 300, 300));
202 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 325, 325));
203 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 350, 325));
204 EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 375, 330, 390, 360, 400, 400));
205 EXPECT_TRUE(FPDFPath_Close(blue_path));
206 FPDFPage_InsertObject(page, blue_path);
207 EXPECT_TRUE(FPDFPage_GenerateContent(page));
208 page_bitmap = RenderPage(page);
209 CompareBitmap(page_bitmap, 612, 792, "9823e1a21bd9b72b6a442ba4f12af946");
210 FPDFBitmap_Destroy(page_bitmap);
211
212 // Now save the result, closing the page and document
213 EXPECT_TRUE(FPDF_SaveAsCopy(doc, this, 0));
214 FPDF_ClosePage(page);
215 FPDF_CloseDocument(doc);
216 std::string new_file = GetString();
217
218 // Render the saved result
219 FPDF_FILEACCESS file_access;
220 memset(&file_access, 0, sizeof(file_access));
221 file_access.m_FileLen = new_file.size();
222 file_access.m_GetBlock = GetBlockFromString;
223 file_access.m_Param = &new_file;
224 FPDF_DOCUMENT new_doc = FPDF_LoadCustomDocument(&file_access, nullptr);
225 ASSERT_NE(nullptr, new_doc);
226 EXPECT_EQ(1, FPDF_GetPageCount(new_doc));
227 FPDF_PAGE new_page = FPDF_LoadPage(new_doc, 0);
228 ASSERT_NE(nullptr, new_page);
229 FPDF_BITMAP new_bitmap = RenderPage(new_page);
230 CompareBitmap(new_bitmap, 612, 792, "9823e1a21bd9b72b6a442ba4f12af946");
231 FPDFBitmap_Destroy(new_bitmap);
232 FPDF_ClosePage(new_page);
233 FPDF_CloseDocument(new_doc);
234}
235
236TEST_F(FPDFEditEmbeddertest, PathOnTopOfText) {
237 // Load document with some text
238 EXPECT_TRUE(OpenDocument("hello_world.pdf"));
239 FPDF_PAGE page = LoadPage(0);
240 EXPECT_NE(nullptr, page);
241
242 // Add an opaque rectangle on top of some of the text.
243 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
244 EXPECT_TRUE(FPDFPath_SetFillColor(red_rect, 255, 0, 0, 255));
245 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
246 FPDFPage_InsertObject(page, red_rect);
247 EXPECT_TRUE(FPDFPage_GenerateContent(page));
248
249 // Add a transparent triangle on top of other part of the text.
250 FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(20, 50);
251 EXPECT_TRUE(FPDFPath_SetFillColor(black_path, 0, 0, 0, 100));
252 EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
253 EXPECT_TRUE(FPDFPath_LineTo(black_path, 30, 80));
254 EXPECT_TRUE(FPDFPath_LineTo(black_path, 40, 10));
255 EXPECT_TRUE(FPDFPath_Close(black_path));
256 FPDFPage_InsertObject(page, black_path);
257 EXPECT_TRUE(FPDFPage_GenerateContent(page));
258
259 // Render and check the result. Text is slightly different on Mac.
260 FPDF_BITMAP bitmap = RenderPage(page);
261#if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
262 const char md5[] = "2f7c0deee10a9490538e195af64beb67";
263#else
264 const char md5[] = "17c942c76ff229200f2c98073bb60d85";
265#endif
266 CompareBitmap(bitmap, 200, 200, md5);
267 FPDFBitmap_Destroy(bitmap);
268 UnloadPage(page);
269}