• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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