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