1 // Copyright 2016 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <stdint.h>
6
7 #include <limits>
8 #include <memory>
9 #include <ostream>
10 #include <string>
11 #include <utility>
12 #include <vector>
13
14 #include "build/build_config.h"
15 #include "core/fpdfapi/font/cpdf_font.h"
16 #include "core/fpdfapi/page/cpdf_page.h"
17 #include "core/fpdfapi/page/cpdf_pageobject.h"
18 #include "core/fpdfapi/parser/cpdf_array.h"
19 #include "core/fpdfapi/parser/cpdf_dictionary.h"
20 #include "core/fpdfapi/parser/cpdf_number.h"
21 #include "core/fpdfapi/parser/cpdf_stream.h"
22 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
23 #include "core/fxcrt/check.h"
24 #include "core/fxcrt/compiler_specific.h"
25 #include "core/fxcrt/fx_codepage.h"
26 #include "core/fxcrt/fx_memcpy_wrappers.h"
27 #include "core/fxcrt/fx_system.h"
28 #include "core/fxcrt/span_util.h"
29 #include "core/fxge/cfx_defaultrenderdevice.h"
30 #include "core/fxge/fx_font.h"
31 #include "fpdfsdk/cpdfsdk_helpers.h"
32 #include "public/cpp/fpdf_scopers.h"
33 #include "public/fpdf_annot.h"
34 #include "public/fpdf_edit.h"
35 #include "public/fpdfview.h"
36 #include "testing/embedder_test.h"
37 #include "testing/embedder_test_constants.h"
38 #include "testing/fx_string_testhelpers.h"
39 #include "testing/gmock/include/gmock/gmock-matchers.h"
40 #include "testing/gtest/include/gtest/gtest.h"
41 #include "testing/utils/file_util.h"
42 #include "testing/utils/hash.h"
43 #include "testing/utils/path_service.h"
44
45 using pdfium::HelloWorldChecksum;
46 using testing::HasSubstr;
47 using testing::Not;
48 using testing::UnorderedElementsAreArray;
49
50 namespace {
51
52 const char kAllRemovedChecksum[] = "eee4600ac08b458ac7ac2320e225674c";
53
54 const wchar_t kBottomText[] = L"I'm at the bottom of the page";
55
BottomTextChecksum()56 const char* BottomTextChecksum() {
57 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
58 #if BUILDFLAG(IS_WIN)
59 return "5d8f2b613a2f9591a52373c72d6b88ee";
60 #elif BUILDFLAG(IS_APPLE)
61 return "8ca7dc6269ee68507389aa40eebcb9f8";
62 #else
63 return "c62d315856a558d2666b80d474831efe";
64 #endif
65 }
66 #if BUILDFLAG(IS_APPLE)
67 return "81636489006a31fcb00cf29efcdf7909";
68 #else
69 return "891dcb6e914c8360998055f1f47c9727";
70 #endif
71 }
72
FirstRemovedChecksum()73 const char* FirstRemovedChecksum() {
74 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
75 #if BUILDFLAG(IS_WIN)
76 return "251007e902e512d0359240ad957ee2dc";
77 #elif BUILDFLAG(IS_APPLE)
78 return "dcb929fae86d5b935888ce7f9f1ab71b";
79 #else
80 return "3006ab2b12d27246eae4faad509ac575";
81 #endif
82 }
83 #if BUILDFLAG(IS_APPLE)
84 return "a1dc2812692fcc7ee4f01ca77435df9d";
85 #else
86 return "e1477dc3b5b3b9c560814c4d1135a02b";
87 #endif
88 }
89
90 const wchar_t kLoadedFontText[] = L"I am testing my loaded font, WEE.";
91
LoadedFontTextChecksum()92 const char* LoadedFontTextChecksum() {
93 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
94 #if BUILDFLAG(IS_WIN)
95 return "b0efd562e84958f06bb006ba27d5f4bd";
96 #elif BUILDFLAG(IS_APPLE)
97 return "23e7874d160692b0ef3e0c8780f73dab";
98 #else
99 return "fc2334c350cbd0d2ae6076689da09741";
100 #endif
101 }
102 #if BUILDFLAG(IS_APPLE)
103 return "0f3e4a7d71f9e7eb8a1a0d69403b9848";
104 #else
105 return "d58570cc045dfb818b92cbabbd1a364c";
106 #endif
107 }
108
109 const char kRedRectangleChecksum[] = "66d02eaa6181e2c069ce2ea99beda497";
110
111 // In embedded_images.pdf.
112 const char kEmbeddedImage33Checksum[] = "cb3637934bb3b95a6e4ae1ea9eb9e56e";
113
NotoSansSCChecksum()114 const char* NotoSansSCChecksum() {
115 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
116 #if BUILDFLAG(IS_WIN)
117 return "a1bc9e4007dc2155e9f56bf16234573e";
118 #elif BUILDFLAG(IS_APPLE)
119 return "9a31fb87d1c6d2346bba22d1196041cd";
120 #else
121 return "5bb65e15fc0a685934cd5006dec08a76";
122 #endif
123 }
124 return "9a31fb87d1c6d2346bba22d1196041cd";
125 }
126
127 struct FPDFEditMoveEmbedderTestCase {
128 std::vector<int> page_indices;
129 int page_indices_len;
130 int dest_page_index;
131 // whether FPDF_MovePages() will succeed or fail
132 bool expected_result;
133 // expected order of pages if `expected_result` is true
134 std::vector<int> expected_order;
135 const char* const name;
136 };
137
operator <<(std::ostream & os,const FPDFEditMoveEmbedderTestCase & t)138 std::ostream& operator<<(std::ostream& os,
139 const FPDFEditMoveEmbedderTestCase& t) {
140 os << t.name << ": Indices are {";
141 for (size_t i = 0; i < t.page_indices.size(); ++i) {
142 os << t.page_indices[i];
143 if (i != t.page_indices.size() - 1) {
144 os << ", ";
145 }
146 }
147 os << "}, page order len is " << t.page_indices_len << ", dest page index is "
148 << t.dest_page_index << ", expected result is " << t.expected_result;
149 return os;
150 }
151
152 } // namespace
153
154 class FPDFEditEmbedderTest : public EmbedderTest {
155 protected:
CreateNewDocument()156 FPDF_DOCUMENT CreateNewDocument() {
157 CreateEmptyDocumentWithoutFormFillEnvironment();
158 cpdf_doc_ = CPDFDocumentFromFPDFDocument(document());
159 return document();
160 }
161
CheckFontDescriptor(const CPDF_Dictionary * font_dict,int font_type,bool bold,bool italic,pdfium::span<const uint8_t> span)162 void CheckFontDescriptor(const CPDF_Dictionary* font_dict,
163 int font_type,
164 bool bold,
165 bool italic,
166 pdfium::span<const uint8_t> span) {
167 RetainPtr<const CPDF_Dictionary> font_desc =
168 font_dict->GetDictFor("FontDescriptor");
169 ASSERT_TRUE(font_desc);
170 EXPECT_EQ("FontDescriptor", font_desc->GetNameFor("Type"));
171 ByteString font_name = font_desc->GetNameFor("FontName");
172 EXPECT_FALSE(font_name.IsEmpty());
173 EXPECT_EQ(font_dict->GetNameFor("BaseFont"), font_name);
174
175 // Check that the font descriptor has the required keys according to spec
176 // 1.7 Table 5.19
177 ASSERT_TRUE(font_desc->KeyExist("Flags"));
178
179 int font_flags = font_desc->GetIntegerFor("Flags");
180 EXPECT_EQ(bold, FontStyleIsForceBold(font_flags));
181 EXPECT_EQ(italic, FontStyleIsItalic(font_flags));
182 EXPECT_TRUE(FontStyleIsNonSymbolic(font_flags));
183 ASSERT_TRUE(font_desc->KeyExist("FontBBox"));
184
185 RetainPtr<const CPDF_Array> fontBBox = font_desc->GetArrayFor("FontBBox");
186 ASSERT_TRUE(fontBBox);
187 EXPECT_EQ(4u, fontBBox->size());
188 // Check that the coordinates are in the preferred order according to spec
189 // 1.7 Section 3.8.4
190 EXPECT_TRUE(fontBBox->GetIntegerAt(0) < fontBBox->GetIntegerAt(2));
191 EXPECT_TRUE(fontBBox->GetIntegerAt(1) < fontBBox->GetIntegerAt(3));
192
193 EXPECT_TRUE(font_desc->KeyExist("ItalicAngle"));
194 EXPECT_TRUE(font_desc->KeyExist("Ascent"));
195 EXPECT_TRUE(font_desc->KeyExist("Descent"));
196 EXPECT_TRUE(font_desc->KeyExist("CapHeight"));
197 EXPECT_TRUE(font_desc->KeyExist("StemV"));
198 ByteString present("FontFile");
199 ByteString absent("FontFile2");
200 if (font_type == FPDF_FONT_TRUETYPE)
201 std::swap(present, absent);
202 EXPECT_TRUE(font_desc->KeyExist(present));
203 EXPECT_FALSE(font_desc->KeyExist(absent));
204
205 auto streamAcc =
206 pdfium::MakeRetain<CPDF_StreamAcc>(font_desc->GetStreamFor(present));
207 streamAcc->LoadAllDataRaw();
208
209 // Check that the font stream is the one that was provided
210 ASSERT_EQ(span.size(), streamAcc->GetSize());
211 if (font_type == FPDF_FONT_TRUETYPE) {
212 ASSERT_EQ(static_cast<int>(span.size()), streamAcc->GetLength1ForTest());
213 }
214
215 pdfium::span<const uint8_t> stream_data = streamAcc->GetSpan();
216 for (size_t j = 0; j < span.size(); j++)
217 EXPECT_EQ(span[j], stream_data[j]) << " at byte " << j;
218 }
219
CheckCompositeFontWidths(const CPDF_Array * widths_array,CPDF_Font * typed_font)220 void CheckCompositeFontWidths(const CPDF_Array* widths_array,
221 CPDF_Font* typed_font) {
222 // Check that W array is in a format that conforms to PDF spec 1.7 section
223 // "Glyph Metrics in CIDFonts" (these checks are not
224 // implementation-specific).
225 EXPECT_GT(widths_array->size(), 1u);
226 int num_cids_checked = 0;
227 int cur_cid = 0;
228 for (size_t idx = 0; idx < widths_array->size(); idx++) {
229 int cid = widths_array->GetFloatAt(idx);
230 EXPECT_GE(cid, cur_cid);
231 ASSERT_FALSE(++idx == widths_array->size());
232 RetainPtr<const CPDF_Object> next = widths_array->GetObjectAt(idx);
233 if (next->IsArray()) {
234 // We are in the c [w1 w2 ...] case
235 const CPDF_Array* arr = next->AsArray();
236 int cnt = static_cast<int>(arr->size());
237 size_t inner_idx = 0;
238 for (cur_cid = cid; cur_cid < cid + cnt; cur_cid++) {
239 int width = arr->GetFloatAt(inner_idx++);
240 EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid))
241 << " at cid " << cur_cid;
242 }
243 num_cids_checked += cnt;
244 continue;
245 }
246 // Otherwise, are in the c_first c_last w case.
247 ASSERT_TRUE(next->IsNumber());
248 int last_cid = next->AsNumber()->GetInteger();
249 ASSERT_FALSE(++idx == widths_array->size());
250 int width = widths_array->GetFloatAt(idx);
251 for (cur_cid = cid; cur_cid <= last_cid; cur_cid++) {
252 EXPECT_EQ(width, typed_font->GetCharWidthF(cur_cid))
253 << " at cid " << cur_cid;
254 }
255 num_cids_checked += last_cid - cid + 1;
256 }
257 // Make sure we have a good amount of cids described
258 EXPECT_GT(num_cids_checked, 200);
259 }
cpdf_doc()260 CPDF_Document* cpdf_doc() { return cpdf_doc_; }
261
262 private:
263 CPDF_Document* cpdf_doc_;
264 };
265
266 namespace {
267
268 const char kExpectedPDF[] =
269 "%PDF-1.7\r\n"
270 "%\xA1\xB3\xC5\xD7\r\n"
271 "1 0 obj\r\n"
272 "<</Pages 2 0 R /Type/Catalog>>\r\n"
273 "endobj\r\n"
274 "2 0 obj\r\n"
275 "<</Count 1/Kids\\[ 4 0 R \\]/Type/Pages>>\r\n"
276 "endobj\r\n"
277 "3 0 obj\r\n"
278 "<</CreationDate\\(D:.*\\)/Creator\\(PDFium\\)>>\r\n"
279 "endobj\r\n"
280 "4 0 obj\r\n"
281 "<</MediaBox\\[ 0 0 640 480\\]/Parent 2 0 R "
282 "/Resources<<>>"
283 "/Rotate 0/Type/Page"
284 ">>\r\n"
285 "endobj\r\n"
286 "xref\r\n"
287 "0 5\r\n"
288 "0000000000 65535 f\r\n"
289 "0000000017 00000 n\r\n"
290 "0000000066 00000 n\r\n"
291 "0000000122 00000 n\r\n"
292 "0000000192 00000 n\r\n"
293 "trailer\r\n"
294 "<<\r\n"
295 "/Root 1 0 R\r\n"
296 "/Info 3 0 R\r\n"
297 "/Size 5/ID\\[<.*><.*>\\]>>\r\n"
298 "startxref\r\n"
299 "285\r\n"
300 "%%EOF\r\n";
301
302 } // namespace
303
TEST_F(FPDFEditEmbedderTest,EmbedNotoSansSCFont)304 TEST_F(FPDFEditEmbedderTest, EmbedNotoSansSCFont) {
305 CreateEmptyDocument();
306 ScopedFPDFPage page(FPDFPage_New(document(), 0, 400, 400));
307 std::string font_path;
308 ASSERT_TRUE(PathService::GetThirdPartyFilePath(
309 "NotoSansCJK/NotoSansSC-Regular.subset.otf", &font_path));
310
311 std::vector<uint8_t> font_data = GetFileContents(font_path.c_str());
312 ASSERT_FALSE(font_data.empty());
313
314 ScopedFPDFFont font(FPDFText_LoadFont(document(), font_data.data(),
315 font_data.size(), FPDF_FONT_TRUETYPE,
316 /*cid=*/true));
317 ASSERT_TRUE(font);
318 FPDF_PAGEOBJECT text_object =
319 FPDFPageObj_CreateTextObj(document(), font.get(), 20.0f);
320 EXPECT_TRUE(text_object);
321
322 // Test the characters which are either mapped to one single unicode or
323 // multiple unicodes in the embedded font.
324 ScopedFPDFWideString text = GetFPDFWideString(L"这是第一句。 这是第二行。");
325 EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
326
327 const FS_MATRIX matrix{1, 0, 0, 1, 50, 200};
328 ASSERT_TRUE(FPDFPageObj_TransformF(text_object, &matrix));
329 FPDFPage_InsertObject(page.get(), text_object);
330 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
331
332 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
333 CompareBitmap(page_bitmap.get(), 400, 400, NotoSansSCChecksum());
334
335 ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
336 VerifySavedDocument(400, 400, NotoSansSCChecksum());
337 }
338
TEST_F(FPDFEditEmbedderTest,EmbedNotoSansSCFontWithCharcodes)339 TEST_F(FPDFEditEmbedderTest, EmbedNotoSansSCFontWithCharcodes) {
340 CreateEmptyDocument();
341 ScopedFPDFPage page(FPDFPage_New(document(), 0, 400, 400));
342 std::string font_path;
343 ASSERT_TRUE(PathService::GetThirdPartyFilePath(
344 "NotoSansCJK/NotoSansSC-Regular.subset.otf", &font_path));
345
346 std::vector<uint8_t> font_data = GetFileContents(font_path.c_str());
347 ASSERT_FALSE(font_data.empty());
348
349 ScopedFPDFFont font(FPDFText_LoadFont(document(), font_data.data(),
350 font_data.size(), FPDF_FONT_TRUETYPE,
351 /*cid=*/true));
352 ASSERT_TRUE(font);
353 FPDF_PAGEOBJECT text_object =
354 FPDFPageObj_CreateTextObj(document(), font.get(), 20.0f);
355 EXPECT_TRUE(text_object);
356
357 // Same as `text` in the EmbedNotoSansSCFont test case above.
358 const std::vector<uint32_t> charcodes = {9, 6, 7, 3, 5, 2, 1,
359 9, 6, 7, 4, 8, 2};
360 EXPECT_TRUE(
361 FPDFText_SetCharcodes(text_object, charcodes.data(), charcodes.size()));
362
363 const FS_MATRIX matrix{1, 0, 0, 1, 50, 200};
364 ASSERT_TRUE(FPDFPageObj_TransformF(text_object, &matrix));
365 FPDFPage_InsertObject(page.get(), text_object);
366 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
367
368 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
369 CompareBitmap(page_bitmap.get(), 400, 400, NotoSansSCChecksum());
370
371 ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
372 VerifySavedDocument(400, 400, NotoSansSCChecksum());
373 }
374
TEST_F(FPDFEditEmbedderTest,Bug2094)375 TEST_F(FPDFEditEmbedderTest, Bug2094) {
376 CreateEmptyDocument();
377 ScopedFPDFPage page(FPDFPage_New(document(), 0, 400, 400));
378 std::string font_path = PathService::GetTestFilePath("fonts/bug_2094.ttf");
379 ASSERT_FALSE(font_path.empty());
380
381 std::vector<uint8_t> font_data = GetFileContents(font_path.c_str());
382 ASSERT_FALSE(font_data.empty());
383
384 ScopedFPDFFont font(FPDFText_LoadFont(document(), font_data.data(),
385 font_data.size(), FPDF_FONT_TRUETYPE,
386 /*cid=*/true));
387 EXPECT_TRUE(font);
388 }
389
TEST_F(FPDFEditEmbedderTest,EmptyCreation)390 TEST_F(FPDFEditEmbedderTest, EmptyCreation) {
391 CreateEmptyDocument();
392 FPDF_PAGE page = FPDFPage_New(document(), 0, 640.0, 480.0);
393 EXPECT_TRUE(page);
394 // The FPDFPage_GenerateContent call should do nothing.
395 EXPECT_TRUE(FPDFPage_GenerateContent(page));
396 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
397
398 EXPECT_THAT(GetString(), testing::MatchesRegex(std::string(
399 kExpectedPDF, sizeof(kExpectedPDF))));
400 FPDF_ClosePage(page);
401 }
402
403 // Regression test for https://crbug.com/667012
TEST_F(FPDFEditEmbedderTest,RasterizePDF)404 TEST_F(FPDFEditEmbedderTest, RasterizePDF) {
405 const char kAllBlackChecksum[] = "5708fc5c4a8bd0abde99c8e8f0390615";
406
407 // Get the bitmap for the original document.
408 ScopedFPDFBitmap orig_bitmap;
409 {
410 ASSERT_TRUE(OpenDocument("black.pdf"));
411 ScopedEmbedderTestPage orig_page = LoadScopedPage(0);
412 ASSERT_TRUE(orig_page);
413 orig_bitmap = RenderLoadedPage(orig_page.get());
414 CompareBitmap(orig_bitmap.get(), 612, 792, kAllBlackChecksum);
415 }
416
417 // Create a new document from |orig_bitmap| and save it.
418 {
419 ScopedFPDFDocument temp_doc(FPDF_CreateNewDocument());
420 ScopedFPDFPage temp_page(FPDFPage_New(temp_doc.get(), 0, 612, 792));
421
422 // Add the bitmap to an image object and add the image object to the output
423 // page.
424 ScopedFPDFPageObject temp_img(FPDFPageObj_NewImageObj(temp_doc.get()));
425 FPDF_PAGE pages_array[] = {temp_page.get()};
426 EXPECT_TRUE(FPDFImageObj_SetBitmap(pages_array, 1, temp_img.get(),
427 orig_bitmap.get()));
428 static constexpr FS_MATRIX kLetterScaleMatrix{612, 0, 0, 792, 0, 0};
429 EXPECT_TRUE(FPDFPageObj_SetMatrix(temp_img.get(), &kLetterScaleMatrix));
430 FPDFPage_InsertObject(temp_page.get(), temp_img.release());
431 EXPECT_TRUE(FPDFPage_GenerateContent(temp_page.get()));
432 EXPECT_TRUE(FPDF_SaveAsCopy(temp_doc.get(), this, 0));
433 }
434
435 // Get the generated content. Make sure it is at least as big as the original
436 // PDF.
437 EXPECT_GT(GetString().size(), 923u);
438 VerifySavedDocument(612, 792, kAllBlackChecksum);
439 }
440
TEST_F(FPDFEditEmbedderTest,AddPaths)441 TEST_F(FPDFEditEmbedderTest, AddPaths) {
442 // Start with a blank page
443 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
444 ASSERT_TRUE(page);
445
446 // We will first add a red rectangle
447 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
448 ASSERT_TRUE(red_rect);
449 // Expect false when trying to set colors out of range
450 EXPECT_FALSE(FPDFPageObj_SetStrokeColor(red_rect, 100, 100, 100, 300));
451 EXPECT_FALSE(FPDFPageObj_SetFillColor(red_rect, 200, 256, 200, 0));
452
453 // Fill rectangle with red and insert to the page
454 EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
455 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
456
457 int fillmode = FPDF_FILLMODE_NONE;
458 FPDF_BOOL stroke = true;
459 EXPECT_TRUE(FPDFPath_GetDrawMode(red_rect, &fillmode, &stroke));
460 EXPECT_EQ(FPDF_FILLMODE_ALTERNATE, fillmode);
461 EXPECT_FALSE(stroke);
462
463 static constexpr FS_MATRIX kMatrix = {1, 2, 3, 4, 5, 6};
464 EXPECT_TRUE(FPDFPageObj_SetMatrix(red_rect, &kMatrix));
465
466 FS_MATRIX matrix;
467 EXPECT_TRUE(FPDFPageObj_GetMatrix(red_rect, &matrix));
468 EXPECT_FLOAT_EQ(1.0f, matrix.a);
469 EXPECT_FLOAT_EQ(2.0f, matrix.b);
470 EXPECT_FLOAT_EQ(3.0f, matrix.c);
471 EXPECT_FLOAT_EQ(4.0f, matrix.d);
472 EXPECT_FLOAT_EQ(5.0f, matrix.e);
473 EXPECT_FLOAT_EQ(6.0f, matrix.f);
474
475 // Set back the identity matrix.
476 matrix = {1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f};
477 EXPECT_TRUE(FPDFPageObj_SetMatrix(red_rect, &matrix));
478
479 FPDFPage_InsertObject(page, red_rect);
480 {
481 ScopedFPDFBitmap page_bitmap = RenderPage(page);
482 CompareBitmap(page_bitmap.get(), 612, 792, kRedRectangleChecksum);
483 }
484
485 // Now add to that a green rectangle with some medium alpha
486 FPDF_PAGEOBJECT green_rect = FPDFPageObj_CreateNewRect(100, 100, 40, 40);
487 EXPECT_TRUE(FPDFPageObj_SetFillColor(green_rect, 0, 255, 0, 128));
488
489 // Make sure the type of the rectangle is a path.
490 EXPECT_EQ(FPDF_PAGEOBJ_PATH, FPDFPageObj_GetType(green_rect));
491
492 // Make sure we get back the same color we set previously.
493 unsigned int R;
494 unsigned int G;
495 unsigned int B;
496 unsigned int A;
497 EXPECT_TRUE(FPDFPageObj_GetFillColor(green_rect, &R, &G, &B, &A));
498 EXPECT_EQ(0u, R);
499 EXPECT_EQ(255u, G);
500 EXPECT_EQ(0u, B);
501 EXPECT_EQ(128u, A);
502
503 // Make sure the path has 5 points (1 CFX_Path::Point::Type::kMove and 4
504 // CFX_Path::Point::Type::kLine).
505 ASSERT_EQ(5, FPDFPath_CountSegments(green_rect));
506 // Verify actual coordinates.
507 FPDF_PATHSEGMENT segment = FPDFPath_GetPathSegment(green_rect, 0);
508 float x;
509 float y;
510 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
511 EXPECT_EQ(100, x);
512 EXPECT_EQ(100, y);
513 EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
514 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
515 segment = FPDFPath_GetPathSegment(green_rect, 1);
516 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
517 EXPECT_EQ(100, x);
518 EXPECT_EQ(140, y);
519 EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
520 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
521 segment = FPDFPath_GetPathSegment(green_rect, 2);
522 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
523 EXPECT_EQ(140, x);
524 EXPECT_EQ(140, y);
525 EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
526 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
527 segment = FPDFPath_GetPathSegment(green_rect, 3);
528 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
529 EXPECT_EQ(140, x);
530 EXPECT_EQ(100, y);
531 EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
532 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
533 segment = FPDFPath_GetPathSegment(green_rect, 4);
534 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
535 EXPECT_EQ(100, x);
536 EXPECT_EQ(100, y);
537 EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
538 EXPECT_TRUE(FPDFPathSegment_GetClose(segment));
539
540 EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect, FPDF_FILLMODE_WINDING, 0));
541 FPDFPage_InsertObject(page, green_rect);
542 {
543 ScopedFPDFBitmap page_bitmap = RenderPage(page);
544 CompareBitmap(page_bitmap.get(), 612, 792,
545 "7b0b87604594e773add528fae567a558");
546 }
547
548 // Add a black triangle.
549 FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(400, 100);
550 EXPECT_TRUE(FPDFPageObj_SetFillColor(black_path, 0, 0, 0, 200));
551 EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
552 EXPECT_TRUE(FPDFPath_LineTo(black_path, 400, 200));
553 EXPECT_TRUE(FPDFPath_LineTo(black_path, 300, 100));
554 EXPECT_TRUE(FPDFPath_Close(black_path));
555
556 // Make sure the path has 3 points (1 CFX_Path::Point::Type::kMove and 2
557 // CFX_Path::Point::Type::kLine).
558 ASSERT_EQ(3, FPDFPath_CountSegments(black_path));
559 // Verify actual coordinates.
560 segment = FPDFPath_GetPathSegment(black_path, 0);
561 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
562 EXPECT_EQ(400, x);
563 EXPECT_EQ(100, y);
564 EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
565 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
566 segment = FPDFPath_GetPathSegment(black_path, 1);
567 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
568 EXPECT_EQ(400, x);
569 EXPECT_EQ(200, y);
570 EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
571 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
572 segment = FPDFPath_GetPathSegment(black_path, 2);
573 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
574 EXPECT_EQ(300, x);
575 EXPECT_EQ(100, y);
576 EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
577 EXPECT_TRUE(FPDFPathSegment_GetClose(segment));
578 // Make sure out of bounds index access fails properly.
579 EXPECT_FALSE(FPDFPath_GetPathSegment(black_path, 3));
580
581 FPDFPage_InsertObject(page, black_path);
582 {
583 ScopedFPDFBitmap page_bitmap = RenderPage(page);
584 CompareBitmap(page_bitmap.get(), 612, 792,
585 "eadc8020a14dfcf091da2688733d8806");
586 }
587
588 // Now add a more complex blue path.
589 FPDF_PAGEOBJECT blue_path = FPDFPageObj_CreateNewPath(200, 200);
590 EXPECT_TRUE(FPDFPageObj_SetFillColor(blue_path, 0, 0, 255, 255));
591 EXPECT_TRUE(FPDFPath_SetDrawMode(blue_path, FPDF_FILLMODE_WINDING, 0));
592 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 230, 230));
593 EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 250, 250, 280, 280, 300, 300));
594 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 325, 325));
595 EXPECT_TRUE(FPDFPath_LineTo(blue_path, 350, 325));
596 EXPECT_TRUE(FPDFPath_BezierTo(blue_path, 375, 330, 390, 360, 400, 400));
597 EXPECT_TRUE(FPDFPath_Close(blue_path));
598 FPDFPage_InsertObject(page, blue_path);
599 {
600 const char* blue_path_checksum = []() {
601 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
602 return "ed14c60702b1489c597c7d46ece7f86d";
603 }
604 return "9823e1a21bd9b72b6a442ba4f12af946";
605 }();
606 ScopedFPDFBitmap page_bitmap = RenderPage(page);
607 CompareBitmap(page_bitmap.get(), 612, 792, blue_path_checksum);
608 }
609
610 // Now save the result, closing the page and document.
611 EXPECT_TRUE(FPDFPage_GenerateContent(page));
612 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
613 FPDF_ClosePage(page);
614
615 // Render the saved result. The checksum will change due to floating point
616 // precision error.
617 {
618 const char* last_checksum = []() {
619 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
620 return "ed14c60702b1489c597c7d46ece7f86d";
621 }
622 return "9823e1a21bd9b72b6a442ba4f12af946";
623 }();
624 VerifySavedDocument(612, 792, last_checksum);
625 }
626 }
627
TEST_F(FPDFEditEmbedderTest,ClipPath)628 TEST_F(FPDFEditEmbedderTest, ClipPath) {
629 // Load document with a clipped rectangle.
630 ASSERT_TRUE(OpenDocument("clip_path.pdf"));
631 ScopedEmbedderTestPage page = LoadScopedPage(0);
632 ASSERT_TRUE(page);
633
634 ASSERT_EQ(1, FPDFPage_CountObjects(page.get()));
635
636 FPDF_PAGEOBJECT triangle = FPDFPage_GetObject(page.get(), 0);
637 ASSERT_TRUE(triangle);
638
639 // Test that we got the expected triangle.
640 ASSERT_EQ(4, FPDFPath_CountSegments(triangle));
641
642 FPDF_PATHSEGMENT segment = FPDFPath_GetPathSegment(triangle, 0);
643 float x;
644 float y;
645 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
646 EXPECT_EQ(10, x);
647 EXPECT_EQ(10, y);
648 EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
649 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
650
651 segment = FPDFPath_GetPathSegment(triangle, 1);
652 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
653 EXPECT_EQ(25, x);
654 EXPECT_EQ(40, y);
655 EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
656 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
657
658 segment = FPDFPath_GetPathSegment(triangle, 2);
659 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
660 EXPECT_EQ(40, x);
661 EXPECT_EQ(10, y);
662 EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
663 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
664
665 segment = FPDFPath_GetPathSegment(triangle, 3);
666 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
667 EXPECT_TRUE(FPDFPathSegment_GetClose(segment));
668
669 // Test FPDFPageObj_GetClipPath().
670 ASSERT_EQ(nullptr, FPDFPageObj_GetClipPath(nullptr));
671
672 FPDF_CLIPPATH clip_path = FPDFPageObj_GetClipPath(triangle);
673 ASSERT_TRUE(clip_path);
674
675 // Test FPDFClipPath_CountPaths().
676 ASSERT_EQ(-1, FPDFClipPath_CountPaths(nullptr));
677 ASSERT_EQ(1, FPDFClipPath_CountPaths(clip_path));
678
679 // Test FPDFClipPath_CountPathSegments().
680 ASSERT_EQ(-1, FPDFClipPath_CountPathSegments(nullptr, 0));
681 ASSERT_EQ(-1, FPDFClipPath_CountPathSegments(clip_path, -1));
682 ASSERT_EQ(-1, FPDFClipPath_CountPathSegments(clip_path, 1));
683 ASSERT_EQ(4, FPDFClipPath_CountPathSegments(clip_path, 0));
684
685 // FPDFClipPath_GetPathSegment() negative testing.
686 ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(nullptr, 0, 0));
687 ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(clip_path, -1, 0));
688 ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(clip_path, 1, 0));
689 ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(clip_path, 0, -1));
690 ASSERT_EQ(nullptr, FPDFClipPath_GetPathSegment(clip_path, 0, 4));
691
692 // FPDFClipPath_GetPathSegment() positive testing.
693 segment = FPDFClipPath_GetPathSegment(clip_path, 0, 0);
694 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
695 EXPECT_EQ(10, x);
696 EXPECT_EQ(15, y);
697 EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
698 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
699
700 segment = FPDFClipPath_GetPathSegment(clip_path, 0, 1);
701 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
702 EXPECT_EQ(40, x);
703 EXPECT_EQ(15, y);
704 EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
705 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
706
707 segment = FPDFClipPath_GetPathSegment(clip_path, 0, 2);
708 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
709 EXPECT_EQ(40, x);
710 EXPECT_EQ(35, y);
711 EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
712 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
713
714 segment = FPDFClipPath_GetPathSegment(clip_path, 0, 3);
715 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
716 EXPECT_EQ(10, x);
717 EXPECT_EQ(35, y);
718 EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
719 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
720 }
721
TEST_F(FPDFEditEmbedderTest,Bug1399)722 TEST_F(FPDFEditEmbedderTest, Bug1399) {
723 // Load document with a clipped rectangle.
724 ASSERT_TRUE(OpenDocument("bug_1399.pdf"));
725 ScopedEmbedderTestPage page = LoadScopedPage(0);
726 ASSERT_TRUE(page);
727
728 ASSERT_EQ(7, FPDFPage_CountObjects(page.get()));
729
730 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page.get(), 0);
731 ASSERT_TRUE(obj);
732
733 ASSERT_EQ(2, FPDFPath_CountSegments(obj));
734
735 FPDF_PATHSEGMENT segment = FPDFPath_GetPathSegment(obj, 0);
736 float x;
737 float y;
738 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
739 EXPECT_FLOAT_EQ(107.718f, x);
740 EXPECT_FLOAT_EQ(719.922f, y);
741 EXPECT_EQ(FPDF_SEGMENT_MOVETO, FPDFPathSegment_GetType(segment));
742 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
743
744 segment = FPDFPath_GetPathSegment(obj, 1);
745 EXPECT_TRUE(FPDFPathSegment_GetPoint(segment, &x, &y));
746 EXPECT_FLOAT_EQ(394.718f, x);
747 EXPECT_FLOAT_EQ(719.922f, y);
748 EXPECT_EQ(FPDF_SEGMENT_LINETO, FPDFPathSegment_GetType(segment));
749 EXPECT_FALSE(FPDFPathSegment_GetClose(segment));
750
751 FPDF_CLIPPATH clip_path = FPDFPageObj_GetClipPath(obj);
752 ASSERT_TRUE(clip_path);
753
754 EXPECT_EQ(-1, FPDFClipPath_CountPaths(clip_path));
755 EXPECT_EQ(-1, FPDFClipPath_CountPathSegments(clip_path, 0));
756 EXPECT_FALSE(FPDFClipPath_GetPathSegment(clip_path, 0, 0));
757 }
758
TEST_F(FPDFEditEmbedderTest,Bug1549)759 TEST_F(FPDFEditEmbedderTest, Bug1549) {
760 static const char kOriginalChecksum[] = "126366fb95e6caf8ea196780075b22b2";
761 static const char kRemovedChecksum[] = "6ec2f27531927882624b37bc7d8e12f4";
762
763 ASSERT_TRUE(OpenDocument("bug_1549.pdf"));
764 ScopedEmbedderTestPage page = LoadScopedPage(0);
765
766 {
767 ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get());
768 CompareBitmap(bitmap.get(), 100, 150, kOriginalChecksum);
769
770 ScopedFPDFPageObject obj(FPDFPage_GetObject(page.get(), 0));
771 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj.get()));
772 ASSERT_TRUE(FPDFPage_RemoveObject(page.get(), obj.get()));
773 }
774
775 ASSERT_TRUE(FPDFPage_GenerateContent(page.get()));
776
777 {
778 ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get());
779 CompareBitmap(bitmap.get(), 100, 150, kRemovedChecksum);
780 }
781
782 ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
783
784 // TODO(crbug.com/pdfium/1549): Should be `kRemovedChecksum`.
785 VerifySavedDocument(100, 150, "4f9889cd5993db20f1ab37d677ac8d26");
786 }
787
TEST_F(FPDFEditEmbedderTest,SetText)788 TEST_F(FPDFEditEmbedderTest, SetText) {
789 // Load document with some text.
790 ASSERT_TRUE(OpenDocument("hello_world.pdf"));
791 ScopedEmbedderTestPage page = LoadScopedPage(0);
792 ASSERT_TRUE(page);
793
794 // Get the "Hello, world!" text object and change it.
795 ASSERT_EQ(2, FPDFPage_CountObjects(page.get()));
796 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 0);
797 ASSERT_TRUE(page_object);
798 ScopedFPDFWideString text1 = GetFPDFWideString(L"Changed for SetText test");
799 EXPECT_TRUE(FPDFText_SetText(page_object, text1.get()));
800
801 // Verify the "Hello, world!" text is gone and "Changed for SetText test" is
802 // now displayed.
803 ASSERT_EQ(2, FPDFPage_CountObjects(page.get()));
804
805 const char* changed_checksum = []() {
806 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
807 #if BUILDFLAG(IS_WIN)
808 return "e1c530ca0705424f19a1b7ff0bffdbaa";
809 #elif BUILDFLAG(IS_APPLE)
810 return "c65881cb16125c23e5513a16dc68f3a2";
811 #else
812 return "4a8345a139507932729e07d4831cbe2b";
813 #endif
814 }
815 #if BUILDFLAG(IS_APPLE)
816 return "b720e83476fd6819d47c533f1f43c728";
817 #else
818 return "9a85b9354a69c61772ed24151c140f46";
819 #endif
820 }();
821 {
822 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
823 CompareBitmap(page_bitmap.get(), 200, 200, changed_checksum);
824 }
825
826 // Now save the result.
827 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
828 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
829
830
831 // Re-open the file and check the changes were kept in the saved .pdf.
832 ASSERT_TRUE(OpenSavedDocument());
833 FPDF_PAGE saved_page = LoadSavedPage(0);
834 ASSERT_TRUE(saved_page);
835 EXPECT_EQ(2, FPDFPage_CountObjects(saved_page));
836 {
837 ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
838 CompareBitmap(page_bitmap.get(), 200, 200, changed_checksum);
839 }
840
841 CloseSavedPage(saved_page);
842 CloseSavedDocument();
843 }
844
TEST_F(FPDFEditEmbedderTest,SetCharcodesBadParams)845 TEST_F(FPDFEditEmbedderTest, SetCharcodesBadParams) {
846 ASSERT_TRUE(OpenDocument("hello_world.pdf"));
847 ScopedEmbedderTestPage page = LoadScopedPage(0);
848 ASSERT_TRUE(page);
849
850 ASSERT_EQ(2, FPDFPage_CountObjects(page.get()));
851 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 0);
852 ASSERT_TRUE(page_object);
853
854 const uint32_t kDummyValue = 42;
855 EXPECT_FALSE(FPDFText_SetCharcodes(nullptr, nullptr, 0));
856 EXPECT_FALSE(FPDFText_SetCharcodes(nullptr, nullptr, 1));
857 EXPECT_FALSE(FPDFText_SetCharcodes(nullptr, &kDummyValue, 0));
858 EXPECT_FALSE(FPDFText_SetCharcodes(nullptr, &kDummyValue, 1));
859 EXPECT_FALSE(FPDFText_SetCharcodes(page_object, nullptr, 1));
860 }
861
TEST_F(FPDFEditEmbedderTest,SetTextKeepClippingPath)862 TEST_F(FPDFEditEmbedderTest, SetTextKeepClippingPath) {
863 // Load document with some text, with parts clipped.
864 ASSERT_TRUE(OpenDocument("bug_1558.pdf"));
865 ScopedEmbedderTestPage page = LoadScopedPage(0);
866 ASSERT_TRUE(page);
867
868 const char* original_checksum = []() {
869 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
870 #if BUILDFLAG(IS_WIN)
871 return "0822ec5d476e8371544ef4bb7a0596e1";
872 #elif BUILDFLAG(IS_APPLE)
873 return "721dae4e2258a52a000af88d09ec75ca";
874 #else
875 return "3c04e3acc732faaf39fb0c19efd056ac";
876 #endif
877 }
878 #if BUILDFLAG(IS_APPLE)
879 return "ae7a25c85e0e2dd0c5cb9dd5cd37f6df";
880 #else
881 return "7af7fe5b281298261eb66ac2d22f5054";
882 #endif
883 }();
884 {
885 // When opened before any editing and saving, the clipping path is rendered.
886 ScopedFPDFBitmap original_bitmap = RenderPage(page.get());
887 CompareBitmap(original_bitmap.get(), 200, 200, original_checksum);
888 }
889
890 // "Change" the text in the objects to their current values to force them to
891 // regenerate when saving.
892 {
893 ScopedFPDFTextPage text_page(FPDFText_LoadPage(page.get()));
894 ASSERT_TRUE(text_page);
895 const int obj_count = FPDFPage_CountObjects(page.get());
896 ASSERT_EQ(2, obj_count);
897 for (int i = 0; i < obj_count; ++i) {
898 FPDF_PAGEOBJECT text_obj = FPDFPage_GetObject(page.get(), i);
899 ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_obj));
900 unsigned long size =
901 FPDFTextObj_GetText(text_obj, text_page.get(),
902 /*buffer=*/nullptr, /*length=*/0);
903 ASSERT_GT(size, 0u);
904 std::vector<FPDF_WCHAR> buffer = GetFPDFWideStringBuffer(size);
905 ASSERT_EQ(size, FPDFTextObj_GetText(text_obj, text_page.get(),
906 buffer.data(), size));
907 EXPECT_TRUE(FPDFText_SetText(text_obj, buffer.data()));
908 }
909 }
910
911 {
912 // After editing but before saving, the clipping path is retained.
913 ScopedFPDFBitmap edited_bitmap = RenderPage(page.get());
914 CompareBitmap(edited_bitmap.get(), 200, 200, original_checksum);
915 }
916
917 // Save the file.
918 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
919 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
920
921 // Open the saved copy and render it.
922 ASSERT_TRUE(OpenSavedDocument());
923 FPDF_PAGE saved_page = LoadSavedPage(0);
924 ASSERT_TRUE(saved_page);
925
926 {
927 ScopedFPDFBitmap saved_bitmap = RenderSavedPage(saved_page);
928 CompareBitmap(saved_bitmap.get(), 200, 200, original_checksum);
929 }
930
931 CloseSavedPage(saved_page);
932 CloseSavedDocument();
933 }
934
TEST_F(FPDFEditEmbedderTest,Bug1574)935 TEST_F(FPDFEditEmbedderTest, Bug1574) {
936 // Load document with some text within a clipping path.
937 ASSERT_TRUE(OpenDocument("bug_1574.pdf"));
938 ScopedEmbedderTestPage page = LoadScopedPage(0);
939 ASSERT_TRUE(page);
940
941 const char* original_checksum = []() {
942 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
943 #if BUILDFLAG(IS_WIN)
944 return "6f22adb3ba2a2c60a940bfb52e14dd58";
945 #elif BUILDFLAG(IS_APPLE)
946 return "afa2260cbe84be78867940d72420d0b4";
947 #else
948 return "d76a31d931a350f0809226a41029a9a4";
949 #endif
950 }
951 #if BUILDFLAG(IS_APPLE)
952 return "1226bc2b8072622eb28f52321876e814";
953 #else
954 return "c5241eef60b9eac68ed1f2a5fd002703";
955 #endif
956 }();
957 {
958 // When opened before any editing and saving, the text object is rendered.
959 ScopedFPDFBitmap original_bitmap = RenderPage(page.get());
960 CompareBitmap(original_bitmap.get(), 200, 300, original_checksum);
961 }
962
963 // "Change" the text in the objects to their current values to force them to
964 // regenerate when saving.
965 {
966 ScopedFPDFTextPage text_page(FPDFText_LoadPage(page.get()));
967 ASSERT_TRUE(text_page);
968
969 ASSERT_EQ(2, FPDFPage_CountObjects(page.get()));
970 FPDF_PAGEOBJECT text_obj = FPDFPage_GetObject(page.get(), 1);
971 ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_obj));
972
973 unsigned long size = FPDFTextObj_GetText(text_obj, text_page.get(),
974 /*buffer=*/nullptr, /*length=*/0);
975 ASSERT_GT(size, 0u);
976 std::vector<FPDF_WCHAR> buffer = GetFPDFWideStringBuffer(size);
977 ASSERT_EQ(size, FPDFTextObj_GetText(text_obj, text_page.get(),
978 buffer.data(), size));
979 EXPECT_TRUE(FPDFText_SetText(text_obj, buffer.data()));
980 }
981
982 // Save the file.
983 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
984 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
985
986 // Open the saved copy and render it.
987 ASSERT_TRUE(OpenSavedDocument());
988 FPDF_PAGE saved_page = LoadSavedPage(0);
989 ASSERT_TRUE(saved_page);
990
991 {
992 ScopedFPDFBitmap saved_bitmap = RenderSavedPage(saved_page);
993 CompareBitmap(saved_bitmap.get(), 200, 300, original_checksum);
994 }
995
996 CloseSavedPage(saved_page);
997 CloseSavedDocument();
998 }
999
TEST_F(FPDFEditEmbedderTest,Bug1893)1000 TEST_F(FPDFEditEmbedderTest, Bug1893) {
1001 ASSERT_TRUE(OpenDocument("bug_1893.pdf"));
1002 ScopedEmbedderTestPage page = LoadScopedPage(0);
1003 {
1004 const char* original_checksum = []() {
1005 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
1006 #if BUILDFLAG(IS_WIN)
1007 return "10c8257bb54b4431196d963d68d45f12";
1008 #elif BUILDFLAG(IS_APPLE)
1009 return "c42eef2028cb86a0a8601d61707b126f";
1010 #else
1011 return "d8be4379e729242785945458924318a3";
1012 #endif
1013 }
1014 #if BUILDFLAG(IS_APPLE)
1015 return "0964322399241618539b474dbf9d40c6";
1016 #else
1017 return "c3672f206e47d98677401f1617ad56eb";
1018 #endif
1019 }();
1020
1021 ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get());
1022 CompareBitmap(bitmap.get(), 200, 300, original_checksum);
1023 }
1024
1025 EXPECT_EQ(3, FPDFPage_CountObjects(page.get()));
1026
1027 const char* removed_checksum = []() {
1028 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
1029 #if BUILDFLAG(IS_WIN)
1030 return "95484d03b9da898617f297b1429f7f84";
1031 #elif BUILDFLAG(IS_APPLE)
1032 return "7222709eca0e8f72a66ce06283f7c10f";
1033 #else
1034 return "4a02191e033dddeb2110d55af3f14544";
1035 #endif
1036 }
1037 #if BUILDFLAG(IS_APPLE)
1038 return "d0837f2b8809a5902d3c4219441fbafe";
1039 #else
1040 return "e9c0cbd6adcb2151b4e36a61ab26a20a";
1041 #endif
1042 }();
1043
1044 // Remove the underline and regenerate the page content.
1045 {
1046 ScopedFPDFPageObject object(FPDFPage_GetObject(page.get(), 0));
1047 ASSERT_TRUE(FPDFPage_RemoveObject(page.get(), object.get()));
1048 ASSERT_TRUE(FPDFPage_GenerateContent(page.get()));
1049
1050 ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get());
1051 CompareBitmap(bitmap.get(), 200, 300, removed_checksum);
1052 }
1053
1054 ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1055
1056 {
1057 ASSERT_TRUE(OpenSavedDocument());
1058 FPDF_PAGE saved_page = LoadSavedPage(0);
1059 ScopedFPDFBitmap bitmap = RenderSavedPageWithFlags(saved_page, FPDF_ANNOT);
1060 CompareBitmap(bitmap.get(), 200, 300, removed_checksum);
1061 CloseSavedPage(saved_page);
1062 CloseSavedDocument();
1063 }
1064 }
1065
TEST_F(FPDFEditEmbedderTest,RemoveTextObject)1066 TEST_F(FPDFEditEmbedderTest, RemoveTextObject) {
1067 // Load document with some text.
1068 ASSERT_TRUE(OpenDocument("hello_world.pdf"));
1069 ScopedEmbedderTestPage page = LoadScopedPage(0);
1070 ASSERT_TRUE(page);
1071
1072 // Show what the original file looks like.
1073 {
1074 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
1075 CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldChecksum());
1076 }
1077
1078 // Check the initial state of the text page as well. `text_page` must be freed
1079 // before calling FPDFPage_RemoveObject() below, so ASAN does not report
1080 // dangling pointers.
1081 {
1082 ScopedFPDFTextPage text_page(FPDFText_LoadPage(page.get()));
1083 ASSERT_TRUE(text_page);
1084 EXPECT_EQ(30, FPDFText_CountChars(text_page.get()));
1085 EXPECT_EQ(0, FPDFText_GetFontWeight(text_page.get(), 0));
1086 }
1087
1088 // Get the "Hello, world!" text object and remove it.
1089 ASSERT_EQ(2, FPDFPage_CountObjects(page.get()));
1090 {
1091 ScopedFPDFPageObject page_object(FPDFPage_GetObject(page.get(), 0));
1092 ASSERT_TRUE(page_object);
1093 ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(page_object.get()));
1094 EXPECT_TRUE(FPDFPage_RemoveObject(page.get(), page_object.get()));
1095 }
1096 ASSERT_EQ(1, FPDFPage_CountObjects(page.get()));
1097
1098 // Verify the "Hello, world!" text is gone.
1099 {
1100 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
1101 CompareBitmap(page_bitmap.get(), 200, 200, FirstRemovedChecksum());
1102 }
1103
1104 // Create a new text page, which has updated results.
1105 {
1106 ScopedFPDFTextPage text_page(FPDFText_LoadPage(page.get()));
1107 ASSERT_TRUE(text_page);
1108 EXPECT_EQ(15, FPDFText_CountChars(text_page.get()));
1109 EXPECT_EQ(0, FPDFText_GetFontWeight(text_page.get(), 0));
1110 }
1111
1112 // Verify the rendering again after calling FPDFPage_GenerateContent().
1113 ASSERT_TRUE(FPDFPage_GenerateContent(page.get()));
1114 {
1115 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
1116 CompareBitmap(page_bitmap.get(), 200, 200, FirstRemovedChecksum());
1117 }
1118
1119 // Save the document and verify it after reloading.
1120 ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1121 VerifySavedDocument(200, 200, FirstRemovedChecksum());
1122
1123 // Verify removed/renamed resources are no longer there.
1124 EXPECT_THAT(GetString(), Not(HasSubstr("/F1")));
1125 EXPECT_THAT(GetString(), Not(HasSubstr("/F2")));
1126 EXPECT_THAT(GetString(), Not(HasSubstr("/Times-Roman")));
1127 }
1128
TEST_F(FPDFEditEmbedderTest,RemoveTextObjectWithTwoPagesSharingContentStreamAndResources)1129 TEST_F(FPDFEditEmbedderTest,
1130 RemoveTextObjectWithTwoPagesSharingContentStreamAndResources) {
1131 // Load document with some text.
1132 ASSERT_TRUE(OpenDocument("hello_world_2_pages.pdf"));
1133 ScopedEmbedderTestPage page1 = LoadScopedPage(0);
1134 ASSERT_TRUE(page1);
1135 ScopedEmbedderTestPage page2 = LoadScopedPage(1);
1136 ASSERT_TRUE(page2);
1137
1138 // Show what the original file looks like.
1139 {
1140 ScopedFPDFBitmap page1_bitmap = RenderPage(page1.get());
1141 CompareBitmap(page1_bitmap.get(), 200, 200, HelloWorldChecksum());
1142 ScopedFPDFBitmap page2_bitmap = RenderPage(page2.get());
1143 CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum());
1144 }
1145
1146 // Get the "Hello, world!" text object from page 1 and remove it.
1147 ASSERT_EQ(2, FPDFPage_CountObjects(page1.get()));
1148 {
1149 ScopedFPDFPageObject page_object(FPDFPage_GetObject(page1.get(), 0));
1150 ASSERT_TRUE(page_object);
1151 ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(page_object.get()));
1152 EXPECT_TRUE(FPDFPage_RemoveObject(page1.get(), page_object.get()));
1153 }
1154 ASSERT_EQ(1, FPDFPage_CountObjects(page1.get()));
1155
1156 // Verify the "Hello, world!" text is gone from page 1.
1157 {
1158 ScopedFPDFBitmap page1_bitmap = RenderPage(page1.get());
1159 CompareBitmap(page1_bitmap.get(), 200, 200, FirstRemovedChecksum());
1160 ScopedFPDFBitmap page2_bitmap = RenderPage(page2.get());
1161 CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum());
1162 }
1163
1164 // Verify the rendering again after calling FPDFPage_GenerateContent().
1165 ASSERT_TRUE(FPDFPage_GenerateContent(page1.get()));
1166 {
1167 ScopedFPDFBitmap page1_bitmap = RenderPage(page1.get());
1168 CompareBitmap(page1_bitmap.get(), 200, 200, FirstRemovedChecksum());
1169 ScopedFPDFBitmap page2_bitmap = RenderPage(page2.get());
1170 CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum());
1171 }
1172
1173 // Save the document and verify it after reloading.
1174 ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1175 ASSERT_TRUE(OpenSavedDocument());
1176 FPDF_PAGE saved_page1 = LoadSavedPage(0);
1177 VerifySavedRendering(saved_page1, 200, 200, FirstRemovedChecksum());
1178 CloseSavedPage(saved_page1);
1179 FPDF_PAGE saved_page2 = LoadSavedPage(1);
1180 VerifySavedRendering(saved_page2, 200, 200, HelloWorldChecksum());
1181 CloseSavedPage(saved_page2);
1182 CloseSavedDocument();
1183
1184 std::vector<std::string> split_saved_data = StringSplit(GetString(), '\n');
1185 // Verify removed/renamed resources are in the save PDF the correct number of
1186 // times.
1187 EXPECT_THAT(split_saved_data, Contains(HasSubstr("/F1")).Times(1));
1188 EXPECT_THAT(split_saved_data, Contains(HasSubstr("/F2")).Times(1));
1189 EXPECT_THAT(split_saved_data, Contains(HasSubstr("/Times-Roman")).Times(1));
1190 }
1191
TEST_F(FPDFEditEmbedderTest,RemoveTextObjectWithTwoPagesSharingContentArrayAndResources)1192 TEST_F(FPDFEditEmbedderTest,
1193 RemoveTextObjectWithTwoPagesSharingContentArrayAndResources) {
1194 // Load document with some text.
1195 ASSERT_TRUE(OpenDocument("hello_world_2_pages_split_streams.pdf"));
1196 ScopedEmbedderTestPage page1 = LoadScopedPage(0);
1197 ASSERT_TRUE(page1);
1198 ScopedEmbedderTestPage page2 = LoadScopedPage(1);
1199 ASSERT_TRUE(page2);
1200
1201 // Show what the original file looks like.
1202 {
1203 ScopedFPDFBitmap page1_bitmap = RenderPage(page1.get());
1204 CompareBitmap(page1_bitmap.get(), 200, 200, HelloWorldChecksum());
1205 ScopedFPDFBitmap page2_bitmap = RenderPage(page2.get());
1206 CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum());
1207 }
1208
1209 // Get the "Hello, world!" text object from page 1 and remove it.
1210 ASSERT_EQ(2, FPDFPage_CountObjects(page1.get()));
1211 {
1212 ScopedFPDFPageObject page_object(FPDFPage_GetObject(page1.get(), 0));
1213 ASSERT_TRUE(page_object);
1214 ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(page_object.get()));
1215 EXPECT_TRUE(FPDFPage_RemoveObject(page1.get(), page_object.get()));
1216 }
1217 ASSERT_EQ(1, FPDFPage_CountObjects(page1.get()));
1218
1219 // Verify the "Hello, world!" text is gone from page 1.
1220 {
1221 ScopedFPDFBitmap page1_bitmap = RenderPage(page1.get());
1222 CompareBitmap(page1_bitmap.get(), 200, 200, FirstRemovedChecksum());
1223 ScopedFPDFBitmap page2_bitmap = RenderPage(page2.get());
1224 CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum());
1225 }
1226
1227 // Verify the rendering again after calling FPDFPage_GenerateContent().
1228 ASSERT_TRUE(FPDFPage_GenerateContent(page1.get()));
1229 {
1230 ScopedFPDFBitmap page1_bitmap = RenderPage(page1.get());
1231 CompareBitmap(page1_bitmap.get(), 200, 200, FirstRemovedChecksum());
1232 ScopedFPDFBitmap page2_bitmap = RenderPage(page2.get());
1233 CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum());
1234 }
1235
1236 // Save the document and verify it after reloading.
1237 ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1238 ASSERT_TRUE(OpenSavedDocument());
1239 FPDF_PAGE saved_page1 = LoadSavedPage(0);
1240 VerifySavedRendering(saved_page1, 200, 200, FirstRemovedChecksum());
1241 CloseSavedPage(saved_page1);
1242 FPDF_PAGE saved_page2 = LoadSavedPage(1);
1243 VerifySavedRendering(saved_page2, 200, 200, HelloWorldChecksum());
1244 CloseSavedPage(saved_page2);
1245 CloseSavedDocument();
1246 }
1247
TEST_F(FPDFEditEmbedderTest,RemoveTextObjectWithTwoPagesSharingResourcesDict)1248 TEST_F(FPDFEditEmbedderTest, RemoveTextObjectWithTwoPagesSharingResourcesDict) {
1249 // Load document with some text.
1250 ASSERT_TRUE(OpenDocument("hello_world_2_pages_shared_resources_dict.pdf"));
1251 ScopedEmbedderTestPage page1 = LoadScopedPage(0);
1252 ASSERT_TRUE(page1);
1253 ScopedEmbedderTestPage page2 = LoadScopedPage(1);
1254 ASSERT_TRUE(page2);
1255
1256 // Show what the original file looks like.
1257 {
1258 ScopedFPDFBitmap page1_bitmap = RenderPage(page1.get());
1259 CompareBitmap(page1_bitmap.get(), 200, 200, HelloWorldChecksum());
1260 ScopedFPDFBitmap page2_bitmap = RenderPage(page2.get());
1261 CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum());
1262 }
1263
1264 // Get the "Hello, world!" text object from page 1 and remove it.
1265 ASSERT_EQ(2, FPDFPage_CountObjects(page1.get()));
1266 {
1267 ScopedFPDFPageObject page_object(FPDFPage_GetObject(page1.get(), 0));
1268 ASSERT_TRUE(page_object);
1269 ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(page_object.get()));
1270 EXPECT_TRUE(FPDFPage_RemoveObject(page1.get(), page_object.get()));
1271 }
1272 ASSERT_EQ(1, FPDFPage_CountObjects(page1.get()));
1273
1274 // Verify the "Hello, world!" text is gone from page 1
1275 {
1276 ScopedFPDFBitmap page1_bitmap = RenderPage(page1.get());
1277 CompareBitmap(page1_bitmap.get(), 200, 200, FirstRemovedChecksum());
1278 ScopedFPDFBitmap page2_bitmap = RenderPage(page2.get());
1279 CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum());
1280 }
1281
1282 // Verify the rendering again after calling FPDFPage_GenerateContent().
1283 ASSERT_TRUE(FPDFPage_GenerateContent(page1.get()));
1284 {
1285 ScopedFPDFBitmap page1_bitmap = RenderPage(page1.get());
1286 CompareBitmap(page1_bitmap.get(), 200, 200, FirstRemovedChecksum());
1287 ScopedFPDFBitmap page2_bitmap = RenderPage(page2.get());
1288 CompareBitmap(page2_bitmap.get(), 200, 200, HelloWorldChecksum());
1289 }
1290
1291 // Save the document and verify it after reloading.
1292 ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1293 ASSERT_TRUE(OpenSavedDocument());
1294 FPDF_PAGE saved_page1 = LoadSavedPage(0);
1295 VerifySavedRendering(saved_page1, 200, 200, FirstRemovedChecksum());
1296 CloseSavedPage(saved_page1);
1297 FPDF_PAGE saved_page2 = LoadSavedPage(1);
1298 VerifySavedRendering(saved_page2, 200, 200, HelloWorldChecksum());
1299 CloseSavedPage(saved_page2);
1300 CloseSavedDocument();
1301 }
1302
CheckMarkCounts(FPDF_PAGE page,int start_from,int expected_object_count,size_t expected_prime_count,size_t expected_square_count,size_t expected_greater_than_ten_count,size_t expected_bounds_count)1303 void CheckMarkCounts(FPDF_PAGE page,
1304 int start_from,
1305 int expected_object_count,
1306 size_t expected_prime_count,
1307 size_t expected_square_count,
1308 size_t expected_greater_than_ten_count,
1309 size_t expected_bounds_count) {
1310 int object_count = FPDFPage_CountObjects(page);
1311 ASSERT_EQ(expected_object_count, object_count);
1312
1313 size_t prime_count = 0;
1314 size_t square_count = 0;
1315 size_t greater_than_ten_count = 0;
1316 size_t bounds_count = 0;
1317 for (int i = 0; i < object_count; ++i) {
1318 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page, i);
1319
1320 int mark_count = FPDFPageObj_CountMarks(page_object);
1321 for (int j = 0; j < mark_count; ++j) {
1322 FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, j);
1323
1324 FPDF_WCHAR buffer[128];
1325 unsigned long name_len = 999u;
1326 ASSERT_TRUE(
1327 FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
1328 EXPECT_GT(name_len, 0u);
1329 EXPECT_NE(999u, name_len);
1330 std::wstring name = GetPlatformWString(buffer);
1331 if (name == L"Prime") {
1332 prime_count++;
1333 } else if (name == L"Square") {
1334 square_count++;
1335 int expected_square = start_from + i;
1336 EXPECT_EQ(1, FPDFPageObjMark_CountParams(mark));
1337
1338 unsigned long get_param_key_return = 999u;
1339 ASSERT_TRUE(FPDFPageObjMark_GetParamKey(mark, 0, buffer, sizeof(buffer),
1340 &get_param_key_return));
1341 EXPECT_EQ((6u + 1u) * 2u, get_param_key_return);
1342 std::wstring key = GetPlatformWString(buffer);
1343 EXPECT_EQ(L"Factor", key);
1344
1345 EXPECT_EQ(FPDF_OBJECT_NUMBER,
1346 FPDFPageObjMark_GetParamValueType(mark, "Factor"));
1347 int square_root;
1348 EXPECT_TRUE(
1349 FPDFPageObjMark_GetParamIntValue(mark, "Factor", &square_root));
1350 EXPECT_EQ(expected_square, square_root * square_root);
1351 } else if (name == L"GreaterThanTen") {
1352 greater_than_ten_count++;
1353 } else if (name == L"Bounds") {
1354 bounds_count++;
1355 EXPECT_EQ(1, FPDFPageObjMark_CountParams(mark));
1356
1357 unsigned long get_param_key_return = 999u;
1358 ASSERT_TRUE(FPDFPageObjMark_GetParamKey(mark, 0, buffer, sizeof(buffer),
1359 &get_param_key_return));
1360 EXPECT_EQ((8u + 1u) * 2u, get_param_key_return);
1361 std::wstring key = GetPlatformWString(buffer);
1362 EXPECT_EQ(L"Position", key);
1363
1364 EXPECT_EQ(FPDF_OBJECT_STRING,
1365 FPDFPageObjMark_GetParamValueType(mark, "Position"));
1366 unsigned long length;
1367 EXPECT_TRUE(FPDFPageObjMark_GetParamStringValue(
1368 mark, "Position", buffer, sizeof(buffer), &length));
1369 ASSERT_GT(length, 0u);
1370 std::wstring value = GetPlatformWString(buffer);
1371
1372 // "Position" can be "First", "Last", or "End".
1373 if (i == 0) {
1374 EXPECT_EQ((5u + 1u) * 2u, length);
1375 EXPECT_EQ(L"First", value);
1376 } else if (i == object_count - 1) {
1377 if (length == (4u + 1u) * 2u) {
1378 EXPECT_EQ(L"Last", value);
1379 } else if (length == (3u + 1u) * 2u) {
1380 EXPECT_EQ(L"End", value);
1381 } else {
1382 FAIL();
1383 }
1384 } else {
1385 FAIL();
1386 }
1387 } else {
1388 FAIL();
1389 }
1390 }
1391 }
1392
1393 // Expect certain number of tagged objects. The test file contains strings
1394 // from 1 to 19.
1395 EXPECT_EQ(expected_prime_count, prime_count);
1396 EXPECT_EQ(expected_square_count, square_count);
1397 EXPECT_EQ(expected_greater_than_ten_count, greater_than_ten_count);
1398 EXPECT_EQ(expected_bounds_count, bounds_count);
1399 }
1400
TEST_F(FPDFEditEmbedderTest,GetMarkedContentId)1401 TEST_F(FPDFEditEmbedderTest, GetMarkedContentId) {
1402 ASSERT_TRUE(OpenDocument("tagged_marked_content.pdf"));
1403 ScopedEmbedderTestPage page = LoadScopedPage(0);
1404 ASSERT_TRUE(page);
1405
1406 constexpr int kExpectedObjectCount = 4;
1407 ASSERT_EQ(kExpectedObjectCount, FPDFPage_CountObjects(page.get()));
1408 for (int i = 0; i < kExpectedObjectCount; ++i) {
1409 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), i);
1410 ASSERT_TRUE(page_object);
1411 ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(page_object));
1412 EXPECT_EQ(i, FPDFPageObj_GetMarkedContentID(page_object));
1413 }
1414
1415 // Negative testing.
1416 EXPECT_EQ(-1, FPDFPageObj_GetMarkedContentID(nullptr));
1417 }
1418
TEST_F(FPDFEditEmbedderTest,ReadMarkedObjectsIndirectDict)1419 TEST_F(FPDFEditEmbedderTest, ReadMarkedObjectsIndirectDict) {
1420 // Load document with some text marked with an indirect property.
1421 ASSERT_TRUE(OpenDocument("text_in_page_marked_indirect.pdf"));
1422 ScopedEmbedderTestPage page = LoadScopedPage(0);
1423 ASSERT_TRUE(page);
1424
1425 CheckMarkCounts(page.get(), 1, 19, 8, 4, 9, 1);
1426 }
1427
TEST_F(FPDFEditEmbedderTest,RemoveMarkedObjectsPrime)1428 TEST_F(FPDFEditEmbedderTest, RemoveMarkedObjectsPrime) {
1429 // Load document with some text.
1430 ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
1431 ScopedEmbedderTestPage page = LoadScopedPage(0);
1432 ASSERT_TRUE(page);
1433
1434 // Show what the original file looks like.
1435 {
1436 const char* original_checksum = []() {
1437 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
1438 #if BUILDFLAG(IS_WIN)
1439 return "cefa45d13f92fb761251661a2c889157";
1440 #elif BUILDFLAG(IS_APPLE)
1441 return "b2044dc5b49fdca723d74bd6277df689";
1442 #else
1443 return "efc2206b313fff03be8e701907322b06";
1444 #endif
1445 }
1446 #if BUILDFLAG(IS_APPLE)
1447 #ifdef ARCH_CPU_ARM64
1448 return "401858d37db450bfd3f9458ac490eb08";
1449 #else
1450 return "6275396f29951f92f8f5e145f0eff03a";
1451 #endif // ARCH_CPU_ARM64
1452 #else
1453 return "3d5a3de53d5866044c2b6bf339742c97";
1454 #endif // BUILDFLAG(IS_APPLE)
1455 }();
1456 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
1457 CompareBitmap(page_bitmap.get(), 200, 200, original_checksum);
1458 }
1459
1460 constexpr int expected_object_count = 19;
1461 CheckMarkCounts(page.get(), 1, expected_object_count, 8, 4, 9, 1);
1462
1463 // Get all objects marked with "Prime"
1464 std::vector<FPDF_PAGEOBJECT> primes;
1465 for (int i = 0; i < expected_object_count; ++i) {
1466 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), i);
1467
1468 int mark_count = FPDFPageObj_CountMarks(page_object);
1469 for (int j = 0; j < mark_count; ++j) {
1470 FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, j);
1471
1472 FPDF_WCHAR buffer[128];
1473 unsigned long name_len = 999u;
1474 ASSERT_TRUE(
1475 FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
1476 EXPECT_GT(name_len, 0u);
1477 EXPECT_NE(999u, name_len);
1478 std::wstring name = GetPlatformWString(buffer);
1479 if (name == L"Prime") {
1480 primes.push_back(page_object);
1481 }
1482 }
1483 }
1484
1485 // Remove all objects marked with "Prime".
1486 for (FPDF_PAGEOBJECT page_object : primes) {
1487 EXPECT_TRUE(FPDFPage_RemoveObject(page.get(), page_object));
1488 FPDFPageObj_Destroy(page_object);
1489 }
1490
1491 EXPECT_EQ(11, FPDFPage_CountObjects(page.get()));
1492 const char* non_primes_checksum = []() {
1493 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
1494 #if BUILDFLAG(IS_WIN)
1495 return "690c7d4c7850fbe726c2299208425f4f";
1496 #elif BUILDFLAG(IS_APPLE)
1497 return "427228e73125ede1050a641cd9b9c8ec";
1498 #else
1499 return "10a6558c9e40ea837922e6f2882a2d57";
1500 #endif
1501 }
1502 #if BUILDFLAG(IS_APPLE)
1503 #ifdef ARCH_CPU_ARM64
1504 return "6a1e31ffe451997946e449250b97d5b2";
1505 #else
1506 return "631be723d5ff1f36e75c971cc940351b";
1507 #endif // ARCH_CPU_ARM64
1508 #else
1509 return "bc8623c052f12376c3d8dd09a6cd27df";
1510 #endif // BUILDFLAG(IS_APPLE)
1511 }();
1512 const char* non_primes_after_save_checksum = []() {
1513 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
1514 #if BUILDFLAG(IS_WIN)
1515 return "690c7d4c7850fbe726c2299208425f4f";
1516 #elif BUILDFLAG(IS_APPLE)
1517 return "427228e73125ede1050a641cd9b9c8ec";
1518 #else
1519 return "10a6558c9e40ea837922e6f2882a2d57";
1520 #endif
1521 }
1522 #if BUILDFLAG(IS_APPLE)
1523 #ifdef ARCH_CPU_ARM64
1524 return "d250bee3658c74e5d74729a09cbd80cd";
1525 #else
1526 return "631be723d5ff1f36e75c971cc940351b";
1527 #endif // ARCH_CPU_ARM64
1528 #else
1529 return "bc8623c052f12376c3d8dd09a6cd27df";
1530 #endif // BUILDFLAG(IS_APPLE)
1531 }();
1532 {
1533 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
1534 CompareBitmap(page_bitmap.get(), 200, 200, non_primes_checksum);
1535 }
1536
1537 // Save the file.
1538 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
1539 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1540
1541 // Re-open the file and check the prime marks are not there anymore.
1542 ASSERT_TRUE(OpenSavedDocument());
1543 FPDF_PAGE saved_page = LoadSavedPage(0);
1544 ASSERT_TRUE(saved_page);
1545 EXPECT_EQ(11, FPDFPage_CountObjects(saved_page));
1546
1547 {
1548 ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
1549 CompareBitmap(page_bitmap.get(), 200, 200, non_primes_after_save_checksum);
1550 }
1551
1552 CloseSavedPage(saved_page);
1553 CloseSavedDocument();
1554 }
1555
TEST_F(FPDFEditEmbedderTest,RemoveMarks)1556 TEST_F(FPDFEditEmbedderTest, RemoveMarks) {
1557 // Load document with some text.
1558 ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
1559 ScopedEmbedderTestPage page = LoadScopedPage(0);
1560 ASSERT_TRUE(page);
1561
1562 constexpr int kExpectedObjectCount = 19;
1563 CheckMarkCounts(page.get(), 1, kExpectedObjectCount, 8, 4, 9, 1);
1564
1565 // Remove all "Prime" content marks.
1566 for (int i = 0; i < kExpectedObjectCount; ++i) {
1567 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), i);
1568
1569 int mark_count = FPDFPageObj_CountMarks(page_object);
1570 for (int j = mark_count - 1; j >= 0; --j) {
1571 FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, j);
1572
1573 FPDF_WCHAR buffer[128];
1574 unsigned long name_len = 999u;
1575 ASSERT_TRUE(
1576 FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
1577 EXPECT_GT(name_len, 0u);
1578 EXPECT_NE(999u, name_len);
1579 std::wstring name = GetPlatformWString(buffer);
1580 if (name == L"Prime") {
1581 // Remove mark.
1582 EXPECT_TRUE(FPDFPageObj_RemoveMark(page_object, mark));
1583
1584 // Verify there is now one fewer mark in the page object.
1585 EXPECT_EQ(mark_count - 1, FPDFPageObj_CountMarks(page_object));
1586 }
1587 }
1588 }
1589
1590 // Verify there are 0 "Prime" content marks now.
1591 CheckMarkCounts(page.get(), 1, kExpectedObjectCount, 0, 4, 9, 1);
1592
1593 // Save the file.
1594 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
1595 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1596
1597 // Re-open the file and check the prime marks are not there anymore.
1598 ASSERT_TRUE(OpenSavedDocument());
1599 FPDF_PAGE saved_page = LoadSavedPage(0);
1600 ASSERT_TRUE(saved_page);
1601
1602 CheckMarkCounts(saved_page, 1, kExpectedObjectCount, 0, 4, 9, 1);
1603
1604 CloseSavedPage(saved_page);
1605 CloseSavedDocument();
1606 }
1607
TEST_F(FPDFEditEmbedderTest,RemoveMarkParam)1608 TEST_F(FPDFEditEmbedderTest, RemoveMarkParam) {
1609 // Load document with some text.
1610 ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
1611 ScopedEmbedderTestPage page = LoadScopedPage(0);
1612 ASSERT_TRUE(page);
1613
1614 constexpr int kExpectedObjectCount = 19;
1615 CheckMarkCounts(page.get(), 1, kExpectedObjectCount, 8, 4, 9, 1);
1616
1617 // Remove all "Square" content marks parameters.
1618 for (int i = 0; i < kExpectedObjectCount; ++i) {
1619 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), i);
1620
1621 int mark_count = FPDFPageObj_CountMarks(page_object);
1622 for (int j = 0; j < mark_count; ++j) {
1623 FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, j);
1624
1625 FPDF_WCHAR buffer[128];
1626 unsigned long name_len = 999u;
1627 ASSERT_TRUE(
1628 FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
1629 EXPECT_GT(name_len, 0u);
1630 EXPECT_NE(999u, name_len);
1631 std::wstring name = GetPlatformWString(buffer);
1632 if (name == L"Square") {
1633 // Show the mark has a "Factor" parameter.
1634 int out_value;
1635 EXPECT_TRUE(
1636 FPDFPageObjMark_GetParamIntValue(mark, "Factor", &out_value));
1637
1638 // Remove parameter.
1639 EXPECT_TRUE(FPDFPageObjMark_RemoveParam(page_object, mark, "Factor"));
1640
1641 // Verify the "Factor" parameter is gone.
1642 EXPECT_FALSE(
1643 FPDFPageObjMark_GetParamIntValue(mark, "Factor", &out_value));
1644 }
1645 }
1646 }
1647
1648 // Save the file.
1649 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
1650 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1651
1652 // Re-open the file and check the "Factor" parameters are still gone.
1653 ASSERT_TRUE(OpenSavedDocument());
1654 FPDF_PAGE saved_page = LoadSavedPage(0);
1655 ASSERT_TRUE(saved_page);
1656
1657 size_t square_count = 0;
1658 for (int i = 0; i < kExpectedObjectCount; ++i) {
1659 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(saved_page, i);
1660
1661 int mark_count = FPDFPageObj_CountMarks(page_object);
1662 for (int j = 0; j < mark_count; ++j) {
1663 FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, j);
1664
1665 FPDF_WCHAR buffer[128];
1666 unsigned long name_len = 999u;
1667 ASSERT_TRUE(
1668 FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
1669 EXPECT_GT(name_len, 0u);
1670 EXPECT_NE(999u, name_len);
1671 std::wstring name = GetPlatformWString(buffer);
1672 if (name == L"Square") {
1673 // Verify the "Factor" parameter is still gone.
1674 int out_value;
1675 EXPECT_FALSE(
1676 FPDFPageObjMark_GetParamIntValue(mark, "Factor", &out_value));
1677
1678 ++square_count;
1679 }
1680 }
1681 }
1682
1683 // Verify the parameters are gone, but the marks are not.
1684 EXPECT_EQ(4u, square_count);
1685
1686 CloseSavedPage(saved_page);
1687 CloseSavedDocument();
1688 }
1689
TEST_F(FPDFEditEmbedderTest,MaintainMarkedObjects)1690 TEST_F(FPDFEditEmbedderTest, MaintainMarkedObjects) {
1691 // Load document with some text.
1692 ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
1693 ScopedEmbedderTestPage page = LoadScopedPage(0);
1694 ASSERT_TRUE(page);
1695
1696 // Iterate over all objects, counting the number of times each content mark
1697 // name appears.
1698 CheckMarkCounts(page.get(), 1, 19, 8, 4, 9, 1);
1699
1700 // Remove first page object.
1701 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 0);
1702 EXPECT_TRUE(FPDFPage_RemoveObject(page.get(), page_object));
1703 FPDFPageObj_Destroy(page_object);
1704
1705 CheckMarkCounts(page.get(), 2, 18, 8, 3, 9, 1);
1706
1707 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
1708 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1709
1710
1711 ASSERT_TRUE(OpenSavedDocument());
1712 FPDF_PAGE saved_page = LoadSavedPage(0);
1713 ASSERT_TRUE(saved_page);
1714
1715 CheckMarkCounts(saved_page, 2, 18, 8, 3, 9, 1);
1716
1717 CloseSavedPage(saved_page);
1718 CloseSavedDocument();
1719 }
1720
TEST_F(FPDFEditEmbedderTest,MaintainIndirectMarkedObjects)1721 TEST_F(FPDFEditEmbedderTest, MaintainIndirectMarkedObjects) {
1722 // Load document with some text.
1723 ASSERT_TRUE(OpenDocument("text_in_page_marked_indirect.pdf"));
1724 ScopedEmbedderTestPage page = LoadScopedPage(0);
1725 ASSERT_TRUE(page);
1726
1727 // Iterate over all objects, counting the number of times each content mark
1728 // name appears.
1729 CheckMarkCounts(page.get(), 1, 19, 8, 4, 9, 1);
1730
1731 // Remove first page object.
1732 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 0);
1733 EXPECT_TRUE(FPDFPage_RemoveObject(page.get(), page_object));
1734 FPDFPageObj_Destroy(page_object);
1735
1736 CheckMarkCounts(page.get(), 2, 18, 8, 3, 9, 1);
1737
1738 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
1739 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1740
1741
1742 ASSERT_TRUE(OpenSavedDocument());
1743 FPDF_PAGE saved_page = LoadSavedPage(0);
1744 ASSERT_TRUE(saved_page);
1745
1746 CheckMarkCounts(saved_page, 2, 18, 8, 3, 9, 1);
1747
1748 CloseSavedPage(saved_page);
1749 CloseSavedDocument();
1750 }
1751
TEST_F(FPDFEditEmbedderTest,RemoveExistingPageObject)1752 TEST_F(FPDFEditEmbedderTest, RemoveExistingPageObject) {
1753 // Load document with some text.
1754 ASSERT_TRUE(OpenDocument("hello_world.pdf"));
1755 ScopedEmbedderTestPage page = LoadScopedPage(0);
1756 ASSERT_TRUE(page);
1757
1758 // Get the "Hello, world!" text object and remove it.
1759 ASSERT_EQ(2, FPDFPage_CountObjects(page.get()));
1760 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 0);
1761 ASSERT_TRUE(page_object);
1762 EXPECT_TRUE(FPDFPage_RemoveObject(page.get(), page_object));
1763
1764 // Verify the "Hello, world!" text is gone.
1765 ASSERT_EQ(1, FPDFPage_CountObjects(page.get()));
1766
1767 // Save the file
1768 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
1769 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1770 FPDFPageObj_Destroy(page_object);
1771
1772 // Re-open the file and check the page object count is still 1.
1773 ASSERT_TRUE(OpenSavedDocument());
1774 FPDF_PAGE saved_page = LoadSavedPage(0);
1775 ASSERT_TRUE(saved_page);
1776 EXPECT_EQ(1, FPDFPage_CountObjects(saved_page));
1777 CloseSavedPage(saved_page);
1778 CloseSavedDocument();
1779 }
1780
TEST_F(FPDFEditEmbedderTest,RemoveExistingPageObjectSplitStreamsNotLonely)1781 TEST_F(FPDFEditEmbedderTest, RemoveExistingPageObjectSplitStreamsNotLonely) {
1782 // Load document with some text.
1783 ASSERT_TRUE(OpenDocument("hello_world_split_streams.pdf"));
1784 ScopedEmbedderTestPage page = LoadScopedPage(0);
1785 ASSERT_TRUE(page);
1786
1787 // Get the "Hello, world!" text object and remove it. There is another object
1788 // in the same stream that says "Goodbye, world!"
1789 ASSERT_EQ(3, FPDFPage_CountObjects(page.get()));
1790 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 0);
1791 ASSERT_TRUE(page_object);
1792 EXPECT_TRUE(FPDFPage_RemoveObject(page.get(), page_object));
1793
1794 // Verify the "Hello, world!" text is gone.
1795 ASSERT_EQ(2, FPDFPage_CountObjects(page.get()));
1796 const char* hello_removed_checksum = []() {
1797 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
1798 #if BUILDFLAG(IS_WIN)
1799 return "48b5524e20e942d2a8f7e15611968cc7";
1800 #elif BUILDFLAG(IS_APPLE)
1801 return "5b9d1dee233eb9d51e23a36c6c631443";
1802 #else
1803 return "204c11472f5b93719487de7b9c1b1c93";
1804 #endif
1805 }
1806 #if BUILDFLAG(IS_APPLE)
1807 return "5508c2f06d104050f74f655693e38c2c";
1808 #else
1809 return "a8cd82499cf744e0862ca468c9d4ceb8";
1810 #endif
1811 }();
1812 {
1813 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
1814 CompareBitmap(page_bitmap.get(), 200, 200, hello_removed_checksum);
1815 }
1816
1817 // Save the file
1818 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
1819 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1820 FPDFPageObj_Destroy(page_object);
1821
1822 // Re-open the file and check the page object count is still 2.
1823 ASSERT_TRUE(OpenSavedDocument());
1824 FPDF_PAGE saved_page = LoadSavedPage(0);
1825 ASSERT_TRUE(saved_page);
1826
1827 EXPECT_EQ(2, FPDFPage_CountObjects(saved_page));
1828 {
1829 ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
1830 CompareBitmap(page_bitmap.get(), 200, 200, hello_removed_checksum);
1831 }
1832
1833 CloseSavedPage(saved_page);
1834 CloseSavedDocument();
1835 }
1836
TEST_F(FPDFEditEmbedderTest,RemoveExistingPageObjectSplitStreamsLonely)1837 TEST_F(FPDFEditEmbedderTest, RemoveExistingPageObjectSplitStreamsLonely) {
1838 // Load document with some text.
1839 ASSERT_TRUE(OpenDocument("hello_world_split_streams.pdf"));
1840 ScopedEmbedderTestPage page = LoadScopedPage(0);
1841 ASSERT_TRUE(page);
1842
1843 // Get the "Greetings, world!" text object and remove it. This is the only
1844 // object in the stream.
1845 ASSERT_EQ(3, FPDFPage_CountObjects(page.get()));
1846 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 2);
1847 ASSERT_TRUE(page_object);
1848 EXPECT_TRUE(FPDFPage_RemoveObject(page.get(), page_object));
1849
1850 // Verify the "Greetings, world!" text is gone.
1851 ASSERT_EQ(2, FPDFPage_CountObjects(page.get()));
1852 {
1853 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
1854 CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldChecksum());
1855 }
1856
1857 // Save the file
1858 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
1859 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1860 FPDFPageObj_Destroy(page_object);
1861
1862 // Re-open the file and check the page object count is still 2.
1863 ASSERT_TRUE(OpenSavedDocument());
1864 FPDF_PAGE saved_page = LoadSavedPage(0);
1865 ASSERT_TRUE(saved_page);
1866
1867 EXPECT_EQ(2, FPDFPage_CountObjects(saved_page));
1868 {
1869 ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
1870 CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldChecksum());
1871 }
1872
1873 CloseSavedPage(saved_page);
1874 CloseSavedDocument();
1875 }
1876
TEST_F(FPDFEditEmbedderTest,GetContentStream)1877 TEST_F(FPDFEditEmbedderTest, GetContentStream) {
1878 // Load document with some text split across streams.
1879 ASSERT_TRUE(OpenDocument("split_streams.pdf"));
1880 ScopedEmbedderTestPage page = LoadScopedPage(0);
1881 ASSERT_TRUE(page);
1882
1883 // Content stream 0: page objects 0-14.
1884 // Content stream 1: page objects 15-17.
1885 // Content stream 2: page object 18.
1886 ASSERT_EQ(19, FPDFPage_CountObjects(page.get()));
1887 for (int i = 0; i < 19; i++) {
1888 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), i);
1889 ASSERT_TRUE(page_object);
1890 CPDF_PageObject* cpdf_page_object =
1891 CPDFPageObjectFromFPDFPageObject(page_object);
1892 if (i < 15)
1893 EXPECT_EQ(0, cpdf_page_object->GetContentStream()) << i;
1894 else if (i < 18)
1895 EXPECT_EQ(1, cpdf_page_object->GetContentStream()) << i;
1896 else
1897 EXPECT_EQ(2, cpdf_page_object->GetContentStream()) << i;
1898 }
1899 }
1900
TEST_F(FPDFEditEmbedderTest,RemoveAllFromStream)1901 TEST_F(FPDFEditEmbedderTest, RemoveAllFromStream) {
1902 // Load document with some text split across streams.
1903 ASSERT_TRUE(OpenDocument("split_streams.pdf"));
1904 ScopedEmbedderTestPage page = LoadScopedPage(0);
1905 ASSERT_TRUE(page);
1906
1907 // Content stream 0: page objects 0-14.
1908 // Content stream 1: page objects 15-17.
1909 // Content stream 2: page object 18.
1910 ASSERT_EQ(19, FPDFPage_CountObjects(page.get()));
1911
1912 // Loop backwards because objects will being removed, which shifts the indexes
1913 // after the removed position.
1914 for (int i = 18; i >= 0; i--) {
1915 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), i);
1916 ASSERT_TRUE(page_object);
1917 CPDF_PageObject* cpdf_page_object =
1918 CPDFPageObjectFromFPDFPageObject(page_object);
1919
1920 // Empty content stream 1.
1921 if (cpdf_page_object->GetContentStream() == 1) {
1922 EXPECT_TRUE(FPDFPage_RemoveObject(page.get(), page_object));
1923 FPDFPageObj_Destroy(page_object);
1924 }
1925 }
1926
1927 // Content stream 0: page objects 0-14.
1928 // Content stream 2: page object 15.
1929 ASSERT_EQ(16, FPDFPage_CountObjects(page.get()));
1930 for (int i = 0; i < 16; i++) {
1931 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), i);
1932 ASSERT_TRUE(page_object);
1933 CPDF_PageObject* cpdf_page_object =
1934 CPDFPageObjectFromFPDFPageObject(page_object);
1935 if (i < 15)
1936 EXPECT_EQ(0, cpdf_page_object->GetContentStream()) << i;
1937 else
1938 EXPECT_EQ(2, cpdf_page_object->GetContentStream()) << i;
1939 }
1940
1941 // Generate contents should remove the empty stream and update the page
1942 // objects' contents stream indexes.
1943 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
1944
1945 // Content stream 0: page objects 0-14.
1946 // Content stream 1: page object 15.
1947 ASSERT_EQ(16, FPDFPage_CountObjects(page.get()));
1948 for (int i = 0; i < 16; i++) {
1949 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), i);
1950 ASSERT_TRUE(page_object);
1951 CPDF_PageObject* cpdf_page_object =
1952 CPDFPageObjectFromFPDFPageObject(page_object);
1953 if (i < 15)
1954 EXPECT_EQ(0, cpdf_page_object->GetContentStream()) << i;
1955 else
1956 EXPECT_EQ(1, cpdf_page_object->GetContentStream()) << i;
1957 }
1958
1959 const char* stream1_removed_checksum = []() {
1960 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
1961 #if BUILDFLAG(IS_WIN)
1962 return "d7e6debf2dc02de449860ee8012a18d2";
1963 #elif BUILDFLAG(IS_APPLE)
1964 return "b26ac6d9bef9c756a19a9aafc60709bd";
1965 #else
1966 return "0b3ef335b8d86a3f9d609368b9d075e0";
1967 #endif
1968 }
1969 #if BUILDFLAG(IS_APPLE)
1970 #if ARCH_CPU_ARM64
1971 return "a47297bbcfa01e27891eeb52375b6f9e";
1972 #else
1973 return "1c1d478b59e3e63813f0f56124564f48";
1974 #endif // ARCH_CPU_ARM64
1975 #else
1976 return "b474826df1acedb05c7b82e1e49e64a6";
1977 #endif // BUILDFLAG(IS_APPLE)
1978 }();
1979 {
1980 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
1981 CompareBitmap(page_bitmap.get(), 200, 200, stream1_removed_checksum);
1982 }
1983
1984 // Save the file
1985 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
1986
1987 // Re-open the file and check the page object count is still 16, and that
1988 // content stream 1 was removed.
1989 ASSERT_TRUE(OpenSavedDocument());
1990 FPDF_PAGE saved_page = LoadSavedPage(0);
1991 ASSERT_TRUE(saved_page);
1992
1993 // Content stream 0: page objects 0-14.
1994 // Content stream 1: page object 15.
1995 EXPECT_EQ(16, FPDFPage_CountObjects(saved_page));
1996 for (int i = 0; i < 16; i++) {
1997 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(saved_page, i);
1998 ASSERT_TRUE(page_object);
1999 CPDF_PageObject* cpdf_page_object =
2000 CPDFPageObjectFromFPDFPageObject(page_object);
2001 if (i < 15)
2002 EXPECT_EQ(0, cpdf_page_object->GetContentStream()) << i;
2003 else
2004 EXPECT_EQ(1, cpdf_page_object->GetContentStream()) << i;
2005 }
2006
2007 {
2008 ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
2009 CompareBitmap(page_bitmap.get(), 200, 200, stream1_removed_checksum);
2010 }
2011
2012 CloseSavedPage(saved_page);
2013 CloseSavedDocument();
2014 }
2015
TEST_F(FPDFEditEmbedderTest,RemoveAllFromSingleStream)2016 TEST_F(FPDFEditEmbedderTest, RemoveAllFromSingleStream) {
2017 // Load document with a single stream.
2018 ASSERT_TRUE(OpenDocument("hello_world.pdf"));
2019 ScopedEmbedderTestPage page = LoadScopedPage(0);
2020 ASSERT_TRUE(page);
2021
2022 // Content stream 0: page objects 0-1.
2023 ASSERT_EQ(2, FPDFPage_CountObjects(page.get()));
2024
2025 // Loop backwards because objects will being removed, which shifts the indexes
2026 // after the removed position.
2027 for (int i = 1; i >= 0; i--) {
2028 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), i);
2029 ASSERT_TRUE(page_object);
2030 CPDF_PageObject* cpdf_page_object =
2031 CPDFPageObjectFromFPDFPageObject(page_object);
2032 ASSERT_EQ(0, cpdf_page_object->GetContentStream());
2033 ASSERT_TRUE(FPDFPage_RemoveObject(page.get(), page_object));
2034 FPDFPageObj_Destroy(page_object);
2035 }
2036
2037 // No more objects in the stream
2038 ASSERT_EQ(0, FPDFPage_CountObjects(page.get()));
2039
2040 // Generate contents should remove the empty stream and update the page
2041 // objects' contents stream indexes.
2042 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2043
2044 ASSERT_EQ(0, FPDFPage_CountObjects(page.get()));
2045
2046 {
2047 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
2048 CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedChecksum);
2049 }
2050
2051 // Save the file
2052 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2053
2054 // Re-open the file and check the page object count is still 0.
2055 ASSERT_TRUE(OpenSavedDocument());
2056 FPDF_PAGE saved_page = LoadSavedPage(0);
2057 ASSERT_TRUE(saved_page);
2058
2059 EXPECT_EQ(0, FPDFPage_CountObjects(saved_page));
2060 {
2061 ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
2062 CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedChecksum);
2063 }
2064
2065 CloseSavedPage(saved_page);
2066 CloseSavedDocument();
2067 }
2068
TEST_F(FPDFEditEmbedderTest,RemoveFirstFromSingleStream)2069 TEST_F(FPDFEditEmbedderTest, RemoveFirstFromSingleStream) {
2070 // Load document with a single stream.
2071 ASSERT_TRUE(OpenDocument("hello_world.pdf"));
2072 ScopedEmbedderTestPage page = LoadScopedPage(0);
2073 ASSERT_TRUE(page);
2074
2075 // Content stream 0: page objects 0-1.
2076 ASSERT_EQ(2, FPDFPage_CountObjects(page.get()));
2077
2078 // Remove first object.
2079 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 0);
2080 ASSERT_TRUE(page_object);
2081 CPDF_PageObject* cpdf_page_object =
2082 CPDFPageObjectFromFPDFPageObject(page_object);
2083 ASSERT_EQ(0, cpdf_page_object->GetContentStream());
2084 ASSERT_TRUE(FPDFPage_RemoveObject(page.get(), page_object));
2085 FPDFPageObj_Destroy(page_object);
2086
2087 // One object left in the stream.
2088 ASSERT_EQ(1, FPDFPage_CountObjects(page.get()));
2089 page_object = FPDFPage_GetObject(page.get(), 0);
2090 ASSERT_TRUE(page_object);
2091 cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
2092 ASSERT_EQ(0, cpdf_page_object->GetContentStream());
2093
2094 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2095
2096 // Still one object left in the stream.
2097 ASSERT_EQ(1, FPDFPage_CountObjects(page.get()));
2098 page_object = FPDFPage_GetObject(page.get(), 0);
2099 ASSERT_TRUE(page_object);
2100 cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
2101 ASSERT_EQ(0, cpdf_page_object->GetContentStream());
2102
2103 {
2104 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
2105 CompareBitmap(page_bitmap.get(), 200, 200, FirstRemovedChecksum());
2106 }
2107
2108 // Save the file
2109 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2110
2111 // Re-open the file and check the page object count is still 0.
2112 ASSERT_TRUE(OpenSavedDocument());
2113 FPDF_PAGE saved_page = LoadSavedPage(0);
2114 ASSERT_TRUE(saved_page);
2115
2116 ASSERT_EQ(1, FPDFPage_CountObjects(saved_page));
2117 page_object = FPDFPage_GetObject(saved_page, 0);
2118 ASSERT_TRUE(page_object);
2119 cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
2120 ASSERT_EQ(0, cpdf_page_object->GetContentStream());
2121 {
2122 ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
2123 CompareBitmap(page_bitmap.get(), 200, 200, FirstRemovedChecksum());
2124 }
2125
2126 CloseSavedPage(saved_page);
2127 CloseSavedDocument();
2128 }
2129
TEST_F(FPDFEditEmbedderTest,RemoveLastFromSingleStream)2130 TEST_F(FPDFEditEmbedderTest, RemoveLastFromSingleStream) {
2131 // Load document with a single stream.
2132 ASSERT_TRUE(OpenDocument("hello_world.pdf"));
2133 ScopedEmbedderTestPage page = LoadScopedPage(0);
2134 ASSERT_TRUE(page);
2135
2136 // Content stream 0: page objects 0-1.
2137 ASSERT_EQ(2, FPDFPage_CountObjects(page.get()));
2138
2139 // Remove last object
2140 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 1);
2141 ASSERT_TRUE(page_object);
2142 CPDF_PageObject* cpdf_page_object =
2143 CPDFPageObjectFromFPDFPageObject(page_object);
2144 ASSERT_EQ(0, cpdf_page_object->GetContentStream());
2145 ASSERT_TRUE(FPDFPage_RemoveObject(page.get(), page_object));
2146 FPDFPageObj_Destroy(page_object);
2147
2148 // One object left in the stream.
2149 ASSERT_EQ(1, FPDFPage_CountObjects(page.get()));
2150 page_object = FPDFPage_GetObject(page.get(), 0);
2151 ASSERT_TRUE(page_object);
2152 cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
2153 ASSERT_EQ(0, cpdf_page_object->GetContentStream());
2154
2155 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2156
2157 // Still one object left in the stream.
2158 ASSERT_EQ(1, FPDFPage_CountObjects(page.get()));
2159 page_object = FPDFPage_GetObject(page.get(), 0);
2160 ASSERT_TRUE(page_object);
2161 cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
2162 ASSERT_EQ(0, cpdf_page_object->GetContentStream());
2163
2164 using pdfium::HelloWorldRemovedChecksum;
2165 {
2166 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
2167 CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldRemovedChecksum());
2168 }
2169
2170 // Save the file
2171 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2172
2173 // Re-open the file and check the page object count is still 0.
2174 ASSERT_TRUE(OpenSavedDocument());
2175 FPDF_PAGE saved_page = LoadSavedPage(0);
2176 ASSERT_TRUE(saved_page);
2177
2178 ASSERT_EQ(1, FPDFPage_CountObjects(saved_page));
2179 page_object = FPDFPage_GetObject(saved_page, 0);
2180 ASSERT_TRUE(page_object);
2181 cpdf_page_object = CPDFPageObjectFromFPDFPageObject(page_object);
2182 ASSERT_EQ(0, cpdf_page_object->GetContentStream());
2183 {
2184 ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
2185 CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldRemovedChecksum());
2186 }
2187
2188 CloseSavedPage(saved_page);
2189 CloseSavedDocument();
2190 }
2191
TEST_F(FPDFEditEmbedderTest,RemoveAllFromMultipleStreams)2192 TEST_F(FPDFEditEmbedderTest, RemoveAllFromMultipleStreams) {
2193 // Load document with some text.
2194 ASSERT_TRUE(OpenDocument("hello_world_split_streams.pdf"));
2195 ScopedEmbedderTestPage page = LoadScopedPage(0);
2196 ASSERT_TRUE(page);
2197
2198 // Content stream 0: page objects 0-1.
2199 // Content stream 1: page object 2.
2200 ASSERT_EQ(3, FPDFPage_CountObjects(page.get()));
2201
2202 // Loop backwards because objects will being removed, which shifts the indexes
2203 // after the removed position.
2204 for (int i = 2; i >= 0; i--) {
2205 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), i);
2206 ASSERT_TRUE(page_object);
2207 ASSERT_TRUE(FPDFPage_RemoveObject(page.get(), page_object));
2208 FPDFPageObj_Destroy(page_object);
2209 }
2210
2211 // No more objects in the page.
2212 ASSERT_EQ(0, FPDFPage_CountObjects(page.get()));
2213
2214 // Generate contents should remove the empty streams and update the page
2215 // objects' contents stream indexes.
2216 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2217
2218 ASSERT_EQ(0, FPDFPage_CountObjects(page.get()));
2219
2220 {
2221 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
2222 CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedChecksum);
2223 }
2224
2225 // Save the file
2226 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2227
2228 // Re-open the file and check the page object count is still 0.
2229 ASSERT_TRUE(OpenSavedDocument());
2230 FPDF_PAGE saved_page = LoadSavedPage(0);
2231 ASSERT_TRUE(saved_page);
2232
2233 EXPECT_EQ(0, FPDFPage_CountObjects(saved_page));
2234 {
2235 ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
2236 CompareBitmap(page_bitmap.get(), 200, 200, kAllRemovedChecksum);
2237 }
2238
2239 CloseSavedPage(saved_page);
2240 CloseSavedDocument();
2241 }
2242
TEST_F(FPDFEditEmbedderTest,InsertPageObjectAndSave)2243 TEST_F(FPDFEditEmbedderTest, InsertPageObjectAndSave) {
2244 // Load document with some text.
2245 ASSERT_TRUE(OpenDocument("hello_world.pdf"));
2246 ScopedEmbedderTestPage page = LoadScopedPage(0);
2247 ASSERT_TRUE(page);
2248
2249 // Add a red rectangle.
2250 ASSERT_EQ(2, FPDFPage_CountObjects(page.get()));
2251 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
2252 EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
2253 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
2254 FPDFPage_InsertObject(page.get(), red_rect);
2255
2256 // Verify the red rectangle was added.
2257 ASSERT_EQ(3, FPDFPage_CountObjects(page.get()));
2258
2259 // Save the file
2260 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2261 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2262
2263 // Re-open the file and check the page object count is still 3.
2264 ASSERT_TRUE(OpenSavedDocument());
2265 FPDF_PAGE saved_page = LoadSavedPage(0);
2266 ASSERT_TRUE(saved_page);
2267 EXPECT_EQ(3, FPDFPage_CountObjects(saved_page));
2268 CloseSavedPage(saved_page);
2269 CloseSavedDocument();
2270 }
2271
TEST_F(FPDFEditEmbedderTest,InsertPageObjectEditAndSave)2272 TEST_F(FPDFEditEmbedderTest, InsertPageObjectEditAndSave) {
2273 // Load document with some text.
2274 ASSERT_TRUE(OpenDocument("hello_world.pdf"));
2275 ScopedEmbedderTestPage page = LoadScopedPage(0);
2276 ASSERT_TRUE(page);
2277
2278 // Add a red rectangle.
2279 ASSERT_EQ(2, FPDFPage_CountObjects(page.get()));
2280 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
2281 EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 100, 100, 255));
2282 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
2283 FPDFPage_InsertObject(page.get(), red_rect);
2284
2285 // Verify the red rectangle was added.
2286 ASSERT_EQ(3, FPDFPage_CountObjects(page.get()));
2287
2288 // Generate content but change it again
2289 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2290 EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
2291
2292 // Save the file
2293 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2294 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2295
2296 // Re-open the file and check the page object count is still 3.
2297 ASSERT_TRUE(OpenSavedDocument());
2298 FPDF_PAGE saved_page = LoadSavedPage(0);
2299 ASSERT_TRUE(saved_page);
2300 EXPECT_EQ(3, FPDFPage_CountObjects(saved_page));
2301 CloseSavedPage(saved_page);
2302 CloseSavedDocument();
2303 }
2304
TEST_F(FPDFEditEmbedderTest,InsertAndRemoveLargeFile)2305 TEST_F(FPDFEditEmbedderTest, InsertAndRemoveLargeFile) {
2306 const int kOriginalObjectCount = 600;
2307
2308 // Load document with many objects.
2309 ASSERT_TRUE(OpenDocument("many_rectangles.pdf"));
2310 ScopedEmbedderTestPage page = LoadScopedPage(0);
2311 ASSERT_TRUE(page);
2312
2313 using pdfium::ManyRectanglesChecksum;
2314 {
2315 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
2316 CompareBitmap(page_bitmap.get(), 200, 300, ManyRectanglesChecksum());
2317 }
2318
2319 // Add a black rectangle.
2320 ASSERT_EQ(kOriginalObjectCount, FPDFPage_CountObjects(page.get()));
2321 FPDF_PAGEOBJECT black_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
2322 EXPECT_TRUE(FPDFPageObj_SetFillColor(black_rect, 0, 0, 0, 255));
2323 EXPECT_TRUE(FPDFPath_SetDrawMode(black_rect, FPDF_FILLMODE_ALTERNATE, 0));
2324 FPDFPage_InsertObject(page.get(), black_rect);
2325
2326 // Verify the black rectangle was added.
2327 ASSERT_EQ(kOriginalObjectCount + 1, FPDFPage_CountObjects(page.get()));
2328 const char* plus_rectangle_checksum = []() {
2329 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
2330 return "0d3715fcfb9bd0dd25dcce60800bff47";
2331 }
2332 return "6b9396ab570754b32b04ca629e902f77";
2333 }();
2334 {
2335 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
2336 CompareBitmap(page_bitmap.get(), 200, 300, plus_rectangle_checksum);
2337 }
2338
2339 // Save the file.
2340 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2341 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2342
2343 // Re-open the file and check the rectangle added is still there.
2344 ASSERT_TRUE(OpenSavedDocument());
2345 FPDF_PAGE saved_page = LoadSavedPage(0);
2346 ASSERT_TRUE(saved_page);
2347 EXPECT_EQ(kOriginalObjectCount + 1, FPDFPage_CountObjects(saved_page));
2348 {
2349 ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
2350 CompareBitmap(page_bitmap.get(), 200, 300, plus_rectangle_checksum);
2351 }
2352
2353 // Remove the added rectangle.
2354 FPDF_PAGEOBJECT added_object =
2355 FPDFPage_GetObject(saved_page, kOriginalObjectCount);
2356 EXPECT_TRUE(FPDFPage_RemoveObject(saved_page, added_object));
2357 FPDFPageObj_Destroy(added_object);
2358 {
2359 ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
2360 CompareBitmap(page_bitmap.get(), 200, 300, ManyRectanglesChecksum());
2361 }
2362 EXPECT_EQ(kOriginalObjectCount, FPDFPage_CountObjects(saved_page));
2363
2364 // Save the file again.
2365 EXPECT_TRUE(FPDFPage_GenerateContent(saved_page));
2366 EXPECT_TRUE(FPDF_SaveAsCopy(saved_document(), this, 0));
2367
2368 CloseSavedPage(saved_page);
2369 CloseSavedDocument();
2370
2371 // Re-open the file (again) and check the black rectangle was removed and the
2372 // rest is intact.
2373 ASSERT_TRUE(OpenSavedDocument());
2374 saved_page = LoadSavedPage(0);
2375 ASSERT_TRUE(saved_page);
2376 EXPECT_EQ(kOriginalObjectCount, FPDFPage_CountObjects(saved_page));
2377 {
2378 ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
2379 CompareBitmap(page_bitmap.get(), 200, 300, ManyRectanglesChecksum());
2380 }
2381
2382 CloseSavedPage(saved_page);
2383 CloseSavedDocument();
2384 }
2385
TEST_F(FPDFEditEmbedderTest,AddAndRemovePaths)2386 TEST_F(FPDFEditEmbedderTest, AddAndRemovePaths) {
2387 // Start with a blank page.
2388 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
2389 ASSERT_TRUE(page);
2390
2391 // Render the blank page and verify it's a blank bitmap.
2392 using pdfium::kBlankPage612By792Checksum;
2393 {
2394 ScopedFPDFBitmap page_bitmap = RenderPage(page);
2395 CompareBitmap(page_bitmap.get(), 612, 792, kBlankPage612By792Checksum);
2396 }
2397 ASSERT_EQ(0, FPDFPage_CountObjects(page));
2398
2399 // Add a red rectangle.
2400 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
2401 ASSERT_TRUE(red_rect);
2402 EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
2403 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
2404 FPDFPage_InsertObject(page, red_rect);
2405 {
2406 ScopedFPDFBitmap page_bitmap = RenderPage(page);
2407 CompareBitmap(page_bitmap.get(), 612, 792, kRedRectangleChecksum);
2408 }
2409 EXPECT_EQ(1, FPDFPage_CountObjects(page));
2410
2411 // Remove rectangle and verify it does not render anymore and the bitmap is
2412 // back to a blank one.
2413 EXPECT_TRUE(FPDFPage_RemoveObject(page, red_rect));
2414 {
2415 ScopedFPDFBitmap page_bitmap = RenderPage(page);
2416 CompareBitmap(page_bitmap.get(), 612, 792, kBlankPage612By792Checksum);
2417 }
2418 EXPECT_EQ(0, FPDFPage_CountObjects(page));
2419
2420 // Trying to remove an object not in the page should return false.
2421 EXPECT_FALSE(FPDFPage_RemoveObject(page, red_rect));
2422
2423 FPDF_ClosePage(page);
2424 FPDFPageObj_Destroy(red_rect);
2425 }
2426
TEST_F(FPDFEditEmbedderTest,PathsPoints)2427 TEST_F(FPDFEditEmbedderTest, PathsPoints) {
2428 CreateNewDocument();
2429 FPDF_PAGEOBJECT img = FPDFPageObj_NewImageObj(document());
2430 // This should fail gracefully, even if img is not a path.
2431 ASSERT_EQ(-1, FPDFPath_CountSegments(img));
2432
2433 // This should fail gracefully, even if path is NULL.
2434 ASSERT_EQ(-1, FPDFPath_CountSegments(nullptr));
2435
2436 // FPDFPath_GetPathSegment() with a non-path.
2437 ASSERT_EQ(nullptr, FPDFPath_GetPathSegment(img, 0));
2438 // FPDFPath_GetPathSegment() with a NULL path.
2439 ASSERT_EQ(nullptr, FPDFPath_GetPathSegment(nullptr, 0));
2440 float x;
2441 float y;
2442 // FPDFPathSegment_GetPoint() with a NULL segment.
2443 EXPECT_FALSE(FPDFPathSegment_GetPoint(nullptr, &x, &y));
2444
2445 // FPDFPathSegment_GetType() with a NULL segment.
2446 ASSERT_EQ(FPDF_SEGMENT_UNKNOWN, FPDFPathSegment_GetType(nullptr));
2447
2448 // FPDFPathSegment_GetClose() with a NULL segment.
2449 EXPECT_FALSE(FPDFPathSegment_GetClose(nullptr));
2450
2451 FPDFPageObj_Destroy(img);
2452 }
2453
TEST_F(FPDFEditEmbedderTest,PathOnTopOfText)2454 TEST_F(FPDFEditEmbedderTest, PathOnTopOfText) {
2455 // Load document with some text
2456 ASSERT_TRUE(OpenDocument("hello_world.pdf"));
2457 ScopedEmbedderTestPage page = LoadScopedPage(0);
2458 ASSERT_TRUE(page);
2459
2460 // Add an opaque rectangle on top of some of the text.
2461 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(20, 100, 50, 50);
2462 EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
2463 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
2464 FPDFPage_InsertObject(page.get(), red_rect);
2465
2466 // Add a transparent triangle on top of other part of the text.
2467 FPDF_PAGEOBJECT black_path = FPDFPageObj_CreateNewPath(20, 50);
2468 EXPECT_TRUE(FPDFPageObj_SetFillColor(black_path, 0, 0, 0, 100));
2469 EXPECT_TRUE(FPDFPath_SetDrawMode(black_path, FPDF_FILLMODE_ALTERNATE, 0));
2470 EXPECT_TRUE(FPDFPath_LineTo(black_path, 30, 80));
2471 EXPECT_TRUE(FPDFPath_LineTo(black_path, 40, 10));
2472 EXPECT_TRUE(FPDFPath_Close(black_path));
2473 FPDFPage_InsertObject(page.get(), black_path);
2474
2475 // Render and check the result.
2476 ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get());
2477 const char* checksum = []() {
2478 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
2479 #if BUILDFLAG(IS_WIN)
2480 return "52c133b5b6bf76760c59cffc12c1131a";
2481 #elif BUILDFLAG(IS_APPLE)
2482 return "9eba2a1a6599c2abcf002012217a6505";
2483 #else
2484 return "72523cfac069f8a81057164682998961";
2485 #endif
2486 }
2487 #if BUILDFLAG(IS_APPLE)
2488 return "279693baca9f48da2d75a8e289aed58e";
2489 #else
2490 return "fe415d47945c10b9cc8e9ca08887369e";
2491 #endif
2492 }();
2493 CompareBitmap(bitmap.get(), 200, 200, checksum);
2494 }
2495
TEST_F(FPDFEditEmbedderTest,EditOverExistingContent)2496 TEST_F(FPDFEditEmbedderTest, EditOverExistingContent) {
2497 // Load document with existing content
2498 ASSERT_TRUE(OpenDocument("bug_717.pdf"));
2499 ScopedEmbedderTestPage page = LoadScopedPage(0);
2500 ASSERT_TRUE(page);
2501
2502 // Add a transparent rectangle on top of the existing content
2503 FPDF_PAGEOBJECT red_rect2 = FPDFPageObj_CreateNewRect(90, 700, 25, 50);
2504 EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect2, 255, 0, 0, 100));
2505 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect2, FPDF_FILLMODE_ALTERNATE, 0));
2506 FPDFPage_InsertObject(page.get(), red_rect2);
2507
2508 // Add an opaque rectangle on top of the existing content
2509 FPDF_PAGEOBJECT red_rect = FPDFPageObj_CreateNewRect(115, 700, 25, 50);
2510 EXPECT_TRUE(FPDFPageObj_SetFillColor(red_rect, 255, 0, 0, 255));
2511 EXPECT_TRUE(FPDFPath_SetDrawMode(red_rect, FPDF_FILLMODE_ALTERNATE, 0));
2512 FPDFPage_InsertObject(page.get(), red_rect);
2513
2514 const char* original_checksum = []() {
2515 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
2516 return "1e82fbdd21490cee9d3479fe6125af67";
2517 }
2518 return "ad04e5bd0f471a9a564fb034bd0fb073";
2519 }();
2520 ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get());
2521 CompareBitmap(bitmap.get(), 612, 792, original_checksum);
2522 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2523
2524 // Now save the result, closing the page and document
2525 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2526
2527 ASSERT_TRUE(OpenSavedDocument());
2528 FPDF_PAGE saved_page = LoadSavedPage(0);
2529 ASSERT_TRUE(saved_page);
2530 VerifySavedRendering(saved_page, 612, 792, original_checksum);
2531
2532 ClearString();
2533 // Add another opaque rectangle on top of the existing content
2534 FPDF_PAGEOBJECT green_rect = FPDFPageObj_CreateNewRect(150, 700, 25, 50);
2535 EXPECT_TRUE(FPDFPageObj_SetFillColor(green_rect, 0, 255, 0, 255));
2536 EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect, FPDF_FILLMODE_ALTERNATE, 0));
2537 FPDFPage_InsertObject(saved_page, green_rect);
2538
2539 // Add another transparent rectangle on top of existing content
2540 FPDF_PAGEOBJECT green_rect2 = FPDFPageObj_CreateNewRect(175, 700, 25, 50);
2541 EXPECT_TRUE(FPDFPageObj_SetFillColor(green_rect2, 0, 255, 0, 100));
2542 EXPECT_TRUE(FPDFPath_SetDrawMode(green_rect2, FPDF_FILLMODE_ALTERNATE, 0));
2543 FPDFPage_InsertObject(saved_page, green_rect2);
2544 const char* last_checksum = []() {
2545 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
2546 return "8705d023e5fec3499d1e30cf2bcc5dc1";
2547 }
2548 return "4b5b00f824620f8c9b8801ebb98e1cdd";
2549 }();
2550 {
2551 ScopedFPDFBitmap new_bitmap = RenderSavedPage(saved_page);
2552 CompareBitmap(new_bitmap.get(), 612, 792, last_checksum);
2553 }
2554 EXPECT_TRUE(FPDFPage_GenerateContent(saved_page));
2555
2556 // Now save the result, closing the page and document
2557 EXPECT_TRUE(FPDF_SaveAsCopy(saved_document(), this, 0));
2558
2559 CloseSavedPage(saved_page);
2560 CloseSavedDocument();
2561
2562 // Render the saved result
2563 VerifySavedDocument(612, 792, last_checksum);
2564 }
2565
TEST_F(FPDFEditEmbedderTest,AddStrokedPaths)2566 TEST_F(FPDFEditEmbedderTest, AddStrokedPaths) {
2567 // Start with a blank page
2568 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
2569
2570 // Add a large stroked rectangle (fill color should not affect it).
2571 FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(20, 20, 200, 400);
2572 EXPECT_TRUE(FPDFPageObj_SetFillColor(rect, 255, 0, 0, 255));
2573 EXPECT_TRUE(FPDFPageObj_SetStrokeColor(rect, 0, 255, 0, 255));
2574 EXPECT_TRUE(FPDFPageObj_SetStrokeWidth(rect, 15.0f));
2575
2576 float width = 0;
2577 EXPECT_TRUE(FPDFPageObj_GetStrokeWidth(rect, &width));
2578 EXPECT_EQ(15.0f, width);
2579
2580 EXPECT_TRUE(FPDFPath_SetDrawMode(rect, 0, 1));
2581 FPDFPage_InsertObject(page, rect);
2582 {
2583 ScopedFPDFBitmap page_bitmap = RenderPage(page);
2584 const char* checksum_1 = []() {
2585 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
2586 return "1469acf60e7647ebeb8e1fb08c5d6c7a";
2587 }
2588 return "64bd31f862a89e0a9e505a5af6efd506";
2589 }();
2590 CompareBitmap(page_bitmap.get(), 612, 792, checksum_1);
2591 }
2592
2593 // Add crossed-checkmark
2594 FPDF_PAGEOBJECT check = FPDFPageObj_CreateNewPath(300, 500);
2595 EXPECT_TRUE(FPDFPath_LineTo(check, 400, 400));
2596 EXPECT_TRUE(FPDFPath_LineTo(check, 600, 600));
2597 EXPECT_TRUE(FPDFPath_MoveTo(check, 400, 600));
2598 EXPECT_TRUE(FPDFPath_LineTo(check, 600, 400));
2599 EXPECT_TRUE(FPDFPageObj_SetStrokeColor(check, 128, 128, 128, 180));
2600 EXPECT_TRUE(FPDFPageObj_SetStrokeWidth(check, 8.35f));
2601 EXPECT_TRUE(FPDFPath_SetDrawMode(check, 0, 1));
2602 FPDFPage_InsertObject(page, check);
2603 {
2604 ScopedFPDFBitmap page_bitmap = RenderPage(page);
2605 const char* checksum_2 = []() {
2606 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
2607 return "c4b2314ce2da802fbb390ea3bb2adae9";
2608 }
2609 return "4b6f3b9d25c4e194821217d5016c3724";
2610 }();
2611 CompareBitmap(page_bitmap.get(), 612, 792, checksum_2);
2612 }
2613
2614 // Add stroked and filled oval-ish path.
2615 FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(250, 100);
2616 EXPECT_TRUE(FPDFPath_BezierTo(path, 180, 166, 180, 233, 250, 300));
2617 EXPECT_TRUE(FPDFPath_LineTo(path, 255, 305));
2618 EXPECT_TRUE(FPDFPath_BezierTo(path, 325, 233, 325, 166, 255, 105));
2619 EXPECT_TRUE(FPDFPath_Close(path));
2620 EXPECT_TRUE(FPDFPageObj_SetFillColor(path, 200, 128, 128, 100));
2621 EXPECT_TRUE(FPDFPageObj_SetStrokeColor(path, 128, 200, 128, 150));
2622 EXPECT_TRUE(FPDFPageObj_SetStrokeWidth(path, 10.5f));
2623 EXPECT_TRUE(FPDFPath_SetDrawMode(path, FPDF_FILLMODE_ALTERNATE, 1));
2624 FPDFPage_InsertObject(page, path);
2625 {
2626 ScopedFPDFBitmap page_bitmap = RenderPage(page);
2627 const char* checksum_3 = []() {
2628 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
2629 return "e37dfe983eac22a3f936dfc86355fde5";
2630 }
2631 return "ff3e6a22326754944cc6e56609acd73b";
2632 }();
2633 CompareBitmap(page_bitmap.get(), 612, 792, checksum_3);
2634 }
2635 FPDF_ClosePage(page);
2636 }
2637
2638 // Tests adding text from standard font using FPDFPageObj_NewTextObj.
TEST_F(FPDFEditEmbedderTest,AddStandardFontText)2639 TEST_F(FPDFEditEmbedderTest, AddStandardFontText) {
2640 // Start with a blank page
2641 ScopedFPDFPage page(FPDFPage_New(CreateNewDocument(), 0, 612, 792));
2642
2643 // Add some text to the page
2644 FPDF_PAGEOBJECT text_object1 =
2645 FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
2646 EXPECT_TRUE(text_object1);
2647 ScopedFPDFWideString text1 = GetFPDFWideString(kBottomText);
2648 EXPECT_TRUE(FPDFText_SetText(text_object1, text1.get()));
2649 static constexpr FS_MATRIX kMatrix1{1, 0, 0, 1, 20, 20};
2650 EXPECT_TRUE(FPDFPageObj_SetMatrix(text_object1, &kMatrix1));
2651 FPDFPage_InsertObject(page.get(), text_object1);
2652 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2653 {
2654 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
2655 CompareBitmap(page_bitmap.get(), 612, 792, BottomTextChecksum());
2656
2657 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2658 VerifySavedDocument(612, 792, BottomTextChecksum());
2659 }
2660
2661 // Try another font
2662 FPDF_PAGEOBJECT text_object2 =
2663 FPDFPageObj_NewTextObj(document(), "TimesNewRomanBold", 15.0f);
2664 EXPECT_TRUE(text_object2);
2665 ScopedFPDFWideString text2 =
2666 GetFPDFWideString(L"Hi, I'm Bold. Times New Roman Bold.");
2667 EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
2668 FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 100, 600);
2669 FPDFPage_InsertObject(page.get(), text_object2);
2670 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2671 {
2672 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
2673 const char* checksum = []() {
2674 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
2675 #if BUILDFLAG(IS_WIN)
2676 return "667f74c7cbf72c75bce303ca2de975a3";
2677 #elif BUILDFLAG(IS_APPLE)
2678 return "86d51a764615b843465695786e92fec5";
2679 #else
2680 return "3fa05f8935a43a38a8923e9d5fb94365";
2681 #endif
2682 }
2683 #if BUILDFLAG(IS_APPLE)
2684 return "983baaa1f688eff7a14b1bf91c171a1a";
2685 #else
2686 return "161523e196eb5341604cd73e12c97922";
2687 #endif
2688 }();
2689 CompareBitmap(page_bitmap.get(), 612, 792, checksum);
2690
2691 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2692 VerifySavedDocument(612, 792, checksum);
2693 }
2694
2695 // And some randomly transformed text
2696 FPDF_PAGEOBJECT text_object3 =
2697 FPDFPageObj_NewTextObj(document(), "Courier-Bold", 20.0f);
2698 EXPECT_TRUE(text_object3);
2699 ScopedFPDFWideString text3 = GetFPDFWideString(L"Can you read me? <:)>");
2700 EXPECT_TRUE(FPDFText_SetText(text_object3, text3.get()));
2701 FPDFPageObj_Transform(text_object3, 1, 1.5, 2, 0.5, 200, 200);
2702 FPDFPage_InsertObject(page.get(), text_object3);
2703 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2704 {
2705 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
2706 const char* checksum = []() {
2707 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
2708 #if BUILDFLAG(IS_WIN)
2709 return "4695b3de213d6795a591f27cd8d86e26";
2710 #elif BUILDFLAG(IS_APPLE)
2711 return "422f1384c13e95c218498a8f18b9e5a7";
2712 #else
2713 return "63385a217934d9ee9e17ef4d7f7b2128";
2714 #endif
2715 }
2716 #if BUILDFLAG(IS_APPLE)
2717 return "e0b3493c5c16e41d0d892ffb48e63fba";
2718 #else
2719 return "1fbf772dca8d82b960631e6683934964";
2720 #endif
2721 }();
2722 CompareBitmap(page_bitmap.get(), 612, 792, checksum);
2723
2724 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2725 VerifySavedDocument(612, 792, checksum);
2726 }
2727
2728 FS_MATRIX matrix;
2729 EXPECT_TRUE(FPDFPageObj_GetMatrix(text_object3, &matrix));
2730 EXPECT_FLOAT_EQ(1.0f, matrix.a);
2731 EXPECT_FLOAT_EQ(1.5f, matrix.b);
2732 EXPECT_FLOAT_EQ(2.0f, matrix.c);
2733 EXPECT_FLOAT_EQ(0.5f, matrix.d);
2734 EXPECT_FLOAT_EQ(200.0f, matrix.e);
2735 EXPECT_FLOAT_EQ(200.0f, matrix.f);
2736
2737 EXPECT_FALSE(FPDFTextObj_GetFontSize(nullptr, nullptr));
2738 float size = 55;
2739 EXPECT_FALSE(FPDFTextObj_GetFontSize(nullptr, &size));
2740 EXPECT_EQ(55, size);
2741 EXPECT_TRUE(FPDFTextObj_GetFontSize(text_object3, &size));
2742 EXPECT_EQ(20, size);
2743
2744 // TODO(npm): Why are there issues with text rotated by 90 degrees?
2745 // TODO(npm): FPDF_SaveAsCopy not giving the desired result after this.
2746 }
2747
TEST_F(FPDFEditEmbedderTest,AddStandardFontTextOfSizeZero)2748 TEST_F(FPDFEditEmbedderTest, AddStandardFontTextOfSizeZero) {
2749 // Start with a blank page
2750 ScopedFPDFPage page(FPDFPage_New(CreateNewDocument(), 0, 612, 792));
2751
2752 // Add some text of size 0 to the page.
2753 FPDF_PAGEOBJECT text_object =
2754 FPDFPageObj_NewTextObj(document(), "Arial", 0.0f);
2755 EXPECT_TRUE(text_object);
2756 ScopedFPDFWideString text = GetFPDFWideString(kBottomText);
2757 EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
2758 FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 20, 20);
2759
2760 float size = -1; // Make sure 'size' gets changed.
2761 EXPECT_TRUE(FPDFTextObj_GetFontSize(text_object, &size));
2762 EXPECT_EQ(0.0f, size);
2763
2764 FPDFPage_InsertObject(page.get(), text_object);
2765 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2766 {
2767 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
2768 CompareBitmap(page_bitmap.get(), 612, 792,
2769 pdfium::kBlankPage612By792Checksum);
2770
2771 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2772 VerifySavedDocument(612, 792, pdfium::kBlankPage612By792Checksum);
2773 }
2774 }
2775
TEST_F(FPDFEditEmbedderTest,GetTextRenderMode)2776 TEST_F(FPDFEditEmbedderTest, GetTextRenderMode) {
2777 ASSERT_TRUE(OpenDocument("text_render_mode.pdf"));
2778 ScopedEmbedderTestPage page = LoadScopedPage(0);
2779 ASSERT_TRUE(page);
2780 ASSERT_EQ(2, FPDFPage_CountObjects(page.get()));
2781
2782 EXPECT_EQ(FPDF_TEXTRENDERMODE_UNKNOWN,
2783 FPDFTextObj_GetTextRenderMode(nullptr));
2784
2785 FPDF_PAGEOBJECT fill = FPDFPage_GetObject(page.get(), 0);
2786 EXPECT_EQ(FPDF_TEXTRENDERMODE_FILL, FPDFTextObj_GetTextRenderMode(fill));
2787
2788 FPDF_PAGEOBJECT stroke = FPDFPage_GetObject(page.get(), 1);
2789 EXPECT_EQ(FPDF_TEXTRENDERMODE_STROKE, FPDFTextObj_GetTextRenderMode(stroke));
2790 }
2791
TEST_F(FPDFEditEmbedderTest,SetTextRenderMode)2792 TEST_F(FPDFEditEmbedderTest, SetTextRenderMode) {
2793 const char* original_checksum = []() {
2794 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
2795 #if BUILDFLAG(IS_WIN)
2796 return "e17a6453cb48a600f180c5907c4ea02e";
2797 #elif BUILDFLAG(IS_APPLE)
2798 return "e2d5c32499173c0ff939ad2e7fc01fd6";
2799 #else
2800 return "48c7f21b2a1a1bbeab24cccccc131e47";
2801 #endif
2802 }
2803 #if BUILDFLAG(IS_APPLE)
2804 return "c488514ce0fc949069ff560407edacd2";
2805 #else
2806 return "97a4fcf3c9581e19917895631af31d41";
2807 #endif
2808 }();
2809 const char* stroke_checksum = []() {
2810 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
2811 return "d16eb1bb4748eeb5fb801594da70d519";
2812 }
2813 return "e06ee84aeebe926e8c980b7822027e8a";
2814 }();
2815
2816 {
2817 ASSERT_TRUE(OpenDocument("text_render_mode.pdf"));
2818 ScopedEmbedderTestPage page = LoadScopedPage(0);
2819 ASSERT_TRUE(page);
2820 ASSERT_EQ(2, FPDFPage_CountObjects(page.get()));
2821
2822 // Check the bitmap
2823 {
2824 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
2825 CompareBitmap(page_bitmap.get(), 612, 446, original_checksum);
2826 }
2827
2828 // Cannot set on a null object.
2829 EXPECT_FALSE(
2830 FPDFTextObj_SetTextRenderMode(nullptr, FPDF_TEXTRENDERMODE_UNKNOWN));
2831 EXPECT_FALSE(
2832 FPDFTextObj_SetTextRenderMode(nullptr, FPDF_TEXTRENDERMODE_INVISIBLE));
2833
2834 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 0);
2835 ASSERT_TRUE(page_object);
2836 EXPECT_EQ(FPDF_TEXTRENDERMODE_FILL,
2837 FPDFTextObj_GetTextRenderMode(page_object));
2838
2839 // Cannot set UNKNOWN as a render mode.
2840 EXPECT_FALSE(FPDFTextObj_SetTextRenderMode(page_object,
2841 FPDF_TEXTRENDERMODE_UNKNOWN));
2842
2843 EXPECT_TRUE(
2844 FPDFTextObj_SetTextRenderMode(page_object, FPDF_TEXTRENDERMODE_STROKE));
2845 EXPECT_EQ(FPDF_TEXTRENDERMODE_STROKE,
2846 FPDFTextObj_GetTextRenderMode(page_object));
2847
2848 // Check that bitmap displays changed content
2849 {
2850 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
2851 CompareBitmap(page_bitmap.get(), 612, 446, stroke_checksum);
2852 }
2853
2854 // Save a copy.
2855 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
2856 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
2857
2858 }
2859
2860 {
2861 // Open the saved copy and render it. Check that the changed text render
2862 // mode is kept in the saved copy.
2863 ASSERT_TRUE(OpenSavedDocument());
2864 FPDF_PAGE saved_page = LoadSavedPage(0);
2865 ASSERT_TRUE(saved_page);
2866
2867 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(saved_page, 0);
2868 EXPECT_TRUE(page_object);
2869 EXPECT_EQ(FPDF_TEXTRENDERMODE_STROKE,
2870 FPDFTextObj_GetTextRenderMode(page_object));
2871
2872 ScopedFPDFBitmap bitmap = RenderSavedPage(saved_page);
2873 CompareBitmap(bitmap.get(), 612, 446, stroke_checksum);
2874
2875 CloseSavedPage(saved_page);
2876 CloseSavedDocument();
2877 }
2878 }
2879
TEST_F(FPDFEditEmbedderTest,TextFontProperties)2880 TEST_F(FPDFEditEmbedderTest, TextFontProperties) {
2881 // bad object tests
2882 EXPECT_FALSE(FPDFTextObj_GetFont(nullptr));
2883 EXPECT_EQ(0U, FPDFFont_GetBaseFontName(nullptr, nullptr, 5));
2884 EXPECT_EQ(0U, FPDFFont_GetFamilyName(nullptr, nullptr, 5));
2885 EXPECT_EQ(-1, FPDFFont_GetFlags(nullptr));
2886 EXPECT_EQ(-1, FPDFFont_GetWeight(nullptr));
2887 EXPECT_FALSE(FPDFFont_GetItalicAngle(nullptr, nullptr));
2888 EXPECT_FALSE(FPDFFont_GetAscent(nullptr, 12.f, nullptr));
2889 EXPECT_FALSE(FPDFFont_GetDescent(nullptr, 12.f, nullptr));
2890 EXPECT_FALSE(FPDFFont_GetGlyphWidth(nullptr, 's', 12.f, nullptr));
2891 EXPECT_FALSE(FPDFFont_GetGlyphPath(nullptr, 's', 12.f));
2892
2893 // good object tests
2894 ASSERT_TRUE(OpenDocument("text_font.pdf"));
2895 ScopedEmbedderTestPage page = LoadScopedPage(0);
2896 ASSERT_TRUE(page);
2897 ASSERT_EQ(1, FPDFPage_CountObjects(page.get()));
2898 FPDF_PAGEOBJECT text = FPDFPage_GetObject(page.get(), 0);
2899 ASSERT_TRUE(text);
2900 float font_size;
2901 ASSERT_TRUE(FPDFTextObj_GetFontSize(text, &font_size));
2902 FPDF_FONT font = FPDFTextObj_GetFont(text);
2903 ASSERT_TRUE(font);
2904
2905 // null return pointer tests
2906 EXPECT_FALSE(FPDFFont_GetItalicAngle(font, nullptr));
2907 EXPECT_FALSE(FPDFFont_GetAscent(font, font_size, nullptr));
2908 EXPECT_FALSE(FPDFFont_GetDescent(font, font_size, nullptr));
2909 EXPECT_FALSE(FPDFFont_GetGlyphWidth(font, 's', font_size, nullptr));
2910
2911 // correct property tests
2912 {
2913 EXPECT_EQ(4, FPDFFont_GetFlags(font));
2914 EXPECT_EQ(400, FPDFFont_GetWeight(font));
2915
2916 int angle;
2917 EXPECT_TRUE(FPDFFont_GetItalicAngle(font, &angle));
2918 EXPECT_EQ(0, angle);
2919
2920 float ascent;
2921 EXPECT_TRUE(FPDFFont_GetAscent(font, font_size, &ascent));
2922 EXPECT_FLOAT_EQ(891 * font_size / 1000.0f, ascent);
2923
2924 float descent;
2925 EXPECT_TRUE(FPDFFont_GetDescent(font, font_size, &descent));
2926 EXPECT_FLOAT_EQ(-216 * font_size / 1000.0f, descent);
2927
2928 float a12;
2929 float a24;
2930 EXPECT_TRUE(FPDFFont_GetGlyphWidth(font, 'a', 12.0f, &a12));
2931 EXPECT_FLOAT_EQ(a12, 5.316f);
2932 EXPECT_TRUE(FPDFFont_GetGlyphWidth(font, 'a', 24.0f, &a24));
2933 EXPECT_FLOAT_EQ(a24, 10.632f);
2934 }
2935
2936 {
2937 // FPDFFont_GetBaseFontName() positive testing.
2938 size_t size = FPDFFont_GetBaseFontName(font, nullptr, 0);
2939 const char kExpectedFontName[] = "LiberationSerif";
2940 ASSERT_EQ(sizeof(kExpectedFontName), size);
2941 std::vector<char> font_name(size);
2942 ASSERT_EQ(size, FPDFFont_GetBaseFontName(font, font_name.data(), size));
2943 ASSERT_STREQ(kExpectedFontName, font_name.data());
2944
2945 // FPDFFont_GetBaseFontName() negative testing.
2946 ASSERT_EQ(0U, FPDFFont_GetBaseFontName(nullptr, nullptr, 0));
2947
2948 font_name.resize(2);
2949 font_name[0] = 'x';
2950 font_name[1] = '\0';
2951 size = FPDFFont_GetBaseFontName(font, font_name.data(), font_name.size());
2952 ASSERT_EQ(sizeof(kExpectedFontName), size);
2953 ASSERT_STREQ("x", font_name.data());
2954 }
2955
2956 {
2957 // FPDFFont_GetFamilyName() positive testing.
2958 unsigned long size = FPDFFont_GetFamilyName(font, nullptr, 0);
2959 const char kExpectedFontName[] = "Liberation Serif";
2960 ASSERT_EQ(sizeof(kExpectedFontName), size);
2961 std::vector<char> font_name(size);
2962 ASSERT_EQ(size, FPDFFont_GetFamilyName(font, font_name.data(), size));
2963 ASSERT_STREQ(kExpectedFontName, font_name.data());
2964
2965 // FPDFFont_GetFamilyName() negative testing.
2966 ASSERT_EQ(0U, FPDFFont_GetFamilyName(nullptr, nullptr, 0));
2967
2968 font_name.resize(2);
2969 font_name[0] = 'x';
2970 font_name[1] = '\0';
2971 size = FPDFFont_GetFamilyName(font, font_name.data(), font_name.size());
2972 ASSERT_EQ(sizeof(kExpectedFontName), size);
2973 ASSERT_STREQ("x", font_name.data());
2974 }
2975
2976 {
2977 // FPDFFont_GetFontData() positive testing.
2978 constexpr size_t kExpectedSize = 8268;
2979 std::vector<uint8_t> buf;
2980 size_t buf_bytes_required = 123;
2981 ASSERT_TRUE(FPDFFont_GetFontData(font, nullptr, 0, &buf_bytes_required));
2982 ASSERT_EQ(kExpectedSize, buf_bytes_required);
2983
2984 buf.resize(kExpectedSize);
2985 EXPECT_EQ("495800b8e56e2d37f3bc48a1b52db952", GenerateMD5Base16(buf));
2986 buf_bytes_required = 234;
2987 // Test with buffer that is too small. Make sure `buf` is unchanged.
2988 EXPECT_TRUE(FPDFFont_GetFontData(font, buf.data(), buf.size() - 1,
2989 &buf_bytes_required));
2990 EXPECT_EQ("495800b8e56e2d37f3bc48a1b52db952", GenerateMD5Base16(buf));
2991 EXPECT_EQ(kExpectedSize, buf_bytes_required);
2992
2993 // Test with buffer of the correct size.
2994 buf_bytes_required = 234;
2995 EXPECT_TRUE(FPDFFont_GetFontData(font, buf.data(), buf.size(),
2996 &buf_bytes_required));
2997 EXPECT_EQ("1a67be75f719b6c476804d85bb9e4844", GenerateMD5Base16(buf));
2998 EXPECT_EQ(kExpectedSize, buf_bytes_required);
2999
3000 // FPDFFont_GetFontData() negative testing.
3001 EXPECT_FALSE(FPDFFont_GetFontData(nullptr, nullptr, 0, nullptr));
3002 EXPECT_FALSE(FPDFFont_GetFontData(font, nullptr, 0, nullptr));
3003
3004 buf_bytes_required = 345;
3005 EXPECT_FALSE(
3006 FPDFFont_GetFontData(nullptr, nullptr, 0, &buf_bytes_required));
3007 EXPECT_EQ(345u, buf_bytes_required);
3008
3009 EXPECT_FALSE(
3010 FPDFFont_GetFontData(nullptr, buf.data(), buf.size(), nullptr));
3011 EXPECT_FALSE(FPDFFont_GetFontData(font, buf.data(), buf.size(), nullptr));
3012
3013 buf_bytes_required = 345;
3014 EXPECT_FALSE(FPDFFont_GetFontData(nullptr, buf.data(), buf.size(),
3015 &buf_bytes_required));
3016 EXPECT_EQ(345u, buf_bytes_required);
3017 }
3018 {
3019 ASSERT_EQ(1, FPDFFont_GetIsEmbedded(font));
3020 ASSERT_EQ(-1, FPDFFont_GetIsEmbedded(nullptr));
3021 }
3022 }
3023
TEST_F(FPDFEditEmbedderTest,NoEmbeddedFontData)3024 TEST_F(FPDFEditEmbedderTest, NoEmbeddedFontData) {
3025 ASSERT_TRUE(OpenDocument("hello_world.pdf"));
3026 ScopedEmbedderTestPage page = LoadScopedPage(0);
3027 ASSERT_TRUE(page);
3028 ASSERT_EQ(2, FPDFPage_CountObjects(page.get()));
3029
3030 // Since hello_world.pdf does not embed any font data, FPDFFont_GetFontData()
3031 // will return the substitution font data. Since pdfium_embeddertest is
3032 // hermetic, this first object consistently maps to Tinos-Regular.ttf.
3033 constexpr size_t kTinosRegularSize = 469968;
3034 FPDF_PAGEOBJECT text = FPDFPage_GetObject(page.get(), 0);
3035 ASSERT_TRUE(text);
3036 FPDF_FONT font = FPDFTextObj_GetFont(text);
3037 ASSERT_TRUE(font);
3038 std::vector<uint8_t> buf;
3039 buf.resize(kTinosRegularSize);
3040 size_t buf_bytes_required;
3041 ASSERT_TRUE(
3042 FPDFFont_GetFontData(font, buf.data(), buf.size(), &buf_bytes_required));
3043 EXPECT_EQ(kTinosRegularSize, buf_bytes_required);
3044 EXPECT_EQ("2b019558f2c2de0b7cbc0a6e64b20599", GenerateMD5Base16(buf));
3045 EXPECT_EQ(0, FPDFFont_GetIsEmbedded(font));
3046
3047 // Similarly, the second object consistently maps to Arimo-Regular.ttf.
3048 constexpr size_t kArimoRegularSize = 436180;
3049 text = FPDFPage_GetObject(page.get(), 1);
3050 ASSERT_TRUE(text);
3051 font = FPDFTextObj_GetFont(text);
3052 ASSERT_TRUE(font);
3053 buf.resize(kArimoRegularSize);
3054 ASSERT_TRUE(
3055 FPDFFont_GetFontData(font, buf.data(), buf.size(), &buf_bytes_required));
3056 EXPECT_EQ(kArimoRegularSize, buf_bytes_required);
3057 EXPECT_EQ("7ac02a544211773d9636e056e9da6c35", GenerateMD5Base16(buf));
3058 EXPECT_EQ(0, FPDFFont_GetIsEmbedded(font));
3059 }
3060
TEST_F(FPDFEditEmbedderTest,Type1BaseFontName)3061 TEST_F(FPDFEditEmbedderTest, Type1BaseFontName) {
3062 ASSERT_TRUE(OpenDocument("hello_world.pdf"));
3063 ScopedEmbedderTestPage page = LoadScopedPage(0);
3064 ASSERT_TRUE(page);
3065 ASSERT_EQ(2, FPDFPage_CountObjects(page.get()));
3066
3067 {
3068 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 0);
3069 ASSERT_TRUE(page_object);
3070 FPDF_FONT font = FPDFTextObj_GetFont(page_object);
3071 ASSERT_TRUE(font);
3072 size_t size = FPDFFont_GetBaseFontName(font, nullptr, 0);
3073 const char kExpectedFontName[] = "Times-Roman";
3074 ASSERT_EQ(sizeof(kExpectedFontName), size);
3075 std::vector<char> font_name(size);
3076 ASSERT_EQ(size, FPDFFont_GetBaseFontName(font, font_name.data(), size));
3077 EXPECT_STREQ(kExpectedFontName, font_name.data());
3078 }
3079 {
3080 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 1);
3081 ASSERT_TRUE(page_object);
3082 FPDF_FONT font = FPDFTextObj_GetFont(page_object);
3083 ASSERT_TRUE(font);
3084 size_t size = FPDFFont_GetBaseFontName(font, nullptr, 0);
3085 const char kExpectedFontName[] = "Helvetica";
3086 ASSERT_EQ(sizeof(kExpectedFontName), size);
3087 std::vector<char> font_name(size);
3088 ASSERT_EQ(size, FPDFFont_GetBaseFontName(font, font_name.data(), size));
3089 ASSERT_STREQ(kExpectedFontName, font_name.data());
3090 }
3091 }
3092
TEST_F(FPDFEditEmbedderTest,GlyphPaths)3093 TEST_F(FPDFEditEmbedderTest, GlyphPaths) {
3094 // bad glyphpath
3095 EXPECT_EQ(-1, FPDFGlyphPath_CountGlyphSegments(nullptr));
3096 EXPECT_FALSE(FPDFGlyphPath_GetGlyphPathSegment(nullptr, 1));
3097
3098 ASSERT_TRUE(OpenDocument("text_font.pdf"));
3099 ScopedEmbedderTestPage page = LoadScopedPage(0);
3100 ASSERT_TRUE(page);
3101 ASSERT_EQ(1, FPDFPage_CountObjects(page.get()));
3102 FPDF_PAGEOBJECT text = FPDFPage_GetObject(page.get(), 0);
3103 ASSERT_TRUE(text);
3104 FPDF_FONT font = FPDFTextObj_GetFont(text);
3105 ASSERT_TRUE(font);
3106
3107 // bad glyph argument.
3108 ASSERT_FALSE(FPDFFont_GetGlyphPath(font, 1, 12.0f));
3109
3110 // good glyphpath
3111 FPDF_GLYPHPATH gpath = FPDFFont_GetGlyphPath(font, 's', 12.0f);
3112 ASSERT_TRUE(gpath);
3113
3114 int count = FPDFGlyphPath_CountGlyphSegments(gpath);
3115 ASSERT_GT(count, 0);
3116 EXPECT_FALSE(FPDFGlyphPath_GetGlyphPathSegment(gpath, -1));
3117 EXPECT_FALSE(FPDFGlyphPath_GetGlyphPathSegment(gpath, count));
3118
3119 FPDF_PATHSEGMENT segment = FPDFGlyphPath_GetGlyphPathSegment(gpath, 1);
3120 ASSERT_TRUE(segment);
3121 EXPECT_EQ(FPDF_SEGMENT_BEZIERTO, FPDFPathSegment_GetType(segment));
3122 }
3123
TEST_F(FPDFEditEmbedderTest,FormGetObjects)3124 TEST_F(FPDFEditEmbedderTest, FormGetObjects) {
3125 ASSERT_TRUE(OpenDocument("form_object.pdf"));
3126 ScopedEmbedderTestPage page = LoadScopedPage(0);
3127 ASSERT_TRUE(page);
3128 ASSERT_EQ(1, FPDFPage_CountObjects(page.get()));
3129
3130 FPDF_PAGEOBJECT form = FPDFPage_GetObject(page.get(), 0);
3131 ASSERT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(form));
3132 ASSERT_EQ(-1, FPDFFormObj_CountObjects(nullptr));
3133 ASSERT_EQ(2, FPDFFormObj_CountObjects(form));
3134
3135 // FPDFFormObj_GetObject() positive testing.
3136 FPDF_PAGEOBJECT text1 = FPDFFormObj_GetObject(form, 0);
3137 ASSERT_TRUE(text1);
3138 float left = 0;
3139 float bottom = 0;
3140 float right = 0;
3141 float top = 0;
3142 ASSERT_TRUE(FPDFPageObj_GetBounds(text1, &left, &bottom, &right, &top));
3143 ASSERT_EQ(271, static_cast<int>(top));
3144
3145 FPDF_PAGEOBJECT text2 = FPDFFormObj_GetObject(form, 1);
3146 ASSERT_TRUE(text2);
3147 ASSERT_TRUE(FPDFPageObj_GetBounds(text2, &left, &bottom, &right, &top));
3148 ASSERT_EQ(221, static_cast<int>(top));
3149
3150 // FPDFFormObj_GetObject() negative testing.
3151 ASSERT_EQ(nullptr, FPDFFormObj_GetObject(nullptr, 0));
3152 ASSERT_EQ(nullptr, FPDFFormObj_GetObject(form, -1));
3153 ASSERT_EQ(nullptr, FPDFFormObj_GetObject(form, 2));
3154
3155 // FPDFPageObj_GetMatrix() positive testing for forms.
3156 static constexpr FS_MATRIX kMatrix = {1.0f, 1.5f, 2.0f, 2.5f, 100.0f, 200.0f};
3157 EXPECT_TRUE(FPDFPageObj_SetMatrix(form, &kMatrix));
3158
3159 FS_MATRIX matrix;
3160 EXPECT_TRUE(FPDFPageObj_GetMatrix(form, &matrix));
3161 EXPECT_FLOAT_EQ(kMatrix.a, matrix.a);
3162 EXPECT_FLOAT_EQ(kMatrix.b, matrix.b);
3163 EXPECT_FLOAT_EQ(kMatrix.c, matrix.c);
3164 EXPECT_FLOAT_EQ(kMatrix.d, matrix.d);
3165 EXPECT_FLOAT_EQ(kMatrix.e, matrix.e);
3166 EXPECT_FLOAT_EQ(kMatrix.f, matrix.f);
3167
3168 // FPDFPageObj_GetMatrix() negative testing for forms.
3169 EXPECT_FALSE(FPDFPageObj_GetMatrix(form, nullptr));
3170
3171 // Show that FPDFPage_RemoveObject() cannot remove page objects from within
3172 // `form`. This is working as intended, as FPDFPage_RemoveObject() only works
3173 // for page object within `page`.
3174 EXPECT_FALSE(FPDFPage_RemoveObject(page.get(), text1));
3175 EXPECT_FALSE(FPDFPage_RemoveObject(page.get(), text2));
3176 }
3177
TEST_F(FPDFEditEmbedderTest,ModifyFormObject)3178 TEST_F(FPDFEditEmbedderTest, ModifyFormObject) {
3179 const char* orig_checksum = []() {
3180 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
3181 #if BUILDFLAG(IS_WIN)
3182 return "9d0ca0d471efc12950f337a867ab1694";
3183 #elif BUILDFLAG(IS_APPLE)
3184 return "4cfff1919007a7faf099be5cc2cea00a";
3185 #else
3186 return "1c6dae4b04fea7430a791135721eaba5";
3187 #endif
3188 }
3189 #if BUILDFLAG(IS_APPLE)
3190 return "a637057185f50aac1aa5490f726aef95";
3191 #else
3192 return "34a9ec0a9581a7970e073c0bcc4ca676";
3193 #endif
3194 }();
3195 const char* new_checksum = []() {
3196 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
3197 #if BUILDFLAG(IS_WIN)
3198 return "dbebf244eb706dfebfd0594c23e993a9";
3199 #elif BUILDFLAG(IS_APPLE)
3200 return "eb88a6842f5e12f5180385261db1f81d";
3201 #else
3202 return "7282fe98693c0a7ad2c1b3f3f9563977";
3203 #endif
3204 }
3205 #if BUILDFLAG(IS_APPLE)
3206 return "8ad9d79b02b609ff734e2a2195c96e2d";
3207 #else
3208 return "609b5632a21c886fa93182dbc290bf7a";
3209 #endif
3210 }();
3211
3212 ASSERT_TRUE(OpenDocument("form_object.pdf"));
3213 ScopedEmbedderTestPage page = LoadScopedPage(0);
3214 ASSERT_TRUE(page);
3215 ASSERT_EQ(1, FPDFPage_CountObjects(page.get()));
3216
3217 {
3218 ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get());
3219 CompareBitmap(bitmap.get(), 62, 69, orig_checksum);
3220 }
3221
3222 FPDF_PAGEOBJECT form = FPDFPage_GetObject(page.get(), 0);
3223 ASSERT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(form));
3224
3225 FPDFPageObj_Transform(form, 0.5, 0, 0, 0.5, 0, 0);
3226 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
3227
3228 {
3229 ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get());
3230 CompareBitmap(bitmap.get(), 62, 69, new_checksum);
3231 }
3232
3233 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
3234 VerifySavedDocument(62, 69, new_checksum);
3235 }
3236
3237 // Tests adding text from standard font using FPDFText_LoadStandardFont.
TEST_F(FPDFEditEmbedderTest,AddStandardFontText2)3238 TEST_F(FPDFEditEmbedderTest, AddStandardFontText2) {
3239 // Start with a blank page
3240 ScopedFPDFPage page(FPDFPage_New(CreateNewDocument(), 0, 612, 792));
3241
3242 // Load a standard font.
3243 ScopedFPDFFont font(FPDFText_LoadStandardFont(document(), "Helvetica"));
3244 ASSERT_TRUE(font);
3245
3246 // Add some text to the page.
3247 FPDF_PAGEOBJECT text_object =
3248 FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
3249 EXPECT_TRUE(text_object);
3250 ScopedFPDFWideString text = GetFPDFWideString(kBottomText);
3251 EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
3252 FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 20, 20);
3253 FPDFPage_InsertObject(page.get(), text_object);
3254 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
3255 CompareBitmap(page_bitmap.get(), 612, 792, BottomTextChecksum());
3256 }
3257
TEST_F(FPDFEditEmbedderTest,LoadStandardFonts)3258 TEST_F(FPDFEditEmbedderTest, LoadStandardFonts) {
3259 CreateNewDocument();
3260 static constexpr const char* kStandardFontNames[] = {
3261 "Arial",
3262 "Arial-Bold",
3263 "Arial-BoldItalic",
3264 "Arial-Italic",
3265 "Courier",
3266 "Courier-BoldOblique",
3267 "Courier-Oblique",
3268 "Courier-Bold",
3269 "CourierNew",
3270 "CourierNew-Bold",
3271 "CourierNew-BoldItalic",
3272 "CourierNew-Italic",
3273 "Helvetica",
3274 "Helvetica-Bold",
3275 "Helvetica-BoldOblique",
3276 "Helvetica-Oblique",
3277 "Symbol",
3278 "TimesNewRoman",
3279 "TimesNewRoman-Bold",
3280 "TimesNewRoman-BoldItalic",
3281 "TimesNewRoman-Italic",
3282 "ZapfDingbats"};
3283 for (const char* font_name : kStandardFontNames) {
3284 ScopedFPDFFont font(FPDFText_LoadStandardFont(document(), font_name));
3285 EXPECT_TRUE(font) << font_name << " should be considered a standard font.";
3286 }
3287 static constexpr const char* kNotStandardFontNames[] = {
3288 "Abcdefg", "ArialB", "Arial-Style",
3289 "Font Name", "FontArial", "NotAStandardFontName",
3290 "TestFontName", "Quack", "Symbol-Italic",
3291 "Zapf"};
3292 for (const char* font_name : kNotStandardFontNames) {
3293 ScopedFPDFFont font(FPDFText_LoadStandardFont(document(), font_name));
3294 EXPECT_FALSE(font) << font_name
3295 << " should not be considered a standard font.";
3296 }
3297 }
3298
TEST_F(FPDFEditEmbedderTest,GraphicsData)3299 TEST_F(FPDFEditEmbedderTest, GraphicsData) {
3300 // New page
3301 ScopedFPDFPage page(FPDFPage_New(CreateNewDocument(), 0, 612, 792));
3302
3303 // Create a rect with nontrivial graphics
3304 FPDF_PAGEOBJECT rect1 = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
3305 FPDFPageObj_SetBlendMode(rect1, "Color");
3306 FPDFPage_InsertObject(page.get(), rect1);
3307 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
3308
3309 // Check that the ExtGState was created
3310 CPDF_Page* cpage = CPDFPageFromFPDFPage(page.get());
3311 RetainPtr<const CPDF_Dictionary> graphics_dict =
3312 cpage->GetResources()->GetDictFor("ExtGState");
3313 ASSERT_TRUE(graphics_dict);
3314 EXPECT_THAT(graphics_dict->GetKeys(),
3315 UnorderedElementsAreArray({"FXE1", "FXE2"}));
3316
3317 // Add a text object causing no change to the graphics dictionary
3318 FPDF_PAGEOBJECT text1 = FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
3319 // Only alpha, the last component, matters for the graphics dictionary. And
3320 // the default value is 255.
3321 EXPECT_TRUE(FPDFPageObj_SetFillColor(text1, 100, 100, 100, 255));
3322 FPDFPage_InsertObject(page.get(), text1);
3323 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
3324 EXPECT_THAT(graphics_dict->GetKeys(),
3325 UnorderedElementsAreArray({"FXE1", "FXE2"}));
3326
3327 // Add a text object increasing the size of the graphics dictionary
3328 FPDF_PAGEOBJECT text2 =
3329 FPDFPageObj_NewTextObj(document(), "Times-Roman", 12.0f);
3330 FPDFPage_InsertObject(page.get(), text2);
3331 FPDFPageObj_SetBlendMode(text2, "Darken");
3332 EXPECT_TRUE(FPDFPageObj_SetFillColor(text2, 0, 0, 255, 150));
3333 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
3334 EXPECT_THAT(graphics_dict->GetKeys(),
3335 UnorderedElementsAreArray({"FXE1", "FXE2", "FXE3"}));
3336
3337 // Add a path that should reuse graphics
3338 FPDF_PAGEOBJECT path = FPDFPageObj_CreateNewPath(400, 100);
3339 FPDFPageObj_SetBlendMode(path, "Darken");
3340 EXPECT_TRUE(FPDFPageObj_SetFillColor(path, 200, 200, 100, 150));
3341 FPDFPage_InsertObject(page.get(), path);
3342 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
3343 EXPECT_THAT(graphics_dict->GetKeys(),
3344 UnorderedElementsAreArray({"FXE1", "FXE2", "FXE3"}));
3345
3346 // Add a rect increasing the size of the graphics dictionary
3347 FPDF_PAGEOBJECT rect2 = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
3348 FPDFPageObj_SetBlendMode(rect2, "Darken");
3349 EXPECT_TRUE(FPDFPageObj_SetFillColor(rect2, 0, 0, 255, 150));
3350 EXPECT_TRUE(FPDFPageObj_SetStrokeColor(rect2, 0, 0, 0, 200));
3351 FPDFPage_InsertObject(page.get(), rect2);
3352 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
3353 EXPECT_THAT(graphics_dict->GetKeys(),
3354 UnorderedElementsAreArray({"FXE1", "FXE2", "FXE3", "FXE4"}));
3355 }
3356
TEST_F(FPDFEditEmbedderTest,DoubleGenerating)3357 TEST_F(FPDFEditEmbedderTest, DoubleGenerating) {
3358 // Start with a blank page
3359 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
3360
3361 // Add a red rectangle with some non-default alpha
3362 FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(10, 10, 100, 100);
3363 EXPECT_TRUE(FPDFPageObj_SetFillColor(rect, 255, 0, 0, 128));
3364 EXPECT_TRUE(FPDFPath_SetDrawMode(rect, FPDF_FILLMODE_WINDING, 0));
3365 FPDFPage_InsertObject(page, rect);
3366 EXPECT_TRUE(FPDFPage_GenerateContent(page));
3367
3368 // Check the ExtGState
3369 CPDF_Page* cpage = CPDFPageFromFPDFPage(page);
3370 RetainPtr<const CPDF_Dictionary> graphics_dict =
3371 cpage->GetResources()->GetDictFor("ExtGState");
3372 ASSERT_TRUE(graphics_dict);
3373 EXPECT_THAT(graphics_dict->GetKeys(),
3374 UnorderedElementsAreArray({"FXE1", "FXE2"}));
3375
3376 // Check the bitmap
3377 {
3378 ScopedFPDFBitmap page_bitmap = RenderPage(page);
3379 CompareBitmap(page_bitmap.get(), 612, 792,
3380 "5384da3406d62360ffb5cac4476fff1c");
3381 }
3382
3383 // Never mind, my new favorite color is blue, increase alpha.
3384 // The red graphics state goes away.
3385 EXPECT_TRUE(FPDFPageObj_SetFillColor(rect, 0, 0, 255, 180));
3386 EXPECT_TRUE(FPDFPage_GenerateContent(page));
3387 EXPECT_THAT(graphics_dict->GetKeys(),
3388 UnorderedElementsAreArray({"FXE1", "FXE3"}));
3389
3390 // Check that bitmap displays changed content
3391 {
3392 ScopedFPDFBitmap page_bitmap = RenderPage(page);
3393 CompareBitmap(page_bitmap.get(), 612, 792,
3394 "2e51656f5073b0bee611d9cd086aa09c");
3395 }
3396
3397 // And now generate, without changes
3398 EXPECT_TRUE(FPDFPage_GenerateContent(page));
3399 EXPECT_THAT(graphics_dict->GetKeys(),
3400 UnorderedElementsAreArray({"FXE1", "FXE3"}));
3401 {
3402 ScopedFPDFBitmap page_bitmap = RenderPage(page);
3403 CompareBitmap(page_bitmap.get(), 612, 792,
3404 "2e51656f5073b0bee611d9cd086aa09c");
3405 }
3406
3407 // Add some text to the page, which starts out with no fonts.
3408 RetainPtr<const CPDF_Dictionary> font_dict =
3409 cpage->GetResources()->GetDictFor("Font");
3410 EXPECT_FALSE(font_dict);
3411 FPDF_PAGEOBJECT text_object =
3412 FPDFPageObj_NewTextObj(document(), "Arial", 12.0f);
3413 ScopedFPDFWideString text =
3414 GetFPDFWideString(L"Something something #text# something");
3415 EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
3416 FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 300, 300);
3417 FPDFPage_InsertObject(page, text_object);
3418 EXPECT_TRUE(FPDFPage_GenerateContent(page));
3419
3420 // After generating the content, there should now be a font resource.
3421 font_dict = cpage->GetResources()->GetDictFor("Font");
3422 ASSERT_TRUE(font_dict);
3423 EXPECT_THAT(graphics_dict->GetKeys(),
3424 UnorderedElementsAreArray({"FXE1", "FXE3"}));
3425 EXPECT_THAT(font_dict->GetKeys(), UnorderedElementsAreArray({"FXF1"}));
3426
3427 // Generate yet again, check dicts are reasonably sized
3428 EXPECT_TRUE(FPDFPage_GenerateContent(page));
3429 EXPECT_THAT(graphics_dict->GetKeys(),
3430 UnorderedElementsAreArray({"FXE1", "FXE3"}));
3431 EXPECT_THAT(font_dict->GetKeys(), UnorderedElementsAreArray({"FXF1"}));
3432 FPDF_ClosePage(page);
3433 }
3434
TEST_F(FPDFEditEmbedderTest,LoadSimpleType1Font)3435 TEST_F(FPDFEditEmbedderTest, LoadSimpleType1Font) {
3436 CreateNewDocument();
3437 // TODO(npm): use other fonts after disallowing loading any font as any type
3438 RetainPtr<CPDF_Font> stock_font =
3439 CPDF_Font::GetStockFont(cpdf_doc(), "Times-Bold");
3440 pdfium::span<const uint8_t> span = stock_font->GetFont()->GetFontSpan();
3441 ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
3442 FPDF_FONT_TYPE1, false));
3443 ASSERT_TRUE(font);
3444 CPDF_Font* typed_font = CPDFFontFromFPDFFont(font.get());
3445 EXPECT_TRUE(typed_font->IsType1Font());
3446
3447 RetainPtr<const CPDF_Dictionary> font_dict = typed_font->GetFontDict();
3448 EXPECT_EQ("Font", font_dict->GetNameFor("Type"));
3449 EXPECT_EQ("Type1", font_dict->GetNameFor("Subtype"));
3450 EXPECT_EQ("Tinos-Bold", font_dict->GetNameFor("BaseFont"));
3451 ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
3452 ASSERT_TRUE(font_dict->KeyExist("LastChar"));
3453 EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
3454 EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
3455
3456 RetainPtr<const CPDF_Array> widths_array = font_dict->GetArrayFor("Widths");
3457 ASSERT_TRUE(widths_array);
3458 ASSERT_EQ(224u, widths_array->size());
3459 EXPECT_EQ(250, widths_array->GetFloatAt(0));
3460 EXPECT_EQ(569, widths_array->GetFloatAt(11));
3461 EXPECT_EQ(500, widths_array->GetFloatAt(223));
3462 CheckFontDescriptor(font_dict, FPDF_FONT_TYPE1, true, false, span);
3463 }
3464
TEST_F(FPDFEditEmbedderTest,LoadSimpleTrueTypeFont)3465 TEST_F(FPDFEditEmbedderTest, LoadSimpleTrueTypeFont) {
3466 CreateNewDocument();
3467 RetainPtr<CPDF_Font> stock_font =
3468 CPDF_Font::GetStockFont(cpdf_doc(), "Courier");
3469 pdfium::span<const uint8_t> span = stock_font->GetFont()->GetFontSpan();
3470 ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
3471 FPDF_FONT_TRUETYPE, false));
3472 ASSERT_TRUE(font);
3473 CPDF_Font* typed_font = CPDFFontFromFPDFFont(font.get());
3474 EXPECT_TRUE(typed_font->IsTrueTypeFont());
3475
3476 RetainPtr<const CPDF_Dictionary> font_dict = typed_font->GetFontDict();
3477 EXPECT_EQ("Font", font_dict->GetNameFor("Type"));
3478 EXPECT_EQ("TrueType", font_dict->GetNameFor("Subtype"));
3479 EXPECT_EQ("Cousine-Regular", font_dict->GetNameFor("BaseFont"));
3480 ASSERT_TRUE(font_dict->KeyExist("FirstChar"));
3481 ASSERT_TRUE(font_dict->KeyExist("LastChar"));
3482 EXPECT_EQ(32, font_dict->GetIntegerFor("FirstChar"));
3483 EXPECT_EQ(255, font_dict->GetIntegerFor("LastChar"));
3484
3485 RetainPtr<const CPDF_Array> widths_array = font_dict->GetArrayFor("Widths");
3486 ASSERT_TRUE(widths_array);
3487 ASSERT_EQ(224u, widths_array->size());
3488 EXPECT_EQ(600, widths_array->GetFloatAt(33));
3489 EXPECT_EQ(600, widths_array->GetFloatAt(74));
3490 EXPECT_EQ(600, widths_array->GetFloatAt(223));
3491 CheckFontDescriptor(font_dict, FPDF_FONT_TRUETYPE, false, false, span);
3492 }
3493
TEST_F(FPDFEditEmbedderTest,LoadCIDType0Font)3494 TEST_F(FPDFEditEmbedderTest, LoadCIDType0Font) {
3495 CreateNewDocument();
3496 RetainPtr<CPDF_Font> stock_font =
3497 CPDF_Font::GetStockFont(cpdf_doc(), "Times-Roman");
3498 pdfium::span<const uint8_t> span = stock_font->GetFont()->GetFontSpan();
3499 ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
3500 FPDF_FONT_TYPE1, 1));
3501 ASSERT_TRUE(font);
3502 CPDF_Font* typed_font = CPDFFontFromFPDFFont(font.get());
3503 EXPECT_TRUE(typed_font->IsCIDFont());
3504
3505 // Check font dictionary entries
3506 RetainPtr<const CPDF_Dictionary> font_dict = typed_font->GetFontDict();
3507 EXPECT_EQ("Font", font_dict->GetNameFor("Type"));
3508 EXPECT_EQ("Type0", font_dict->GetNameFor("Subtype"));
3509 EXPECT_EQ("Tinos-Regular-Identity-H", font_dict->GetNameFor("BaseFont"));
3510 EXPECT_EQ("Identity-H", font_dict->GetNameFor("Encoding"));
3511 RetainPtr<const CPDF_Array> descendant_array =
3512 font_dict->GetArrayFor("DescendantFonts");
3513 ASSERT_TRUE(descendant_array);
3514 EXPECT_EQ(1u, descendant_array->size());
3515
3516 // Check the CIDFontDict
3517 RetainPtr<const CPDF_Dictionary> cidfont_dict =
3518 descendant_array->GetDictAt(0);
3519 EXPECT_EQ("Font", cidfont_dict->GetNameFor("Type"));
3520 EXPECT_EQ("CIDFontType0", cidfont_dict->GetNameFor("Subtype"));
3521 EXPECT_EQ("Tinos-Regular", cidfont_dict->GetNameFor("BaseFont"));
3522 RetainPtr<const CPDF_Dictionary> cidinfo_dict =
3523 cidfont_dict->GetDictFor("CIDSystemInfo");
3524 ASSERT_TRUE(cidinfo_dict);
3525 RetainPtr<const CPDF_Object> registry =
3526 cidinfo_dict->GetObjectFor("Registry");
3527 ASSERT_TRUE(registry);
3528 EXPECT_EQ(CPDF_Object::kString, registry->GetType());
3529 EXPECT_EQ("Adobe", registry->GetString());
3530 RetainPtr<const CPDF_Object> ordering =
3531 cidinfo_dict->GetObjectFor("Ordering");
3532 ASSERT_TRUE(ordering);
3533 EXPECT_EQ(CPDF_Object::kString, ordering->GetType());
3534 EXPECT_EQ("Identity", ordering->GetString());
3535 EXPECT_EQ(0, cidinfo_dict->GetFloatFor("Supplement"));
3536 CheckFontDescriptor(cidfont_dict.Get(), FPDF_FONT_TYPE1, false, false, span);
3537
3538 // Check widths
3539 RetainPtr<const CPDF_Array> widths_array = cidfont_dict->GetArrayFor("W");
3540 ASSERT_TRUE(widths_array);
3541 EXPECT_GT(widths_array->size(), 1u);
3542 CheckCompositeFontWidths(widths_array.Get(), typed_font);
3543 }
3544
TEST_F(FPDFEditEmbedderTest,LoadCIDType2Font)3545 TEST_F(FPDFEditEmbedderTest, LoadCIDType2Font) {
3546 CreateNewDocument();
3547 RetainPtr<CPDF_Font> stock_font =
3548 CPDF_Font::GetStockFont(cpdf_doc(), "Helvetica-Oblique");
3549 pdfium::span<const uint8_t> span = stock_font->GetFont()->GetFontSpan();
3550 ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
3551 FPDF_FONT_TRUETYPE, 1));
3552 ASSERT_TRUE(font);
3553 CPDF_Font* typed_font = CPDFFontFromFPDFFont(font.get());
3554 EXPECT_TRUE(typed_font->IsCIDFont());
3555
3556 // Check font dictionary entries
3557 RetainPtr<const CPDF_Dictionary> font_dict = typed_font->GetFontDict();
3558 EXPECT_EQ("Font", font_dict->GetNameFor("Type"));
3559 EXPECT_EQ("Type0", font_dict->GetNameFor("Subtype"));
3560 EXPECT_EQ("Arimo-Italic", font_dict->GetNameFor("BaseFont"));
3561 EXPECT_EQ("Identity-H", font_dict->GetNameFor("Encoding"));
3562 RetainPtr<const CPDF_Array> descendant_array =
3563 font_dict->GetArrayFor("DescendantFonts");
3564 ASSERT_TRUE(descendant_array);
3565 EXPECT_EQ(1u, descendant_array->size());
3566
3567 // Check the CIDFontDict
3568 RetainPtr<const CPDF_Dictionary> cidfont_dict =
3569 descendant_array->GetDictAt(0);
3570 EXPECT_EQ("Font", cidfont_dict->GetNameFor("Type"));
3571 EXPECT_EQ("CIDFontType2", cidfont_dict->GetNameFor("Subtype"));
3572 EXPECT_EQ("Arimo-Italic", cidfont_dict->GetNameFor("BaseFont"));
3573 RetainPtr<const CPDF_Dictionary> cidinfo_dict =
3574 cidfont_dict->GetDictFor("CIDSystemInfo");
3575 ASSERT_TRUE(cidinfo_dict);
3576 EXPECT_EQ("Adobe", cidinfo_dict->GetByteStringFor("Registry"));
3577 EXPECT_EQ("Identity", cidinfo_dict->GetByteStringFor("Ordering"));
3578 EXPECT_EQ(0, cidinfo_dict->GetFloatFor("Supplement"));
3579 CheckFontDescriptor(cidfont_dict.Get(), FPDF_FONT_TRUETYPE, false, true,
3580 span);
3581
3582 // Check widths
3583 RetainPtr<const CPDF_Array> widths_array = cidfont_dict->GetArrayFor("W");
3584 ASSERT_TRUE(widths_array);
3585 CheckCompositeFontWidths(widths_array.Get(), typed_font);
3586 }
3587
TEST_F(FPDFEditEmbedderTest,NormalizeNegativeRotation)3588 TEST_F(FPDFEditEmbedderTest, NormalizeNegativeRotation) {
3589 // Load document with a -90 degree rotation
3590 ASSERT_TRUE(OpenDocument("bug_713197.pdf"));
3591 ScopedEmbedderTestPage page = LoadScopedPage(0);
3592 EXPECT_TRUE(page);
3593
3594 EXPECT_EQ(3, FPDFPage_GetRotation(page.get()));
3595 }
3596
TEST_F(FPDFEditEmbedderTest,AddTrueTypeFontText)3597 TEST_F(FPDFEditEmbedderTest, AddTrueTypeFontText) {
3598 // Start with a blank page
3599 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
3600 {
3601 RetainPtr<CPDF_Font> stock_font =
3602 CPDF_Font::GetStockFont(cpdf_doc(), "Arial");
3603 pdfium::span<const uint8_t> span = stock_font->GetFont()->GetFontSpan();
3604 ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
3605 FPDF_FONT_TRUETYPE, 0));
3606 ASSERT_TRUE(font);
3607
3608 // Add some text to the page
3609 FPDF_PAGEOBJECT text_object =
3610 FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
3611 EXPECT_TRUE(text_object);
3612 ScopedFPDFWideString text = GetFPDFWideString(kLoadedFontText);
3613 EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
3614 FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 400, 400);
3615 FPDFPage_InsertObject(page, text_object);
3616 ScopedFPDFBitmap page_bitmap = RenderPage(page);
3617 CompareBitmap(page_bitmap.get(), 612, 792, LoadedFontTextChecksum());
3618
3619 // Add some more text, same font
3620 FPDF_PAGEOBJECT text_object2 =
3621 FPDFPageObj_CreateTextObj(document(), font.get(), 15.0f);
3622 ScopedFPDFWideString text2 = GetFPDFWideString(L"Bigger font size");
3623 EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
3624 FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 200, 200);
3625 FPDFPage_InsertObject(page, text_object2);
3626 }
3627 ScopedFPDFBitmap page_bitmap2 = RenderPage(page);
3628 const char* insert_true_type_checksum = []() {
3629 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
3630 #if BUILDFLAG(IS_WIN)
3631 return "7e44d135666d8bfcef5cdb4c8161fd4b";
3632 #elif BUILDFLAG(IS_APPLE)
3633 return "6bab7f663daca1aab63d787a0f5cb33b";
3634 #else
3635 return "4f9a6c7752ac7d4e4c731260fdb5af15";
3636 #endif
3637 }
3638 #if BUILDFLAG(IS_APPLE)
3639 return "c7e2271a7f30e5b919a13ead47cea105";
3640 #else
3641 return "683f4a385a891494100192cb338b11f0";
3642 #endif
3643 }();
3644 CompareBitmap(page_bitmap2.get(), 612, 792, insert_true_type_checksum);
3645
3646 EXPECT_TRUE(FPDFPage_GenerateContent(page));
3647 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
3648 FPDF_ClosePage(page);
3649
3650 VerifySavedDocument(612, 792, insert_true_type_checksum);
3651 }
3652
TEST_F(FPDFEditEmbedderTest,TransformAnnot)3653 TEST_F(FPDFEditEmbedderTest, TransformAnnot) {
3654 // Open a file with one annotation and load its first page.
3655 ASSERT_TRUE(OpenDocument("annotation_highlight_long_content.pdf"));
3656 ScopedEmbedderTestPage page = LoadScopedPage(0);
3657 ASSERT_TRUE(page);
3658
3659 {
3660 // Add an underline annotation to the page without specifying its rectangle.
3661 ScopedFPDFAnnotation annot(
3662 FPDFPage_CreateAnnot(page.get(), FPDF_ANNOT_UNDERLINE));
3663 ASSERT_TRUE(annot);
3664
3665 // FPDFPage_TransformAnnots() should run without errors when modifying
3666 // annotation rectangles.
3667 FPDFPage_TransformAnnots(page.get(), 1, 2, 3, 4, 5, 6);
3668 }
3669 }
3670
3671 // TODO(npm): Add tests using Japanese fonts in other OS.
3672 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
TEST_F(FPDFEditEmbedderTest,AddCIDFontText)3673 TEST_F(FPDFEditEmbedderTest, AddCIDFontText) {
3674 // Start with a blank page
3675 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
3676 CFX_Font cid_font;
3677 {
3678 // First, get the data from the font
3679 cid_font.LoadSubst("Noto Sans CJK JP", true, 0, 400, 0,
3680 FX_CodePage::kShiftJIS, false);
3681 EXPECT_EQ("Noto Sans CJK JP", cid_font.GetFamilyName());
3682 pdfium::span<const uint8_t> span = cid_font.GetFontSpan();
3683
3684 // Load the data into a FPDF_Font.
3685 ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
3686 FPDF_FONT_TRUETYPE, 1));
3687 ASSERT_TRUE(font);
3688
3689 // Add some text to the page
3690 FPDF_PAGEOBJECT text_object =
3691 FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
3692 ASSERT_TRUE(text_object);
3693 std::wstring wstr = L"ABCDEFGhijklmnop.";
3694 ScopedFPDFWideString text = GetFPDFWideString(wstr);
3695 EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
3696 FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 200, 200);
3697 FPDFPage_InsertObject(page, text_object);
3698
3699 // And add some Japanese characters
3700 FPDF_PAGEOBJECT text_object2 =
3701 FPDFPageObj_CreateTextObj(document(), font.get(), 18.0f);
3702 ASSERT_TRUE(text_object2);
3703 std::wstring wstr2 =
3704 L"\u3053\u3093\u306B\u3061\u306f\u4e16\u754C\u3002\u3053\u3053\u306B1"
3705 L"\u756A";
3706 ScopedFPDFWideString text2 = GetFPDFWideString(wstr2);
3707 EXPECT_TRUE(FPDFText_SetText(text_object2, text2.get()));
3708 FPDFPageObj_Transform(text_object2, 1, 0, 0, 1, 100, 500);
3709 FPDFPage_InsertObject(page, text_object2);
3710 }
3711
3712 // Check that the text renders properly.
3713 const char* checksum = []() {
3714 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
3715 return "2e174d17de96a760d42ca3a06acbf36a";
3716 }
3717 return "84d31d11b76845423a2cfc1879c0fbb9";
3718 }();
3719
3720 {
3721 ScopedFPDFBitmap page_bitmap = RenderPage(page);
3722 CompareBitmap(page_bitmap.get(), 612, 792, checksum);
3723 }
3724
3725 // Save the document, close the page.
3726 EXPECT_TRUE(FPDFPage_GenerateContent(page));
3727 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
3728 FPDF_ClosePage(page);
3729
3730 VerifySavedDocument(612, 792, checksum);
3731 }
3732 #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
3733
TEST_F(FPDFEditEmbedderTest,LoadCidType2FontCustom)3734 TEST_F(FPDFEditEmbedderTest, LoadCidType2FontCustom) {
3735 // This is the same test as FPDFEditEmbedderTest.EmbedNotoSansSCFont, but some
3736 // of the font data is provided by the caller, instead of being generated.
3737 CreateEmptyDocument();
3738 ScopedFPDFPage page(FPDFPage_New(document(), 0, 400, 400));
3739 std::string font_path;
3740 ASSERT_TRUE(PathService::GetThirdPartyFilePath(
3741 "NotoSansCJK/NotoSansSC-Regular.subset.otf", &font_path));
3742
3743 std::vector<uint8_t> font_data = GetFileContents(font_path.c_str());
3744 ASSERT_FALSE(font_data.empty());
3745
3746 static const char kToUnicodeCMap[] = R"(
3747 /CIDInit /ProcSet findresource begin
3748 12 dict begin
3749 begincmap
3750 /CIDSystemInfo <<
3751 /Registry (Adobe)
3752 /Ordering (Identity)
3753 /Supplement 0
3754 >> def
3755 /CMapName /Adobe-Identity-H def
3756 /CMapType 2 def
3757 1 begincodespacerange
3758 <0000> <FFFF>
3759 endcodespacerange
3760 5 beginbfrange
3761 <0001> <0003> [<0020> <3002> <2F00>]
3762 <0003> <0004> [<4E00> <2F06>]
3763 <0004> <0005> [<4E8C> <53E5>]
3764 <0005> <0008> [<F906> <662F> <7B2C> <884C>]
3765 <0008> <0009> [<FA08> <8FD9>]
3766 endbfrange
3767 endcmap
3768 CMapName currentdict /CMap defineresource pop
3769 end
3770 end
3771 )";
3772
3773 const std::vector<uint8_t> cid_to_gid_map = {0, 0, 0, 1, 0, 2, 0, 3, 0, 4,
3774 0, 5, 0, 6, 0, 7, 0, 8, 0, 9};
3775
3776 ScopedFPDFFont font(FPDFText_LoadCidType2Font(
3777 document(), font_data.data(), font_data.size(), kToUnicodeCMap,
3778 cid_to_gid_map.data(), cid_to_gid_map.size()));
3779 ASSERT_TRUE(font);
3780
3781 FPDF_PAGEOBJECT text_object =
3782 FPDFPageObj_CreateTextObj(document(), font.get(), 20.0f);
3783 EXPECT_TRUE(text_object);
3784
3785 // Test the characters which are either mapped to one single unicode or
3786 // multiple unicodes in the embedded font.
3787 ScopedFPDFWideString text = GetFPDFWideString(L"这是第一句。 这是第二行。");
3788 EXPECT_TRUE(FPDFText_SetText(text_object, text.get()));
3789
3790 FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 50, 200);
3791 FPDFPage_InsertObject(page.get(), text_object);
3792 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
3793
3794 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
3795 CompareBitmap(page_bitmap.get(), 400, 400, NotoSansSCChecksum());
3796
3797 ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
3798 VerifySavedDocument(400, 400, NotoSansSCChecksum());
3799 }
3800
TEST_F(FPDFEditEmbedderTest,LoadCidType2FontWithBadParameters)3801 TEST_F(FPDFEditEmbedderTest, LoadCidType2FontWithBadParameters) {
3802 ASSERT_TRUE(CreateNewDocument());
3803
3804 const std::vector<uint8_t> dummy_vec(3);
3805 const char kDummyString[] = "dummy";
3806 EXPECT_FALSE(FPDFText_LoadCidType2Font(nullptr, dummy_vec.data(),
3807 dummy_vec.size(), kDummyString,
3808 dummy_vec.data(), dummy_vec.size()));
3809 EXPECT_FALSE(FPDFText_LoadCidType2Font(document(), nullptr, dummy_vec.size(),
3810 kDummyString, dummy_vec.data(),
3811 dummy_vec.size()));
3812 EXPECT_FALSE(FPDFText_LoadCidType2Font(document(), dummy_vec.data(), 0,
3813 kDummyString, dummy_vec.data(),
3814 dummy_vec.size()));
3815 EXPECT_FALSE(FPDFText_LoadCidType2Font(document(), dummy_vec.data(),
3816 dummy_vec.size(), nullptr,
3817 dummy_vec.data(), dummy_vec.size()));
3818 EXPECT_FALSE(FPDFText_LoadCidType2Font(document(), dummy_vec.data(),
3819 dummy_vec.size(), "", dummy_vec.data(),
3820 dummy_vec.size()));
3821 EXPECT_FALSE(FPDFText_LoadCidType2Font(document(), dummy_vec.data(),
3822 dummy_vec.size(), kDummyString,
3823 nullptr, dummy_vec.size()));
3824 EXPECT_FALSE(FPDFText_LoadCidType2Font(document(), dummy_vec.data(),
3825 dummy_vec.size(), kDummyString,
3826 dummy_vec.data(), 0));
3827 }
3828
TEST_F(FPDFEditEmbedderTest,SaveAndRender)3829 TEST_F(FPDFEditEmbedderTest, SaveAndRender) {
3830 const char* checksum = []() {
3831 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
3832 return "9a78649e85e69d220c22e0fc316da740";
3833 }
3834 return "3c20472b0552c0c22b88ab1ed8c6202b";
3835 }();
3836 {
3837 ASSERT_TRUE(OpenDocument("bug_779.pdf"));
3838 ScopedEmbedderTestPage page = LoadScopedPage(0);
3839 ASSERT_NE(nullptr, page.get());
3840
3841 // Now add a more complex green path.
3842 FPDF_PAGEOBJECT green_path = FPDFPageObj_CreateNewPath(20, 20);
3843 EXPECT_TRUE(FPDFPageObj_SetFillColor(green_path, 0, 255, 0, 200));
3844 // TODO(npm): stroking will cause the checksums to differ.
3845 EXPECT_TRUE(FPDFPath_SetDrawMode(green_path, FPDF_FILLMODE_WINDING, 0));
3846 EXPECT_TRUE(FPDFPath_LineTo(green_path, 20, 63));
3847 EXPECT_TRUE(FPDFPath_BezierTo(green_path, 55, 55, 78, 78, 90, 90));
3848 EXPECT_TRUE(FPDFPath_LineTo(green_path, 133, 133));
3849 EXPECT_TRUE(FPDFPath_LineTo(green_path, 133, 33));
3850 EXPECT_TRUE(FPDFPath_BezierTo(green_path, 38, 33, 39, 36, 40, 40));
3851 EXPECT_TRUE(FPDFPath_Close(green_path));
3852 FPDFPage_InsertObject(page.get(), green_path);
3853 ScopedFPDFBitmap page_bitmap = RenderLoadedPage(page.get());
3854 CompareBitmap(page_bitmap.get(), 612, 792, checksum);
3855
3856 // Now save the result, closing the page and document
3857 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
3858 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
3859 }
3860
3861 VerifySavedDocument(612, 792, checksum);
3862 }
3863
TEST_F(FPDFEditEmbedderTest,AddMark)3864 TEST_F(FPDFEditEmbedderTest, AddMark) {
3865 // Load document with some text.
3866 ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
3867 ScopedEmbedderTestPage page = LoadScopedPage(0);
3868 ASSERT_TRUE(page);
3869
3870 CheckMarkCounts(page.get(), 1, 19, 8, 4, 9, 1);
3871
3872 // Add to the first page object a "Bounds" mark with "Position": "First".
3873 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 0);
3874 FPDF_PAGEOBJECTMARK mark = FPDFPageObj_AddMark(page_object, "Bounds");
3875 EXPECT_TRUE(mark);
3876 EXPECT_TRUE(FPDFPageObjMark_SetStringParam(document(), page_object, mark,
3877 "Position", "First"));
3878
3879 CheckMarkCounts(page.get(), 1, 19, 8, 4, 9, 2);
3880
3881 // Save the file
3882 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
3883 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
3884
3885 // Re-open the file and check the new mark is present.
3886 ASSERT_TRUE(OpenSavedDocument());
3887 FPDF_PAGE saved_page = LoadSavedPage(0);
3888 ASSERT_TRUE(saved_page);
3889
3890 CheckMarkCounts(saved_page, 1, 19, 8, 4, 9, 2);
3891
3892 CloseSavedPage(saved_page);
3893 CloseSavedDocument();
3894 }
3895
TEST_F(FPDFEditEmbedderTest,AddMarkCompressedStream)3896 TEST_F(FPDFEditEmbedderTest, AddMarkCompressedStream) {
3897 // Load document with some text in a compressed stream.
3898 ASSERT_TRUE(OpenDocument("hello_world_compressed_stream.pdf"));
3899 ScopedEmbedderTestPage page = LoadScopedPage(0);
3900 ASSERT_TRUE(page);
3901
3902 // Render and check there are no marks.
3903 {
3904 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
3905 CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldChecksum());
3906 }
3907 CheckMarkCounts(page.get(), 0, 2, 0, 0, 0, 0);
3908
3909 // Add to the first page object a "Bounds" mark with "Position": "First".
3910 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 0);
3911 FPDF_PAGEOBJECTMARK mark = FPDFPageObj_AddMark(page_object, "Bounds");
3912 EXPECT_TRUE(mark);
3913 EXPECT_TRUE(FPDFPageObjMark_SetStringParam(document(), page_object, mark,
3914 "Position", "First"));
3915
3916 // Render and check there is 1 mark.
3917 {
3918 ScopedFPDFBitmap page_bitmap = RenderPage(page.get());
3919 CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldChecksum());
3920 }
3921 CheckMarkCounts(page.get(), 0, 2, 0, 0, 0, 1);
3922
3923 // Save the file.
3924 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
3925 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
3926
3927 // Re-open the file and check the new mark is present.
3928 ASSERT_TRUE(OpenSavedDocument());
3929 FPDF_PAGE saved_page = LoadSavedPage(0);
3930 ASSERT_TRUE(saved_page);
3931
3932 {
3933 ScopedFPDFBitmap page_bitmap = RenderPage(saved_page);
3934 CompareBitmap(page_bitmap.get(), 200, 200, HelloWorldChecksum());
3935 }
3936 CheckMarkCounts(saved_page, 0, 2, 0, 0, 0, 1);
3937
3938 CloseSavedPage(saved_page);
3939 CloseSavedDocument();
3940 }
3941
TEST_F(FPDFEditEmbedderTest,SetMarkParam)3942 TEST_F(FPDFEditEmbedderTest, SetMarkParam) {
3943 // Load document with some text.
3944 ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
3945 ScopedEmbedderTestPage page = LoadScopedPage(0);
3946 ASSERT_TRUE(page);
3947
3948 constexpr int kExpectedObjectCount = 19;
3949 CheckMarkCounts(page.get(), 1, kExpectedObjectCount, 8, 4, 9, 1);
3950
3951 // Check the "Bounds" mark's "Position" param is "Last".
3952 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 18);
3953 FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, 1);
3954 ASSERT_TRUE(mark);
3955 FPDF_WCHAR buffer[128];
3956 unsigned long name_len = 999u;
3957 ASSERT_TRUE(FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
3958 EXPECT_EQ((6u + 1u) * 2u, name_len);
3959 ASSERT_EQ(L"Bounds", GetPlatformWString(buffer));
3960 unsigned long out_buffer_len;
3961 ASSERT_TRUE(FPDFPageObjMark_GetParamStringValue(
3962 mark, "Position", buffer, sizeof(buffer), &out_buffer_len));
3963 ASSERT_EQ(L"Last", GetPlatformWString(buffer));
3964
3965 // Set is to "End".
3966 EXPECT_TRUE(FPDFPageObjMark_SetStringParam(document(), page_object, mark,
3967 "Position", "End"));
3968
3969 // Verify the object passed must correspond to the mark passed.
3970 FPDF_PAGEOBJECT another_page_object = FPDFPage_GetObject(page.get(), 17);
3971 EXPECT_FALSE(FPDFPageObjMark_SetStringParam(document(), another_page_object,
3972 mark, "Position", "End"));
3973
3974 // Verify nothing else changed.
3975 CheckMarkCounts(page.get(), 1, kExpectedObjectCount, 8, 4, 9, 1);
3976
3977 // Verify "Position" now maps to "End".
3978 EXPECT_TRUE(FPDFPageObjMark_GetParamStringValue(
3979 mark, "Position", buffer, sizeof(buffer), &out_buffer_len));
3980 EXPECT_EQ(L"End", GetPlatformWString(buffer));
3981
3982 // Save the file
3983 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
3984 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
3985
3986 // Re-open the file and cerify "Position" still maps to "End".
3987 ASSERT_TRUE(OpenSavedDocument());
3988 FPDF_PAGE saved_page = LoadSavedPage(0);
3989 ASSERT_TRUE(saved_page);
3990
3991 CheckMarkCounts(saved_page, 1, kExpectedObjectCount, 8, 4, 9, 1);
3992 page_object = FPDFPage_GetObject(saved_page, 18);
3993 mark = FPDFPageObj_GetMark(page_object, 1);
3994 EXPECT_TRUE(FPDFPageObjMark_GetParamStringValue(
3995 mark, "Position", buffer, sizeof(buffer), &out_buffer_len));
3996 EXPECT_EQ(L"End", GetPlatformWString(buffer));
3997
3998 CloseSavedPage(saved_page);
3999 CloseSavedDocument();
4000 }
4001
TEST_F(FPDFEditEmbedderTest,AddMarkedText)4002 TEST_F(FPDFEditEmbedderTest, AddMarkedText) {
4003 // Start with a blank page.
4004 FPDF_PAGE page = FPDFPage_New(CreateNewDocument(), 0, 612, 792);
4005
4006 RetainPtr<CPDF_Font> stock_font =
4007 CPDF_Font::GetStockFont(cpdf_doc(), "Arial");
4008 pdfium::span<const uint8_t> span = stock_font->GetFont()->GetFontSpan();
4009 ScopedFPDFFont font(FPDFText_LoadFont(document(), span.data(), span.size(),
4010 FPDF_FONT_TRUETYPE, 0));
4011 ASSERT_TRUE(font.get());
4012
4013 // Add some text to the page.
4014 FPDF_PAGEOBJECT text_object =
4015 FPDFPageObj_CreateTextObj(document(), font.get(), 12.0f);
4016
4017 EXPECT_TRUE(text_object);
4018 ScopedFPDFWideString text1 = GetFPDFWideString(kLoadedFontText);
4019 EXPECT_TRUE(FPDFText_SetText(text_object, text1.get()));
4020 FPDFPageObj_Transform(text_object, 1, 0, 0, 1, 400, 400);
4021 FPDFPage_InsertObject(page, text_object);
4022
4023 // Add a mark with the tag "TestMarkName" to that text.
4024 EXPECT_EQ(0, FPDFPageObj_CountMarks(text_object));
4025 FPDF_PAGEOBJECTMARK mark = FPDFPageObj_AddMark(text_object, "Test Mark Name");
4026 EXPECT_TRUE(mark);
4027 EXPECT_EQ(1, FPDFPageObj_CountMarks(text_object));
4028 EXPECT_EQ(mark, FPDFPageObj_GetMark(text_object, 0));
4029 FPDF_WCHAR buffer[128];
4030 unsigned long name_len = 999u;
4031 ASSERT_TRUE(FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
4032 EXPECT_EQ((14u + 1u) * 2, name_len);
4033 std::wstring name = GetPlatformWString(buffer);
4034 EXPECT_EQ(L"Test Mark Name", name);
4035
4036 // Add parameters:
4037 // - int "IntKey" : 42
4038 // - string "StringKey": "StringValue"
4039 // - blob "BlobKey": "\x01\x02\x03\0BlobValue1\0\0\0BlobValue2\0"
4040 //
4041 // Note that the trailing NUL is in `kBlobData` implicitly.
4042 constexpr uint8_t kBlobData[] = "\x01\x02\x03\0BlobValue1\0\0\0BlobValue2";
4043 EXPECT_EQ(0, FPDFPageObjMark_CountParams(mark));
4044 EXPECT_TRUE(
4045 FPDFPageObjMark_SetIntParam(document(), text_object, mark, "IntKey", 42));
4046 EXPECT_TRUE(FPDFPageObjMark_SetStringParam(document(), text_object, mark,
4047 "StringKey", "StringValue"));
4048 EXPECT_TRUE(FPDFPageObjMark_SetBlobParam(
4049 document(), text_object, mark, "BlobKey", kBlobData, sizeof(kBlobData)));
4050 EXPECT_EQ(3, FPDFPageObjMark_CountParams(mark));
4051
4052 // Check the two parameters can be retrieved.
4053 EXPECT_EQ(FPDF_OBJECT_NUMBER,
4054 FPDFPageObjMark_GetParamValueType(mark, "IntKey"));
4055 int int_value;
4056 EXPECT_TRUE(FPDFPageObjMark_GetParamIntValue(mark, "IntKey", &int_value));
4057 EXPECT_EQ(42, int_value);
4058
4059 EXPECT_EQ(FPDF_OBJECT_STRING,
4060 FPDFPageObjMark_GetParamValueType(mark, "StringKey"));
4061 unsigned long out_buffer_len = 999u;
4062 EXPECT_TRUE(FPDFPageObjMark_GetParamStringValue(
4063 mark, "StringKey", buffer, sizeof(buffer), &out_buffer_len));
4064 EXPECT_GT(out_buffer_len, 0u);
4065 EXPECT_NE(999u, out_buffer_len);
4066 name = GetPlatformWString(buffer);
4067 EXPECT_EQ(L"StringValue", name);
4068
4069 EXPECT_EQ(FPDF_OBJECT_STRING,
4070 FPDFPageObjMark_GetParamValueType(mark, "BlobKey"));
4071 out_buffer_len = 0;
4072 constexpr size_t kBlobLen = 28;
4073 unsigned char blob_buffer[kBlobLen];
4074 EXPECT_TRUE(FPDFPageObjMark_GetParamBlobValue(
4075 mark, "BlobKey", blob_buffer, sizeof(blob_buffer), &out_buffer_len));
4076 EXPECT_EQ(kBlobLen, out_buffer_len);
4077 EXPECT_TRUE(fxcrt::span_equals(pdfium::make_span(kBlobData),
4078 pdfium::make_span(blob_buffer)));
4079
4080 // Render and check the bitmap is the expected one.
4081 {
4082 ScopedFPDFBitmap page_bitmap = RenderPage(page);
4083 CompareBitmap(page_bitmap.get(), 612, 792, LoadedFontTextChecksum());
4084 }
4085
4086 // Now save the result.
4087 EXPECT_EQ(1, FPDFPage_CountObjects(page));
4088 EXPECT_TRUE(FPDFPage_GenerateContent(page));
4089 EXPECT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
4090
4091 FPDF_ClosePage(page);
4092
4093 // Re-open the file and check the changes were kept in the saved .pdf.
4094 ASSERT_TRUE(OpenSavedDocument());
4095 FPDF_PAGE saved_page = LoadSavedPage(0);
4096 ASSERT_TRUE(saved_page);
4097 EXPECT_EQ(1, FPDFPage_CountObjects(saved_page));
4098
4099 text_object = FPDFPage_GetObject(saved_page, 0);
4100 EXPECT_TRUE(text_object);
4101 EXPECT_EQ(1, FPDFPageObj_CountMarks(text_object));
4102 mark = FPDFPageObj_GetMark(text_object, 0);
4103 EXPECT_TRUE(mark);
4104
4105 name_len = 999u;
4106 ASSERT_TRUE(FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &name_len));
4107 EXPECT_EQ((14u + 1u) * 2, name_len);
4108 name = GetPlatformWString(buffer);
4109 EXPECT_EQ(L"Test Mark Name", name);
4110
4111 CloseSavedPage(saved_page);
4112 CloseSavedDocument();
4113 }
4114
TEST_F(FPDFEditEmbedderTest,MarkGetName)4115 TEST_F(FPDFEditEmbedderTest, MarkGetName) {
4116 ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
4117 ScopedEmbedderTestPage page = LoadScopedPage(0);
4118 ASSERT_TRUE(page);
4119 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 18);
4120 FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, 1);
4121 ASSERT_TRUE(mark);
4122
4123 FPDF_WCHAR buffer[128];
4124 unsigned long out_len;
4125
4126 // Show the positive cases of FPDFPageObjMark_GetName.
4127 out_len = 999u;
4128 EXPECT_TRUE(FPDFPageObjMark_GetName(mark, nullptr, 0, &out_len));
4129 EXPECT_EQ((6u + 1u) * 2u, out_len);
4130
4131 out_len = 999u;
4132 EXPECT_TRUE(FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), &out_len));
4133 EXPECT_EQ(L"Bounds", GetPlatformWString(buffer));
4134 EXPECT_EQ((6u + 1u) * 2u, out_len);
4135
4136 // Show the negative cases of FPDFPageObjMark_GetName.
4137 out_len = 999u;
4138 EXPECT_FALSE(
4139 FPDFPageObjMark_GetName(nullptr, buffer, sizeof(buffer), &out_len));
4140 EXPECT_EQ(999u, out_len);
4141
4142 EXPECT_FALSE(FPDFPageObjMark_GetName(mark, buffer, sizeof(buffer), nullptr));
4143 }
4144
TEST_F(FPDFEditEmbedderTest,MarkGetParamKey)4145 TEST_F(FPDFEditEmbedderTest, MarkGetParamKey) {
4146 ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
4147 ScopedEmbedderTestPage page = LoadScopedPage(0);
4148 ASSERT_TRUE(page);
4149 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 18);
4150 FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, 1);
4151 ASSERT_TRUE(mark);
4152
4153 FPDF_WCHAR buffer[128];
4154 unsigned long out_len;
4155
4156 // Show the positive cases of FPDFPageObjMark_GetParamKey.
4157 out_len = 999u;
4158 EXPECT_TRUE(FPDFPageObjMark_GetParamKey(mark, 0, nullptr, 0, &out_len));
4159 EXPECT_EQ((8u + 1u) * 2u, out_len);
4160
4161 out_len = 999u;
4162 EXPECT_TRUE(
4163 FPDFPageObjMark_GetParamKey(mark, 0, buffer, sizeof(buffer), &out_len));
4164 EXPECT_EQ(L"Position", GetPlatformWString(buffer));
4165 EXPECT_EQ((8u + 1u) * 2u, out_len);
4166
4167 // Show the negative cases of FPDFPageObjMark_GetParamKey.
4168 out_len = 999u;
4169 EXPECT_FALSE(FPDFPageObjMark_GetParamKey(nullptr, 0, buffer, sizeof(buffer),
4170 &out_len));
4171 EXPECT_EQ(999u, out_len);
4172
4173 out_len = 999u;
4174 EXPECT_FALSE(
4175 FPDFPageObjMark_GetParamKey(mark, 1, buffer, sizeof(buffer), &out_len));
4176 EXPECT_EQ(999u, out_len);
4177
4178 EXPECT_FALSE(
4179 FPDFPageObjMark_GetParamKey(mark, 0, buffer, sizeof(buffer), nullptr));
4180 }
4181
TEST_F(FPDFEditEmbedderTest,MarkGetIntParam)4182 TEST_F(FPDFEditEmbedderTest, MarkGetIntParam) {
4183 ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
4184 ScopedEmbedderTestPage page = LoadScopedPage(0);
4185 ASSERT_TRUE(page);
4186 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 8);
4187 FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, 0);
4188 ASSERT_TRUE(mark);
4189
4190 int out_value;
4191
4192 // Show the positive cases of FPDFPageObjMark_GetParamIntValue.
4193 out_value = 999;
4194 EXPECT_TRUE(FPDFPageObjMark_GetParamIntValue(mark, "Factor", &out_value));
4195 EXPECT_EQ(3, out_value);
4196
4197 // Show the negative cases of FPDFPageObjMark_GetParamIntValue.
4198 out_value = 999;
4199 EXPECT_FALSE(FPDFPageObjMark_GetParamIntValue(nullptr, "Factor", &out_value));
4200 EXPECT_EQ(999, out_value);
4201
4202 out_value = 999;
4203 EXPECT_FALSE(FPDFPageObjMark_GetParamIntValue(mark, "ParamThatDoesNotExist",
4204 &out_value));
4205 EXPECT_EQ(999, out_value);
4206
4207 EXPECT_FALSE(FPDFPageObjMark_GetParamIntValue(mark, "Factor", nullptr));
4208
4209 page_object = FPDFPage_GetObject(page.get(), 18);
4210 mark = FPDFPageObj_GetMark(page_object, 1);
4211 out_value = 999;
4212 EXPECT_FALSE(FPDFPageObjMark_GetParamIntValue(mark, "Position", &out_value));
4213 EXPECT_EQ(999, out_value);
4214 }
4215
TEST_F(FPDFEditEmbedderTest,MarkGetStringParam)4216 TEST_F(FPDFEditEmbedderTest, MarkGetStringParam) {
4217 ASSERT_TRUE(OpenDocument("text_in_page_marked.pdf"));
4218 ScopedEmbedderTestPage page = LoadScopedPage(0);
4219 ASSERT_TRUE(page);
4220 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 18);
4221 FPDF_PAGEOBJECTMARK mark = FPDFPageObj_GetMark(page_object, 1);
4222 ASSERT_TRUE(mark);
4223
4224 FPDF_WCHAR buffer[128];
4225 unsigned long out_len;
4226
4227 // Show the positive cases of FPDFPageObjMark_GetParamStringValue.
4228 out_len = 999u;
4229 EXPECT_TRUE(FPDFPageObjMark_GetParamStringValue(mark, "Position", nullptr, 0,
4230 &out_len));
4231 EXPECT_EQ((4u + 1u) * 2u, out_len);
4232
4233 out_len = 999u;
4234 EXPECT_TRUE(FPDFPageObjMark_GetParamStringValue(mark, "Position", buffer,
4235 sizeof(buffer), &out_len));
4236 EXPECT_EQ(L"Last", GetPlatformWString(buffer));
4237 EXPECT_EQ((4u + 1u) * 2u, out_len);
4238
4239 // Show the negative cases of FPDFPageObjMark_GetParamStringValue.
4240 out_len = 999u;
4241 EXPECT_FALSE(FPDFPageObjMark_GetParamStringValue(nullptr, "Position", buffer,
4242 sizeof(buffer), &out_len));
4243 EXPECT_EQ(999u, out_len);
4244
4245 out_len = 999u;
4246 EXPECT_FALSE(FPDFPageObjMark_GetParamStringValue(
4247 mark, "ParamThatDoesNotExist", buffer, sizeof(buffer), &out_len));
4248 EXPECT_EQ(999u, out_len);
4249
4250 EXPECT_FALSE(FPDFPageObjMark_GetParamStringValue(mark, "Position", buffer,
4251 sizeof(buffer), nullptr));
4252
4253 page_object = FPDFPage_GetObject(page.get(), 8);
4254 mark = FPDFPageObj_GetMark(page_object, 0);
4255 out_len = 999u;
4256 EXPECT_FALSE(FPDFPageObjMark_GetParamStringValue(mark, "Factor", buffer,
4257 sizeof(buffer), &out_len));
4258 EXPECT_EQ(999u, out_len);
4259 }
4260
4261 // See also FPDFStructTreeEmbedderTest.GetMarkedContentID, which traverses the
4262 // marked contents using FPDF_StructTree_GetForPage() and related API.
TEST_F(FPDFEditEmbedderTest,TraverseMarkedContentID)4263 TEST_F(FPDFEditEmbedderTest, TraverseMarkedContentID) {
4264 ASSERT_TRUE(OpenDocument("marked_content_id.pdf"));
4265 ScopedEmbedderTestPage page = LoadScopedPage(0);
4266 ASSERT_TRUE(page);
4267
4268 ASSERT_EQ(2, FPDFPage_CountObjects(page.get()));
4269 FPDF_PAGEOBJECT object1 = FPDFPage_GetObject(page.get(), 0);
4270 ASSERT_TRUE(object1);
4271 ASSERT_EQ(1, FPDFPageObj_CountMarks(object1));
4272
4273 FPDF_PAGEOBJECTMARK mark11 = FPDFPageObj_GetMark(object1, 0);
4274 ASSERT_TRUE(mark11);
4275 unsigned long len = 0;
4276 unsigned short buf[40];
4277 ASSERT_TRUE(FPDFPageObjMark_GetName(mark11, buf, sizeof(buf), &len));
4278 EXPECT_EQ(18u, len);
4279 EXPECT_EQ(L"Artifact", GetPlatformWString(buf));
4280 ASSERT_EQ(2, FPDFPageObjMark_CountParams(mark11));
4281 ASSERT_TRUE(FPDFPageObjMark_GetParamKey(mark11, 0, buf, sizeof(buf), &len));
4282 EXPECT_EQ(10u, len);
4283 EXPECT_EQ(L"BBox", GetPlatformWString(buf));
4284 EXPECT_EQ(FPDF_OBJECT_ARRAY,
4285 FPDFPageObjMark_GetParamValueType(mark11, "BBox"));
4286 ASSERT_TRUE(FPDFPageObjMark_GetParamKey(mark11, 1, buf, sizeof(buf), &len));
4287 EXPECT_EQ(10u, len);
4288 EXPECT_EQ(L"Type", GetPlatformWString(buf));
4289 EXPECT_EQ(FPDF_OBJECT_NAME,
4290 FPDFPageObjMark_GetParamValueType(mark11, "Type"));
4291
4292 FPDF_PAGEOBJECT object2 = FPDFPage_GetObject(page.get(), 1);
4293 ASSERT_TRUE(object2);
4294 ASSERT_EQ(2, FPDFPageObj_CountMarks(object2));
4295 EXPECT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(object2));
4296
4297 FPDF_PAGEOBJECTMARK mark21 = FPDFPageObj_GetMark(object2, 0);
4298 ASSERT_TRUE(mark21);
4299 ASSERT_TRUE(FPDFPageObjMark_GetName(mark21, buf, sizeof(buf), &len));
4300 EXPECT_EQ(14u, len);
4301 EXPECT_EQ(L"Figure", GetPlatformWString(buf));
4302 ASSERT_EQ(1, FPDFPageObjMark_CountParams(mark21));
4303 ASSERT_TRUE(FPDFPageObjMark_GetParamKey(mark21, 0, buf, sizeof(buf), &len));
4304 EXPECT_EQ(10u, len);
4305 EXPECT_EQ(L"MCID", GetPlatformWString(buf));
4306 ASSERT_EQ(FPDF_OBJECT_NUMBER,
4307 FPDFPageObjMark_GetParamValueType(mark21, "MCID"));
4308 int mcid = -1;
4309 ASSERT_TRUE(FPDFPageObjMark_GetParamIntValue(mark21, "MCID", &mcid));
4310 EXPECT_EQ(0, mcid);
4311
4312 FPDF_PAGEOBJECTMARK mark22 = FPDFPageObj_GetMark(object2, 1);
4313 ASSERT_TRUE(mark22);
4314 ASSERT_TRUE(FPDFPageObjMark_GetName(mark22, buf, sizeof(buf), &len));
4315 EXPECT_EQ(18u, len);
4316 EXPECT_EQ(L"ClipSpan", GetPlatformWString(buf));
4317 EXPECT_EQ(0, FPDFPageObjMark_CountParams(mark22));
4318 }
4319
TEST_F(FPDFEditEmbedderTest,GetBitmap)4320 TEST_F(FPDFEditEmbedderTest, GetBitmap) {
4321 ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
4322 ScopedEmbedderTestPage page = LoadScopedPage(0);
4323 ASSERT_TRUE(page);
4324 ASSERT_EQ(39, FPDFPage_CountObjects(page.get()));
4325
4326 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page.get(), 32);
4327 EXPECT_NE(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4328 EXPECT_FALSE(FPDFImageObj_GetBitmap(obj));
4329
4330 {
4331 obj = FPDFPage_GetObject(page.get(), 33);
4332 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4333 ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
4334 EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get()));
4335 CompareBitmap(bitmap.get(), 109, 88, kEmbeddedImage33Checksum);
4336 }
4337
4338 {
4339 obj = FPDFPage_GetObject(page.get(), 34);
4340 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4341 ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
4342 EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get()));
4343 CompareBitmap(bitmap.get(), 103, 75, "c8d51fa6821ceb2a67f08446ff236c40");
4344 }
4345
4346 {
4347 obj = FPDFPage_GetObject(page.get(), 35);
4348 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4349 ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
4350 EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get()));
4351 CompareBitmap(bitmap.get(), 92, 68, "7e34551035943e30a9f353db17de62ab");
4352 }
4353
4354 {
4355 obj = FPDFPage_GetObject(page.get(), 36);
4356 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4357 ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
4358 EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get()));
4359 CompareBitmap(bitmap.get(), 79, 60, "f4e72fb783a01c7b4614cdc25eaa98ac");
4360 }
4361
4362 {
4363 obj = FPDFPage_GetObject(page.get(), 37);
4364 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4365 ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
4366 EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get()));
4367 CompareBitmap(bitmap.get(), 126, 106, "2cf9e66414c72461f4ccbf9cdebdfa1b");
4368 }
4369
4370 {
4371 obj = FPDFPage_GetObject(page.get(), 38);
4372 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4373 ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
4374 EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get()));
4375 CompareBitmap(bitmap.get(), 194, 119, "a8f3a126cec274dab8242fd2ccdc1b8b");
4376 }
4377 }
4378
TEST_F(FPDFEditEmbedderTest,GetBitmapIgnoresSetMatrix)4379 TEST_F(FPDFEditEmbedderTest, GetBitmapIgnoresSetMatrix) {
4380 ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
4381 ScopedEmbedderTestPage page = LoadScopedPage(0);
4382 ASSERT_TRUE(page);
4383 ASSERT_EQ(39, FPDFPage_CountObjects(page.get()));
4384
4385 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page.get(), 33);
4386 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4387
4388 {
4389 // Render |obj| as is.
4390 ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
4391 EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get()));
4392 CompareBitmap(bitmap.get(), 109, 88, kEmbeddedImage33Checksum);
4393 }
4394
4395 // Check the matrix for |obj|.
4396 FS_MATRIX matrix;
4397 EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
4398 EXPECT_FLOAT_EQ(53.0f, matrix.a);
4399 EXPECT_FLOAT_EQ(0.0f, matrix.b);
4400 EXPECT_FLOAT_EQ(0.0f, matrix.c);
4401 EXPECT_FLOAT_EQ(43.0f, matrix.d);
4402 EXPECT_FLOAT_EQ(72.0f, matrix.e);
4403 EXPECT_FLOAT_EQ(646.510009765625f, matrix.f);
4404
4405 // Modify the matrix for |obj|.
4406 matrix.a = 120.0;
4407 EXPECT_TRUE(FPDFPageObj_SetMatrix(obj, &matrix));
4408
4409 // Make sure the matrix modification took place.
4410 EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
4411 EXPECT_FLOAT_EQ(120.0f, matrix.a);
4412 EXPECT_FLOAT_EQ(0.0f, matrix.b);
4413 EXPECT_FLOAT_EQ(0.0f, matrix.c);
4414 EXPECT_FLOAT_EQ(43.0f, matrix.d);
4415 EXPECT_FLOAT_EQ(72.0f, matrix.e);
4416 EXPECT_FLOAT_EQ(646.510009765625f, matrix.f);
4417
4418 {
4419 // Render |obj| again. Note that the FPDFPageObj_SetMatrix() call has no
4420 // effect.
4421 ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
4422 EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get()));
4423 CompareBitmap(bitmap.get(), 109, 88, kEmbeddedImage33Checksum);
4424 }
4425 }
4426
TEST_F(FPDFEditEmbedderTest,GetBitmapForJBigImage)4427 TEST_F(FPDFEditEmbedderTest, GetBitmapForJBigImage) {
4428 ASSERT_TRUE(OpenDocument("bug_631912.pdf"));
4429 ScopedEmbedderTestPage page = LoadScopedPage(0);
4430 ASSERT_TRUE(page);
4431 ASSERT_EQ(1, FPDFPage_CountObjects(page.get()));
4432
4433 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page.get(), 0);
4434 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4435 {
4436 ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
4437 ASSERT_TRUE(bitmap);
4438 EXPECT_EQ(FPDFBitmap_Gray, FPDFBitmap_GetFormat(bitmap.get()));
4439 CompareBitmap(bitmap.get(), 1152, 720, "3f6a48e2b3e91b799bf34567f55cb4de");
4440 }
4441 }
4442
TEST_F(FPDFEditEmbedderTest,GetBitmapIgnoresSMask)4443 TEST_F(FPDFEditEmbedderTest, GetBitmapIgnoresSMask) {
4444 ASSERT_TRUE(OpenDocument("matte.pdf"));
4445 ScopedEmbedderTestPage page = LoadScopedPage(0);
4446 ASSERT_TRUE(page);
4447
4448 constexpr int kExpectedObjects = 4;
4449 ASSERT_EQ(kExpectedObjects, FPDFPage_CountObjects(page.get()));
4450
4451 for (int i = 0; i < kExpectedObjects; ++i) {
4452 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page.get(), i);
4453 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4454 ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
4455 ASSERT_TRUE(bitmap);
4456 EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get()));
4457 CompareBitmap(bitmap.get(), 50, 50, "46c9a1dbe0b44765ce46017ad629a2fe");
4458 }
4459 }
4460
TEST_F(FPDFEditEmbedderTest,GetBitmapWithArgbImageWithPalette)4461 TEST_F(FPDFEditEmbedderTest, GetBitmapWithArgbImageWithPalette) {
4462 ASSERT_TRUE(OpenDocument("bug_343075986.pdf"));
4463
4464 ScopedEmbedderTestPage page = LoadScopedPage(0);
4465 ASSERT_TRUE(page);
4466
4467 constexpr int kExpectedObjects = 2;
4468 ASSERT_EQ(kExpectedObjects, FPDFPage_CountObjects(page.get()));
4469 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page.get(), 1);
4470 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4471
4472 ScopedFPDFBitmap bitmap(FPDFImageObj_GetBitmap(obj));
4473 ASSERT_TRUE(bitmap);
4474 EXPECT_EQ(FPDFBitmap_BGR, FPDFBitmap_GetFormat(bitmap.get()));
4475 CompareBitmap(bitmap.get(), 4, 4, "49b4d39d3fd81c9853b493b615e475d1");
4476 }
4477
TEST_F(FPDFEditEmbedderTest,GetRenderedBitmapHandlesSetMatrix)4478 TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapHandlesSetMatrix) {
4479 ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
4480 ScopedEmbedderTestPage page = LoadScopedPage(0);
4481 ASSERT_TRUE(page);
4482 ASSERT_EQ(39, FPDFPage_CountObjects(page.get()));
4483
4484 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page.get(), 33);
4485 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4486
4487 {
4488 // Render `obj` as is.
4489 ScopedFPDFBitmap bitmap(
4490 FPDFImageObj_GetRenderedBitmap(document(), page.get(), obj));
4491 EXPECT_EQ(FPDFBitmap_BGRA, FPDFBitmap_GetFormat(bitmap.get()));
4492 const char* checksum = []() {
4493 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
4494 return "3b51fc066ee18efbf70bab0501763603";
4495 }
4496 return "582ca300e003f512d7b552c7b5b45d2e";
4497 }();
4498 CompareBitmap(bitmap.get(), 53, 43, checksum);
4499 }
4500
4501 // Check the matrix for `obj`.
4502 FS_MATRIX matrix;
4503 EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
4504 EXPECT_FLOAT_EQ(53.0f, matrix.a);
4505 EXPECT_FLOAT_EQ(0.0f, matrix.b);
4506 EXPECT_FLOAT_EQ(0.0f, matrix.c);
4507 EXPECT_FLOAT_EQ(43.0f, matrix.d);
4508 EXPECT_FLOAT_EQ(72.0f, matrix.e);
4509 EXPECT_FLOAT_EQ(646.510009765625f, matrix.f);
4510
4511 // Modify the matrix for `obj`.
4512 matrix.a = 120.0;
4513 EXPECT_TRUE(FPDFPageObj_SetMatrix(obj, &matrix));
4514
4515 // Make sure the matrix modification took place.
4516 EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
4517 EXPECT_FLOAT_EQ(120.0f, matrix.a);
4518 EXPECT_FLOAT_EQ(0.0f, matrix.b);
4519 EXPECT_FLOAT_EQ(0.0f, matrix.c);
4520 EXPECT_FLOAT_EQ(43.0f, matrix.d);
4521 EXPECT_FLOAT_EQ(72.0f, matrix.e);
4522 EXPECT_FLOAT_EQ(646.510009765625f, matrix.f);
4523
4524 {
4525 // Render `obj` again. Note that the FPDFPageObj_SetMatrix() call has an
4526 // effect.
4527 ScopedFPDFBitmap bitmap(
4528 FPDFImageObj_GetRenderedBitmap(document(), page.get(), obj));
4529 EXPECT_EQ(FPDFBitmap_BGRA, FPDFBitmap_GetFormat(bitmap.get()));
4530 const char* checksum = []() {
4531 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
4532 return "1003585870ad0fe37baf1c5bb3f5fd76";
4533 }
4534 return "0824c16dcf2dfcef44b45d88db1fddce";
4535 }();
4536 CompareBitmap(bitmap.get(), 120, 43, checksum);
4537 }
4538 }
4539
TEST_F(FPDFEditEmbedderTest,GetRenderedBitmapHandlesSMask)4540 TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapHandlesSMask) {
4541 ASSERT_TRUE(OpenDocument("matte.pdf"));
4542 ScopedEmbedderTestPage page = LoadScopedPage(0);
4543 ASSERT_TRUE(page);
4544
4545 constexpr int kExpectedObjects = 4;
4546 ASSERT_EQ(kExpectedObjects, FPDFPage_CountObjects(page.get()));
4547
4548 const char* smask_checksum = []() {
4549 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
4550 return "a85ca0183ac6aee8851c30c5bdc2f594";
4551 }
4552 return "5a3ae4a660ce919e29c42ec2258142f1";
4553 }();
4554 const char* no_smask_checksum = []() {
4555 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
4556 return "712f832dcbfb6cefc74f39bef459bea4";
4557 }
4558 return "67504e83f5d78214ea00efc19082c5c1";
4559 }();
4560
4561 for (int i = 0; i < kExpectedObjects; ++i) {
4562 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page.get(), i);
4563 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4564 ScopedFPDFBitmap bitmap(
4565 FPDFImageObj_GetRenderedBitmap(document(), page.get(), obj));
4566 ASSERT_TRUE(bitmap);
4567 EXPECT_EQ(FPDFBitmap_BGRA, FPDFBitmap_GetFormat(bitmap.get()));
4568 if (i == 0)
4569 CompareBitmap(bitmap.get(), 40, 60, smask_checksum);
4570 else
4571 CompareBitmap(bitmap.get(), 40, 60, no_smask_checksum);
4572 }
4573 }
4574
TEST_F(FPDFEditEmbedderTest,GetRenderedBitmapBadParams)4575 TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapBadParams) {
4576 ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
4577 ScopedEmbedderTestPage page = LoadScopedPage(0);
4578 ASSERT_TRUE(page);
4579
4580 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page.get(), 33);
4581 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4582
4583 // Test various null parameters.
4584 EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(nullptr, nullptr, nullptr));
4585 EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(document(), nullptr, nullptr));
4586 EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(nullptr, page.get(), nullptr));
4587 EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(nullptr, nullptr, obj));
4588 EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(document(), page.get(), nullptr));
4589 EXPECT_FALSE(FPDFImageObj_GetRenderedBitmap(nullptr, page.get(), obj));
4590
4591 // Test mismatch between document and page parameters.
4592 ScopedFPDFDocument new_document(FPDF_CreateNewDocument());
4593 EXPECT_FALSE(
4594 FPDFImageObj_GetRenderedBitmap(new_document.get(), page.get(), obj));
4595 }
4596
TEST_F(FPDFEditEmbedderTest,GetImageData)4597 TEST_F(FPDFEditEmbedderTest, GetImageData) {
4598 ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
4599 ScopedEmbedderTestPage page = LoadScopedPage(0);
4600 ASSERT_TRUE(page);
4601 ASSERT_EQ(39, FPDFPage_CountObjects(page.get()));
4602
4603 // Retrieve an image object with flate-encoded data stream.
4604 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page.get(), 33);
4605 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4606
4607 // Check that the raw image data has the correct length and hash value.
4608 unsigned long len = FPDFImageObj_GetImageDataRaw(obj, nullptr, 0);
4609 std::vector<uint8_t> buf(len);
4610 EXPECT_EQ(4091u, FPDFImageObj_GetImageDataRaw(obj, buf.data(), len));
4611 EXPECT_EQ("f73802327d2e88e890f653961bcda81a", GenerateMD5Base16(buf));
4612
4613 // Check that the decoded image data has the correct length and hash value.
4614 len = FPDFImageObj_GetImageDataDecoded(obj, nullptr, 0);
4615 buf.clear();
4616 buf.resize(len);
4617 EXPECT_EQ(28776u, FPDFImageObj_GetImageDataDecoded(obj, buf.data(), len));
4618 EXPECT_EQ(kEmbeddedImage33Checksum, GenerateMD5Base16(buf));
4619
4620 // Retrieve an image object with DCTDecode-encoded data stream.
4621 obj = FPDFPage_GetObject(page.get(), 37);
4622 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4623
4624 // Check that the raw image data has the correct length and hash value.
4625 len = FPDFImageObj_GetImageDataRaw(obj, nullptr, 0);
4626 buf.clear();
4627 buf.resize(len);
4628 EXPECT_EQ(4370u, FPDFImageObj_GetImageDataRaw(obj, buf.data(), len));
4629 EXPECT_EQ("6aae1f3710335023a9e12191be66b64b", GenerateMD5Base16(buf));
4630
4631 // Check that the decoded image data has the correct length and hash value,
4632 // which should be the same as those of the raw data, since this image is
4633 // encoded by a single DCTDecode filter and decoding is a noop.
4634 len = FPDFImageObj_GetImageDataDecoded(obj, nullptr, 0);
4635 buf.clear();
4636 buf.resize(len);
4637 EXPECT_EQ(4370u, FPDFImageObj_GetImageDataDecoded(obj, buf.data(), len));
4638 EXPECT_EQ("6aae1f3710335023a9e12191be66b64b", GenerateMD5Base16(buf));
4639 }
4640
TEST_F(FPDFEditEmbedderTest,GetImageMatrix)4641 TEST_F(FPDFEditEmbedderTest, GetImageMatrix) {
4642 ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
4643 ScopedEmbedderTestPage page = LoadScopedPage(0);
4644 ASSERT_TRUE(page);
4645 ASSERT_EQ(39, FPDFPage_CountObjects(page.get()));
4646
4647 FPDF_PAGEOBJECT obj;
4648 FS_MATRIX matrix;
4649
4650 obj = FPDFPage_GetObject(page.get(), 33);
4651 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4652 EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
4653 EXPECT_FLOAT_EQ(53.0f, matrix.a);
4654 EXPECT_FLOAT_EQ(0.0f, matrix.b);
4655 EXPECT_FLOAT_EQ(0.0f, matrix.c);
4656 EXPECT_FLOAT_EQ(43.0f, matrix.d);
4657 EXPECT_FLOAT_EQ(72.0f, matrix.e);
4658 EXPECT_FLOAT_EQ(646.510009765625f, matrix.f);
4659
4660 obj = FPDFPage_GetObject(page.get(), 34);
4661 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4662 EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
4663 EXPECT_FLOAT_EQ(70.0f, matrix.a);
4664 EXPECT_FLOAT_EQ(0.0f, matrix.b);
4665 EXPECT_FLOAT_EQ(0.0f, matrix.c);
4666 EXPECT_FLOAT_EQ(51.0f, matrix.d);
4667 EXPECT_FLOAT_EQ(216.0f, matrix.e);
4668 EXPECT_FLOAT_EQ(646.510009765625f, matrix.f);
4669
4670 obj = FPDFPage_GetObject(page.get(), 35);
4671 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4672 EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
4673 EXPECT_FLOAT_EQ(69.0f, matrix.a);
4674 EXPECT_FLOAT_EQ(0.0f, matrix.b);
4675 EXPECT_FLOAT_EQ(0.0f, matrix.c);
4676 EXPECT_FLOAT_EQ(51.0f, matrix.d);
4677 EXPECT_FLOAT_EQ(360.0f, matrix.e);
4678 EXPECT_FLOAT_EQ(646.510009765625f, matrix.f);
4679
4680 obj = FPDFPage_GetObject(page.get(), 36);
4681 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4682 EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
4683 EXPECT_FLOAT_EQ(59.0f, matrix.a);
4684 EXPECT_FLOAT_EQ(0.0f, matrix.b);
4685 EXPECT_FLOAT_EQ(0.0f, matrix.c);
4686 EXPECT_FLOAT_EQ(45.0f, matrix.d);
4687 EXPECT_FLOAT_EQ(72.0f, matrix.e);
4688 EXPECT_FLOAT_EQ(553.510009765625f, matrix.f);
4689
4690 obj = FPDFPage_GetObject(page.get(), 37);
4691 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4692 EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
4693 EXPECT_FLOAT_EQ(55.94000244140625f, matrix.a);
4694 EXPECT_FLOAT_EQ(0.0f, matrix.b);
4695 EXPECT_FLOAT_EQ(0.0f, matrix.c);
4696 EXPECT_FLOAT_EQ(46.950000762939453f, matrix.d);
4697 EXPECT_FLOAT_EQ(216.0f, matrix.e);
4698 EXPECT_FLOAT_EQ(552.510009765625f, matrix.f);
4699
4700 obj = FPDFPage_GetObject(page.get(), 38);
4701 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4702 EXPECT_TRUE(FPDFPageObj_GetMatrix(obj, &matrix));
4703 EXPECT_FLOAT_EQ(70.528999328613281f, matrix.a);
4704 EXPECT_FLOAT_EQ(0.0f, matrix.b);
4705 EXPECT_FLOAT_EQ(0.0f, matrix.c);
4706 EXPECT_FLOAT_EQ(43.149997711181641f, matrix.d);
4707 EXPECT_FLOAT_EQ(360.0f, matrix.e);
4708 EXPECT_FLOAT_EQ(553.3599853515625f, matrix.f);
4709 }
4710
TEST_F(FPDFEditEmbedderTest,DestroyPageObject)4711 TEST_F(FPDFEditEmbedderTest, DestroyPageObject) {
4712 FPDF_PAGEOBJECT rect = FPDFPageObj_CreateNewRect(10, 10, 20, 20);
4713 ASSERT_TRUE(rect);
4714
4715 // There should be no memory leaks with a call to FPDFPageObj_Destroy().
4716 FPDFPageObj_Destroy(rect);
4717 }
4718
TEST_F(FPDFEditEmbedderTest,GetImageFilters)4719 TEST_F(FPDFEditEmbedderTest, GetImageFilters) {
4720 ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
4721 ScopedEmbedderTestPage page = LoadScopedPage(0);
4722 ASSERT_TRUE(page);
4723
4724 // Verify that retrieving the filter of a non-image object would fail.
4725 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page.get(), 32);
4726 ASSERT_NE(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4727 ASSERT_EQ(0, FPDFImageObj_GetImageFilterCount(obj));
4728 EXPECT_EQ(0u, FPDFImageObj_GetImageFilter(obj, 0, nullptr, 0));
4729
4730 // Verify the returned filter string for an image object with a single filter.
4731 obj = FPDFPage_GetObject(page.get(), 33);
4732 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4733 ASSERT_EQ(1, FPDFImageObj_GetImageFilterCount(obj));
4734 unsigned long len = FPDFImageObj_GetImageFilter(obj, 0, nullptr, 0);
4735 std::vector<char> buf(len);
4736 static constexpr char kFlateDecode[] = "FlateDecode";
4737 EXPECT_EQ(sizeof(kFlateDecode),
4738 FPDFImageObj_GetImageFilter(obj, 0, buf.data(), len));
4739 EXPECT_STREQ(kFlateDecode, buf.data());
4740 EXPECT_EQ(0u, FPDFImageObj_GetImageFilter(obj, 1, nullptr, 0));
4741
4742 // Verify all the filters for an image object with a list of filters.
4743 obj = FPDFPage_GetObject(page.get(), 38);
4744 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4745 ASSERT_EQ(2, FPDFImageObj_GetImageFilterCount(obj));
4746 len = FPDFImageObj_GetImageFilter(obj, 0, nullptr, 0);
4747 buf.clear();
4748 buf.resize(len);
4749 static constexpr char kASCIIHexDecode[] = "ASCIIHexDecode";
4750 EXPECT_EQ(sizeof(kASCIIHexDecode),
4751 FPDFImageObj_GetImageFilter(obj, 0, buf.data(), len));
4752 EXPECT_STREQ(kASCIIHexDecode, buf.data());
4753
4754 len = FPDFImageObj_GetImageFilter(obj, 1, nullptr, 0);
4755 buf.clear();
4756 buf.resize(len);
4757 static constexpr char kDCTDecode[] = "DCTDecode";
4758 EXPECT_EQ(sizeof(kDCTDecode),
4759 FPDFImageObj_GetImageFilter(obj, 1, buf.data(), len));
4760 EXPECT_STREQ(kDCTDecode, buf.data());
4761 }
4762
TEST_F(FPDFEditEmbedderTest,GetImageMetadata)4763 TEST_F(FPDFEditEmbedderTest, GetImageMetadata) {
4764 ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
4765 ScopedEmbedderTestPage page = LoadScopedPage(0);
4766 ASSERT_TRUE(page);
4767
4768 // Check that getting the metadata of a null object would fail.
4769 FPDF_IMAGEOBJ_METADATA metadata;
4770 EXPECT_FALSE(FPDFImageObj_GetImageMetadata(nullptr, page.get(), &metadata));
4771
4772 // Check that receiving the metadata with a null metadata object would fail.
4773 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page.get(), 35);
4774 EXPECT_FALSE(FPDFImageObj_GetImageMetadata(obj, page.get(), nullptr));
4775
4776 // Check that when retrieving an image object's metadata without passing in
4777 // |page|, all values are correct, with the last two being default values.
4778 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4779 ASSERT_TRUE(FPDFImageObj_GetImageMetadata(obj, nullptr, &metadata));
4780 EXPECT_EQ(7, metadata.marked_content_id);
4781 EXPECT_EQ(92u, metadata.width);
4782 EXPECT_EQ(68u, metadata.height);
4783 EXPECT_FLOAT_EQ(96.0f, metadata.horizontal_dpi);
4784 EXPECT_FLOAT_EQ(96.0f, metadata.vertical_dpi);
4785 EXPECT_EQ(0u, metadata.bits_per_pixel);
4786 EXPECT_EQ(FPDF_COLORSPACE_UNKNOWN, metadata.colorspace);
4787
4788 // Verify the metadata of a bitmap image with indexed colorspace.
4789 ASSERT_TRUE(FPDFImageObj_GetImageMetadata(obj, page.get(), &metadata));
4790 EXPECT_EQ(7, metadata.marked_content_id);
4791 EXPECT_EQ(92u, metadata.width);
4792 EXPECT_EQ(68u, metadata.height);
4793 EXPECT_FLOAT_EQ(96.0f, metadata.horizontal_dpi);
4794 EXPECT_FLOAT_EQ(96.0f, metadata.vertical_dpi);
4795 EXPECT_EQ(1u, metadata.bits_per_pixel);
4796 EXPECT_EQ(FPDF_COLORSPACE_INDEXED, metadata.colorspace);
4797
4798 // Verify the metadata of an image with RGB colorspace.
4799 obj = FPDFPage_GetObject(page.get(), 37);
4800 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4801 ASSERT_TRUE(FPDFImageObj_GetImageMetadata(obj, page.get(), &metadata));
4802 EXPECT_EQ(9, metadata.marked_content_id);
4803 EXPECT_EQ(126u, metadata.width);
4804 EXPECT_EQ(106u, metadata.height);
4805 EXPECT_FLOAT_EQ(162.173752f, metadata.horizontal_dpi);
4806 EXPECT_FLOAT_EQ(162.555878f, metadata.vertical_dpi);
4807 EXPECT_EQ(24u, metadata.bits_per_pixel);
4808 EXPECT_EQ(FPDF_COLORSPACE_DEVICERGB, metadata.colorspace);
4809 }
4810
TEST_F(FPDFEditEmbedderTest,GetImageMetadataJpxLzw)4811 TEST_F(FPDFEditEmbedderTest, GetImageMetadataJpxLzw) {
4812 ASSERT_TRUE(OpenDocument("jpx_lzw.pdf"));
4813 ScopedEmbedderTestPage page = LoadScopedPage(0);
4814 ASSERT_TRUE(page);
4815
4816 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page.get(), 0);
4817 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4818
4819 FPDF_IMAGEOBJ_METADATA metadata;
4820 ASSERT_TRUE(FPDFImageObj_GetImageMetadata(obj, page.get(), &metadata));
4821 EXPECT_EQ(-1, metadata.marked_content_id);
4822 EXPECT_EQ(612u, metadata.width);
4823 EXPECT_EQ(792u, metadata.height);
4824 EXPECT_FLOAT_EQ(72.0f, metadata.horizontal_dpi);
4825 EXPECT_FLOAT_EQ(72.0f, metadata.vertical_dpi);
4826 EXPECT_EQ(24u, metadata.bits_per_pixel);
4827 EXPECT_EQ(FPDF_COLORSPACE_UNKNOWN, metadata.colorspace);
4828 }
4829
TEST_F(FPDFEditEmbedderTest,GetImagePixelSize)4830 TEST_F(FPDFEditEmbedderTest, GetImagePixelSize) {
4831 ASSERT_TRUE(OpenDocument("embedded_images.pdf"));
4832 ScopedEmbedderTestPage page = LoadScopedPage(0);
4833 ASSERT_TRUE(page);
4834
4835 // Check that getting the size of a null object would fail.
4836 unsigned int width = 0;
4837 unsigned int height = 0;
4838 EXPECT_FALSE(FPDFImageObj_GetImagePixelSize(nullptr, &width, &height));
4839
4840 // Check that receiving the size with a null width and height pointers would
4841 // fail.
4842 FPDF_PAGEOBJECT obj = FPDFPage_GetObject(page.get(), 35);
4843 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4844 EXPECT_FALSE(FPDFImageObj_GetImagePixelSize(obj, nullptr, nullptr));
4845 EXPECT_FALSE(FPDFImageObj_GetImagePixelSize(obj, nullptr, &height));
4846 EXPECT_FALSE(FPDFImageObj_GetImagePixelSize(obj, &width, nullptr));
4847
4848 // Verify the pixel size of image.
4849 ASSERT_TRUE(FPDFImageObj_GetImagePixelSize(obj, &width, &height));
4850 EXPECT_EQ(92u, width);
4851 EXPECT_EQ(68u, height);
4852
4853 obj = FPDFPage_GetObject(page.get(), 37);
4854 ASSERT_EQ(FPDF_PAGEOBJ_IMAGE, FPDFPageObj_GetType(obj));
4855 ASSERT_TRUE(FPDFImageObj_GetImagePixelSize(obj, &width, &height));
4856 EXPECT_EQ(126u, width);
4857 EXPECT_EQ(106u, height);
4858 }
4859
TEST_F(FPDFEditEmbedderTest,GetRenderedBitmapForHelloWorldText)4860 TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForHelloWorldText) {
4861 ASSERT_TRUE(OpenDocument("hello_world.pdf"));
4862 ScopedEmbedderTestPage page = LoadScopedPage(0);
4863 ASSERT_TRUE(page);
4864
4865 {
4866 FPDF_PAGEOBJECT text_object = FPDFPage_GetObject(page.get(), 0);
4867 ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object));
4868
4869 ScopedFPDFBitmap bitmap(
4870 FPDFTextObj_GetRenderedBitmap(document(), page.get(), text_object, 1));
4871 ASSERT_TRUE(bitmap);
4872 const char* checksum = []() {
4873 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
4874 #if BUILDFLAG(IS_WIN)
4875 return "764e3503960ef0b176796faa3543b9c7";
4876 #elif BUILDFLAG(IS_APPLE)
4877 return "9e7774173acee966fcaa72e599eb9a93";
4878 #else
4879 return "b17801afe8a36d6aad6c2239b88f2a73";
4880 #endif
4881 }
4882 return "bb0abe1accca1cfeaaf78afa35762350";
4883 }();
4884 CompareBitmap(bitmap.get(), 64, 11, checksum);
4885
4886 ScopedFPDFBitmap x2_bitmap(FPDFTextObj_GetRenderedBitmap(
4887 document(), page.get(), text_object, 2.4f));
4888 ASSERT_TRUE(x2_bitmap);
4889 const char* x2_checksum = []() {
4890 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
4891 #if BUILDFLAG(IS_WIN)
4892 return "3cea4255285df04659e3c7477287bdb1";
4893 #elif BUILDFLAG(IS_APPLE)
4894 return "2b34bddd2a1471e245cf72603c6799b4";
4895 #else
4896 return "33af8b151ab26ebce5a71b39eedea6b1";
4897 #endif
4898 }
4899 return "80db528ec7146d92247f2339a8f10ba5";
4900 }();
4901 CompareBitmap(x2_bitmap.get(), 153, 25, x2_checksum);
4902
4903 ScopedFPDFBitmap x10_bitmap(
4904 FPDFTextObj_GetRenderedBitmap(document(), page.get(), text_object, 10));
4905 ASSERT_TRUE(x10_bitmap);
4906 const char* x10_checksum = []() {
4907 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
4908 #if BUILDFLAG(IS_WIN)
4909 return "1cc617da9ed5922eeac2414108509ef5";
4910 #elif BUILDFLAG(IS_APPLE)
4911 return "0450d576560274a7df31cb93d040e721";
4912 #else
4913 return "93dd7ad07bdaaba9ecd268350cb91596";
4914 #endif
4915 }
4916 return "149f63de758ab01d3b75605cdfd4c176";
4917 }();
4918 CompareBitmap(x10_bitmap.get(), 631, 103, x10_checksum);
4919 }
4920
4921 {
4922 FPDF_PAGEOBJECT text_object = FPDFPage_GetObject(page.get(), 1);
4923 ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object));
4924
4925 ScopedFPDFBitmap bitmap(
4926 FPDFTextObj_GetRenderedBitmap(document(), page.get(), text_object, 1));
4927 ASSERT_TRUE(bitmap);
4928 const char* checksum = []() {
4929 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
4930 #if BUILDFLAG(IS_WIN)
4931 return "4cdba7492317bcae2643bd4090e18812";
4932 #elif BUILDFLAG(IS_APPLE)
4933 return "0b9efedcb8f5aa9246c52e90811cb046";
4934 #else
4935 return "63fd059d984a5bea10f27ba026420202";
4936 #endif
4937 }
4938 return "3fc1101b2408c5484adc24ba0a11ff3d";
4939 }();
4940 CompareBitmap(bitmap.get(), 116, 16, checksum);
4941
4942 ScopedFPDFBitmap x2_bitmap(FPDFTextObj_GetRenderedBitmap(
4943 document(), page.get(), text_object, 2.4f));
4944 ASSERT_TRUE(x2_bitmap);
4945 const char* x2_checksum = []() {
4946 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
4947 #if BUILDFLAG(IS_WIN)
4948 return "c5cecc5553843a4dd4fff3ceb4855a82";
4949 #elif BUILDFLAG(IS_APPLE)
4950 return "10f4d9528a5471ab0b235984f0354dd4";
4951 #else
4952 return "fc45021e3ea3ebd406fe6ffaa8c5c5b7";
4953 #endif
4954 }
4955 return "429960ae7b822f0c630432535e637465";
4956 }();
4957 CompareBitmap(x2_bitmap.get(), 276, 36, x2_checksum);
4958
4959 ScopedFPDFBitmap x10_bitmap(
4960 FPDFTextObj_GetRenderedBitmap(document(), page.get(), text_object, 10));
4961 ASSERT_TRUE(x10_bitmap);
4962 const char* x10_checksum = []() {
4963 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
4964 #if BUILDFLAG(IS_WIN)
4965 return "cff29dcbe77092ec7f73e46766a289c7";
4966 #elif BUILDFLAG(IS_APPLE)
4967 return "9e87791ffdf4cca0a0f118be245970c8";
4968 #else
4969 return "61476636eaa0da0b93d8b1937cf22b75";
4970 #endif
4971 }
4972 return "f5f93bf64de579b59e775d7076ca0a5a";
4973 }();
4974 CompareBitmap(x10_bitmap.get(), 1143, 150, x10_checksum);
4975 }
4976 }
4977
TEST_F(FPDFEditEmbedderTest,GetRenderedBitmapForRotatedText)4978 TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForRotatedText) {
4979 ASSERT_TRUE(OpenDocument("rotated_text.pdf"));
4980 ScopedEmbedderTestPage page = LoadScopedPage(0);
4981 ASSERT_TRUE(page);
4982
4983 FPDF_PAGEOBJECT text_object = FPDFPage_GetObject(page.get(), 0);
4984 ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object));
4985
4986 ScopedFPDFBitmap bitmap(
4987 FPDFTextObj_GetRenderedBitmap(document(), page.get(), text_object, 1));
4988 ASSERT_TRUE(bitmap);
4989 const char* checksum = []() {
4990 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
4991 #if BUILDFLAG(IS_WIN)
4992 return "ba5322a4e6b0f79dca42be88f3007708";
4993 #elif BUILDFLAG(IS_APPLE)
4994 return "22cf71716a7059f481a63e32b6088c8c";
4995 #else
4996 return "f515a7209d7892065d3716ec462f5c10";
4997 #endif
4998 }
4999 return "08ada0802f780d3fefb161dc6fb45977";
5000 }();
5001 CompareBitmap(bitmap.get(), 29, 28, checksum);
5002
5003 ScopedFPDFBitmap x2_bitmap(
5004 FPDFTextObj_GetRenderedBitmap(document(), page.get(), text_object, 2.4f));
5005 ASSERT_TRUE(x2_bitmap);
5006 const char* x2_checksum = []() {
5007 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
5008 #if BUILDFLAG(IS_WIN)
5009 return "e8fb0a707b2924726757a2ed32d6f28d";
5010 #elif BUILDFLAG(IS_APPLE)
5011 return "5d4be6808bdcec3f6ee7352122dd986d";
5012 #else
5013 return "c69bbe5318ec149f63228e276e708612";
5014 #endif
5015 }
5016 return "09d7ddb647b8653cb59aede349a0c3e1";
5017 }();
5018 CompareBitmap(x2_bitmap.get(), 67, 67, x2_checksum);
5019
5020 ScopedFPDFBitmap x10_bitmap(
5021 FPDFTextObj_GetRenderedBitmap(document(), page.get(), text_object, 10));
5022 ASSERT_TRUE(x10_bitmap);
5023 const char* x10_checksum = []() {
5024 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
5025 #if BUILDFLAG(IS_WIN)
5026 return "eb0cbf56707d1c39ce0ab89a9b43d6a8";
5027 #elif BUILDFLAG(IS_APPLE)
5028 return "98757f865abde60c7f7f60c74435cb85";
5029 #else
5030 return "bb7c2ec575f27cf882dcd38f2563c00f";
5031 #endif
5032 }
5033 return "bbd3842a4b50dbfcbce4eee2b067a297";
5034 }();
5035 CompareBitmap(x10_bitmap.get(), 275, 275, x10_checksum);
5036 }
5037
TEST_F(FPDFEditEmbedderTest,GetRenderedBitmapForColorText)5038 TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForColorText) {
5039 ASSERT_TRUE(OpenDocument("text_color.pdf"));
5040 ScopedEmbedderTestPage page = LoadScopedPage(0);
5041 ASSERT_TRUE(page);
5042
5043 FPDF_PAGEOBJECT text_object = FPDFPage_GetObject(page.get(), 0);
5044 ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object));
5045
5046 ScopedFPDFBitmap bitmap(
5047 FPDFTextObj_GetRenderedBitmap(document(), page.get(), text_object, 7.3f));
5048 ASSERT_TRUE(bitmap);
5049 const char* checksum = []() {
5050 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
5051 return "9199f0c27c8a61a57189b1b044941e5e";
5052 }
5053 return "e8154fa8ededf4d9b8b35b5260897b6c";
5054 }();
5055 CompareBitmap(bitmap.get(), 120, 186, checksum);
5056 }
5057
TEST_F(FPDFEditEmbedderTest,GetRenderedBitmapForNewlyCreatedText)5058 TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForNewlyCreatedText) {
5059 // Start with a blank document.
5060 ASSERT_TRUE(CreateNewDocument());
5061
5062 // Create a new text object.
5063 ScopedFPDFPageObject text_object(
5064 FPDFPageObj_NewTextObj(document(), "Arial", 12.0f));
5065 ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text_object.get()));
5066 ScopedFPDFWideString text = GetFPDFWideString(kBottomText);
5067 EXPECT_TRUE(FPDFText_SetText(text_object.get(), text.get()));
5068
5069 ScopedFPDFBitmap bitmap(
5070 FPDFTextObj_GetRenderedBitmap(document(), nullptr, text_object.get(), 1));
5071 ASSERT_TRUE(bitmap);
5072 const char* checksum = []() {
5073 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
5074 #if BUILDFLAG(IS_WIN)
5075 return "6d88537a49fa2dccfa0f58ac325c5b75";
5076 #elif BUILDFLAG(IS_APPLE)
5077 return "a637d62f2e8ae10c3267b2ff5fcc2246";
5078 #else
5079 return "574ae982d02e653ab6a8f23a6cdf4085";
5080 #endif
5081 }
5082 return "fa947759dab76d68a07ccf6f97b2d9c2";
5083 }();
5084 CompareBitmap(bitmap.get(), 151, 12, checksum);
5085 }
5086
TEST_F(FPDFEditEmbedderTest,GetRenderedBitmapForTextWithBadParameters)5087 TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForTextWithBadParameters) {
5088 ASSERT_TRUE(OpenDocument("hello_world.pdf"));
5089 ScopedEmbedderTestPage page = LoadScopedPage(0);
5090 ASSERT_TRUE(page);
5091
5092 FPDF_PAGEOBJECT text_object = FPDFPage_GetObject(page.get(), 0);
5093 ASSERT_TRUE(text_object);
5094
5095 // Simple bad parameters testing.
5096 EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, nullptr, nullptr, 0));
5097 EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(document(), nullptr, nullptr, 0));
5098 EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, page.get(), nullptr, 0));
5099 EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, nullptr, text_object, 0));
5100 EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, nullptr, nullptr, 1));
5101 EXPECT_FALSE(
5102 FPDFTextObj_GetRenderedBitmap(document(), page.get(), nullptr, 0));
5103 EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(document(), nullptr, nullptr, 1));
5104 EXPECT_FALSE(
5105 FPDFTextObj_GetRenderedBitmap(nullptr, page.get(), text_object, 0));
5106 EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, page.get(), nullptr, 1));
5107 EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(nullptr, nullptr, text_object, 1));
5108 EXPECT_FALSE(
5109 FPDFTextObj_GetRenderedBitmap(document(), page.get(), nullptr, 1));
5110 EXPECT_FALSE(
5111 FPDFTextObj_GetRenderedBitmap(nullptr, page.get(), text_object, 1));
5112
5113 // Test bad scale values.
5114 EXPECT_FALSE(
5115 FPDFTextObj_GetRenderedBitmap(document(), page.get(), text_object, 0));
5116 EXPECT_FALSE(
5117 FPDFTextObj_GetRenderedBitmap(document(), page.get(), text_object, -1));
5118 EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(document(), page.get(),
5119 text_object, 10000));
5120 EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(
5121 document(), page.get(), text_object, std::numeric_limits<float>::max()));
5122 EXPECT_FALSE(
5123 FPDFTextObj_GetRenderedBitmap(document(), page.get(), text_object,
5124 std::numeric_limits<float>::infinity()));
5125
5126 {
5127 // `text_object` will render without `page`, but may not render correctly
5128 // without the resources from `page`. Although it does in this simple case.
5129 ScopedFPDFBitmap bitmap(
5130 FPDFTextObj_GetRenderedBitmap(document(), nullptr, text_object, 1));
5131 EXPECT_TRUE(bitmap);
5132 }
5133
5134 // Mismatch between the document and the page fails too.
5135 ScopedFPDFDocument empty_document(FPDF_CreateNewDocument());
5136 EXPECT_FALSE(FPDFTextObj_GetRenderedBitmap(empty_document.get(), page.get(),
5137 text_object, 1));
5138 }
5139
TEST_F(FPDFEditEmbedderTest,GetRenderedBitmapForRotatedImage)5140 TEST_F(FPDFEditEmbedderTest, GetRenderedBitmapForRotatedImage) {
5141 ScopedFPDFDocument doc(FPDF_CreateNewDocument());
5142 ScopedFPDFPage page(FPDFPage_New(doc.get(), 0, 100, 100));
5143 EXPECT_EQ(0, FPDFPage_CountObjects(page.get()));
5144
5145 constexpr int kBitmapWidth = 50;
5146 constexpr int kBitmapHeight = 100;
5147 ScopedFPDFBitmap bitmap(FPDFBitmap_Create(kBitmapWidth, kBitmapHeight, 0));
5148 ASSERT_TRUE(FPDFBitmap_FillRect(bitmap.get(), 0, 0, kBitmapWidth,
5149 kBitmapHeight, 0x00000000));
5150 ScopedFPDFPageObject page_image(FPDFPageObj_NewImageObj(doc.get()));
5151 ASSERT_TRUE(
5152 FPDFImageObj_SetBitmap(nullptr, 0, page_image.get(), bitmap.get()));
5153
5154 // Set bitmap matrix with scaling and 90 degrees clockwise rotation.
5155 constexpr int kScaleX = 2;
5156 constexpr int kScaleY = 3;
5157 static constexpr FS_MATRIX kBitmapMatrix{
5158 0, -kScaleX * kBitmapWidth, kScaleY * kBitmapHeight, 0, 0, 0};
5159 ASSERT_TRUE(FPDFPageObj_SetMatrix(page_image.get(), &kBitmapMatrix));
5160 FPDFPage_InsertObject(page.get(), page_image.release());
5161 EXPECT_EQ(1, FPDFPage_CountObjects(page.get()));
5162 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
5163
5164 FPDF_PAGEOBJECT page_object = FPDFPage_GetObject(page.get(), 0);
5165 ScopedFPDFBitmap extracted_bitmap(
5166 FPDFImageObj_GetRenderedBitmap(doc.get(), page.get(), page_object));
5167 ASSERT_TRUE(extracted_bitmap);
5168
5169 ASSERT_EQ(FPDFBitmap_GetWidth(extracted_bitmap.get()),
5170 kScaleY * kBitmapHeight);
5171 ASSERT_EQ(FPDFBitmap_GetHeight(extracted_bitmap.get()),
5172 kScaleX * kBitmapWidth);
5173 }
5174
TEST_F(FPDFEditEmbedderTest,MultipleGraphicsStates)5175 TEST_F(FPDFEditEmbedderTest, MultipleGraphicsStates) {
5176 ASSERT_TRUE(OpenDocument("multiple_graphics_states.pdf"));
5177 ScopedEmbedderTestPage page = LoadScopedPage(0);
5178 ASSERT_TRUE(page);
5179
5180 {
5181 ScopedFPDFPageObject path(FPDFPageObj_CreateNewPath(400, 100));
5182 EXPECT_TRUE(FPDFPageObj_SetFillColor(path.get(), 255, 0, 0, 255));
5183 EXPECT_TRUE(FPDFPath_SetDrawMode(path.get(), FPDF_FILLMODE_ALTERNATE, 0));
5184 EXPECT_TRUE(FPDFPath_MoveTo(path.get(), 100, 100));
5185 EXPECT_TRUE(FPDFPath_LineTo(path.get(), 100, 125));
5186 EXPECT_TRUE(FPDFPath_Close(path.get()));
5187
5188 FPDFPage_InsertObject(page.get(), path.release());
5189 EXPECT_TRUE(FPDFPage_GenerateContent(page.get()));
5190 }
5191
5192 const char* checksum = CFX_DefaultRenderDevice::UseSkiaRenderer()
5193 ? "7ebec75d95c64b522999a710de76c52c"
5194 : "f4b36616a7fea81a4f06cc7b01a55ac1";
5195
5196 ScopedFPDFBitmap bitmap = RenderPage(page.get());
5197 CompareBitmap(bitmap.get(), 200, 300, checksum);
5198
5199 ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
5200 VerifySavedDocument(200, 300, checksum);
5201 }
5202
TEST_F(FPDFEditEmbedderTest,GetAndSetMatrixForFormWithText)5203 TEST_F(FPDFEditEmbedderTest, GetAndSetMatrixForFormWithText) {
5204 constexpr int kExpectedWidth = 200;
5205 constexpr int kExpectedHeight = 200;
5206
5207 OpenDocument("form_object_with_text.pdf");
5208 ScopedEmbedderTestPage page = LoadScopedPage(0);
5209 ASSERT_TRUE(page);
5210
5211 {
5212 ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get());
5213 CompareBitmap(bitmap.get(), kExpectedWidth, kExpectedHeight,
5214 HelloWorldChecksum());
5215 }
5216
5217 FPDF_PAGEOBJECT form = FPDFPage_GetObject(page.get(), 0);
5218 ASSERT_TRUE(form);
5219 ASSERT_EQ(FPDF_PAGEOBJ_FORM, FPDFPageObj_GetType(form));
5220
5221 FS_MATRIX matrix;
5222 ASSERT_TRUE(FPDFPageObj_GetMatrix(form, &matrix));
5223 EXPECT_FLOAT_EQ(2.0f, matrix.a);
5224 EXPECT_FLOAT_EQ(0.0f, matrix.b);
5225 EXPECT_FLOAT_EQ(0.0f, matrix.c);
5226 EXPECT_FLOAT_EQ(-1.0f, matrix.d);
5227 EXPECT_FLOAT_EQ(0.0f, matrix.e);
5228 EXPECT_FLOAT_EQ(200.0f, matrix.f);
5229
5230 ASSERT_TRUE(FPDFPageObj_SetMatrix(form, &matrix));
5231 {
5232 ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get());
5233 CompareBitmap(bitmap.get(), kExpectedWidth, kExpectedHeight,
5234 HelloWorldChecksum());
5235 }
5236
5237 FPDF_PAGEOBJECT text = FPDFFormObj_GetObject(form, 0);
5238 ASSERT_TRUE(text);
5239 ASSERT_EQ(FPDF_PAGEOBJ_TEXT, FPDFPageObj_GetType(text));
5240
5241 ASSERT_TRUE(FPDFPageObj_GetMatrix(text, &matrix));
5242 EXPECT_FLOAT_EQ(0.5f, matrix.a);
5243 EXPECT_FLOAT_EQ(0.0f, matrix.b);
5244 EXPECT_FLOAT_EQ(0.0f, matrix.c);
5245 EXPECT_FLOAT_EQ(-1.0f, matrix.d);
5246 EXPECT_FLOAT_EQ(10.0f, matrix.e);
5247 EXPECT_FLOAT_EQ(150.0f, matrix.f);
5248
5249 ASSERT_TRUE(FPDFPageObj_SetMatrix(text, &matrix));
5250 {
5251 ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get());
5252 CompareBitmap(bitmap.get(), kExpectedWidth, kExpectedHeight,
5253 HelloWorldChecksum());
5254 }
5255
5256 ASSERT_TRUE(FPDFPage_GenerateContent(page.get()));
5257 ASSERT_TRUE(FPDF_SaveAsCopy(document(), this, 0));
5258
5259 {
5260 ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get());
5261 CompareBitmap(bitmap.get(), kExpectedWidth, kExpectedHeight,
5262 HelloWorldChecksum());
5263 }
5264
5265
5266 VerifySavedDocument(kExpectedWidth, kExpectedHeight, HelloWorldChecksum());
5267 }
5268
TEST_F(FPDFEditEmbedderTest,PageObjTransformFWithBadParameters)5269 TEST_F(FPDFEditEmbedderTest, PageObjTransformFWithBadParameters) {
5270 ScopedFPDFDocument doc(FPDF_CreateNewDocument());
5271 ScopedFPDFPageObject image(FPDFPageObj_NewImageObj(doc.get()));
5272 ASSERT_TRUE(image);
5273
5274 const FS_MATRIX matrix{1, 2, 3, 4, 5, 6};
5275 EXPECT_FALSE(FPDFPageObj_TransformF(nullptr, nullptr));
5276 EXPECT_FALSE(FPDFPageObj_TransformF(image.get(), nullptr));
5277 EXPECT_FALSE(FPDFPageObj_TransformF(nullptr, &matrix));
5278 }
5279
5280 class FPDFEditMoveEmbedderTest : public EmbedderTest {
5281 protected:
HashesForDocument(int page_count)5282 std::vector<std::string> HashesForDocument(int page_count) {
5283 std::vector<std::string> hashes;
5284 hashes.reserve(page_count);
5285 for (int i = 0; i < page_count; ++i) {
5286 hashes.push_back(HashForPage(i));
5287 }
5288 return hashes;
5289 }
5290
5291 private:
HashForPage(int page_index)5292 std::string HashForPage(int page_index) {
5293 ScopedEmbedderTestPage page = LoadScopedPage(page_index);
5294 EXPECT_TRUE(page);
5295 ScopedFPDFBitmap bitmap = RenderLoadedPage(page.get());
5296 std::string hash = HashBitmap(bitmap.get());
5297 return hash;
5298 }
5299 };
5300
TEST_F(FPDFEditMoveEmbedderTest,MovePagesTest)5301 TEST_F(FPDFEditMoveEmbedderTest, MovePagesTest) {
5302 static const FPDFEditMoveEmbedderTestCase kTestCases[] = {
5303 {{0, 1, 2, 3, 4}, 5, 0, true, {0, 1, 2, 3, 4}, "no change"},
5304 {{0, 4, 2, 1, 3}, 5, 0, true, {0, 4, 2, 1, 3}, "reorder all pages"},
5305 {{0, 2, 4, 3}, 4, 1, true, {1, 0, 2, 4, 3}, "reorder 4 pages"},
5306 {{1, 4, 2}, 3, 2, true, {0, 3, 1, 4, 2}, "reorder 3 pages"},
5307 {{3, 2}, 2, 3, true, {0, 1, 4, 3, 2}, "reorder 2 pages"},
5308 {{3}, 1, 4, true, {0, 1, 2, 4, 3}, "reorder 1 page"},
5309 {{1, 1}, 2, 2, false, {}, "duplicate index"},
5310 {{5, 3, 2}, 3, 0, false, {}, "out of range index"},
5311 {{3}, 0, 0, false, {}, "page_indices_len needs to be in range [1, 5]"},
5312 {{4, 3, 2, 1, 0}, 6, 0, false, {}, "page_indices_len is too big"},
5313 {{3}, 0, 5, false, {}, "dest_page_index is out of range"},
5314 {{3, 1, 4}, 0, -1, false, {}, "dest_page_index is out of range"},
5315 };
5316
5317 // Try all test cases with a freshly opened document that has 5 pages.
5318 for (const FPDFEditMoveEmbedderTestCase& tc : kTestCases) {
5319 ASSERT_TRUE(OpenDocument("rectangles_multi_pages.pdf"));
5320 const int page_count = GetPageCount();
5321 ASSERT_EQ(page_count, 5);
5322 // Check that the test case has correctly formed expected result.
5323 if (tc.expected_result) {
5324 ASSERT_THAT(tc.expected_order, testing::SizeIs(page_count));
5325 } else {
5326 ASSERT_THAT(tc.expected_order, testing::SizeIs(0));
5327 }
5328
5329 // Cache the original pages' hashes.
5330 std::vector<std::string> orig_hashes = HashesForDocument(page_count);
5331 ASSERT_THAT(orig_hashes, testing::SizeIs(page_count));
5332
5333 EXPECT_EQ(FPDF_MovePages(document(), &tc.page_indices[0],
5334 tc.page_indices_len, tc.dest_page_index),
5335 tc.expected_result)
5336 << tc;
5337
5338 if (tc.expected_result) {
5339 // Check for updated page order.
5340 std::vector<std::string> new_hashes = HashesForDocument(page_count);
5341 std::vector<std::string> expected_hashes;
5342 expected_hashes.reserve(page_count);
5343 for (int i = 0; i < page_count; ++i) {
5344 expected_hashes.push_back(orig_hashes[tc.expected_order[i]]);
5345 }
5346 EXPECT_THAT(new_hashes, testing::ContainerEq(expected_hashes)) << tc;
5347 } else {
5348 // Check that pages are unchanged.
5349 EXPECT_THAT(HashesForDocument(page_count),
5350 testing::ContainerEq(orig_hashes))
5351 << tc;
5352 }
5353
5354 CloseDocument();
5355 }
5356 }
5357